疯狂的狮子li
2021-06-23 c12dc71ecea33d5c23e2ceb32bfc3db68e4198e3
Merge remote-tracking branch 'origin/dev'

# Conflicts:
# ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RedisLockAspect.java
已修改143个文件
已添加9个文件
已重命名2个文件
已删除12个文件
7299 ■■■■■ 文件已修改
README.md 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml 71 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application-dev.yml 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application-prod.yml 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application.yml 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/pom.xml 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/annotation/RedisLock.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/cache/MybatisPlusRedisCache.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/core/BaseMapperPlus.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/core/IServicePlus.java 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/core/ServicePlusImpl.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/methods/InsertAll.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/page/BaseMapperPlus.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisLockManager.java 170 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java 152 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/JsonUtils.java 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java 208 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java 262 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java 83 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java 196 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java 460 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java 179 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestDemoAddBo.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisLockController.java 61 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestBatchController.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestDemo.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestTree.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/feign/FeignTestService.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/feign/constant/FeignTestConstant.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestTreeMapper.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/service/ITestDemoService.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/service/ITestTreeService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestDemoServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestTreeServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/vo/TestDemoVo.java 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/vo/TestTreeVo.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RedisLockAspect.java 139 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/AsyncConfig.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/FeignConfig.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/JacksonConfig.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java 111 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java 259 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java 54 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java 55 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/AsyncService.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java 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 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableColumnService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/java/addBo.java.vm 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/java/domain.java.vm 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/java/editBo.java.vm 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/java/mapper.java.vm 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/java/queryBo.java.vm 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/java/service.java.vm 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/java/vo.java.vm 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm 1094 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-generator/src/main/resources/vm/vue/index.vue.vm 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java 278 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobLogMapper.java 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobMapper.java 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobLogService.java 124 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobService.java 212 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobLogServiceImpl.java 228 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java 492 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPostService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | 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 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java 56 ●●●● 补丁 | 查看 | 原始文档 | 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 9 ●●●●● 补丁 | 查看 | 原始文档 | 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 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/.env.development 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/.env.production 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/.env.staging 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/package.json 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/App.vue 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/assets/styles/ruoyi.scss 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/components/DictTag/index.vue 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/components/Pagination/index.vue 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/components/iFrame/index.vue 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/layout/components/Settings/index.vue 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/layout/components/Sidebar/Logo.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/main.js 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/permission.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/settings.js 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/store/modules/settings.js 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/demo/demo/index.vue 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/monitor/druid/index.vue 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/monitor/logininfor/index.vue 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/monitor/operlog/index.vue 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/dict/data.vue 59 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/dict/index.vue 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/user/index.vue 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/user/profile/userInfo.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/tool/gen/genInfoForm.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/tool/swagger/index.vue 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/vue.config.js 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ry.sh 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
README.md
@@ -2,6 +2,8 @@
[![码云Gitee](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/badge/star.svg?theme=blue)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus)
[![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)
[![JDK-8+](https://img.shields.io/badge/JDK-8-green.svg)]()
[![JDK-11](https://img.shields.io/badge/JDK-11-green.svg)]()
基于 RuoYi-Vue é›†æˆ Mybatis-Plus Lombok Hutool ç­‰ä¾¿æ·å¼€å‘工具 é€‚配重写相关业务 ä¾¿äºŽå¼€å‘ å®šæœŸä¸Ž RuoYi-Vue åŒæ­¥
* å‰ç«¯å¼€å‘框架 Vue、Element UI
@@ -16,9 +18,15 @@
* ç›‘控框架 spring-boot-admin å…¨æ–¹ä½æœåŠ¡ç›‘æŽ§
* æ ¡éªŒæ¡†æž¶ validation å¢žå¼ºæŽ¥å£å®‰å…¨æ€§ ä¸¥è°¨æ€§
* æ–‡æ¡£æ¡†æž¶ knife4j ç¾ŽåŒ–接口文档
* åºåˆ—化框架 ç»Ÿä¸€ä½¿ç”¨ jackson é«˜æ•ˆå¯é 
* ä»£ç ç”Ÿæˆå™¨ ä¸€é”®ç”Ÿæˆå‰åŽç«¯ä»£ç 
* å¤šæ•°æ®æºæ¡†æž¶ dynamic-datasource æ”¯æŒä¸»ä»Žä¸Žå¤šç§ç±»æ•°æ®åº“异构
* Redis客户端 é‡‡ç”¨ Redisson æ€§èƒ½æ›´å¼º
* åˆ†å¸ƒå¼é” Lock4j æ³¨è§£é”ã€å·¥å…·é” å¤šç§å¤šæ ·
## å‚考文档
使用框架前请仔细阅读文档重点注意事项
[参考文档 Wiki](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages)
## ä¿®æ”¹RuoYi功能
@@ -34,7 +42,10 @@
* é›†æˆ Feign æŽ¥å£åŒ–管理 Http è¯·æ±‚(如三方请求 æ”¯ä»˜,短信,推送等)
* ç§»é™¤ è‡ªå¸¦æœåŠ¡ç›‘æŽ§ æ”¹ä¸º spring-boot-admin å…¨æ–¹ä½ç›‘控
* å¢žåŠ  demo æ¨¡å—示例(给不会增加模块的小伙伴做参考)
* å¢žåŠ  redisson æ”¯æŒåˆ†å¸ƒå¼é” åŠŸèƒ½æ›´å¼ºå¤§
* å¢žåŠ  redisson é«˜æ€§èƒ½ Redis å®¢æˆ·ç«¯
* ç§»é™¤ fastjson ç»Ÿä¸€ä½¿ç”¨ jackson åºåˆ—化
* é›†æˆ dynamic-datasource å¤šæ•°æ®æº(默认支持MySQL,其他种类需自行适配)
* é›†æˆ Lock4j å®žçŽ°åˆ†å¸ƒå¼ æ³¨è§£é”ã€å·¥å…·é” å¤šç§å¤šæ ·
### ä»£ç æ”¹åЍ
@@ -46,6 +57,7 @@
* é¡¹ç›®ä¿®æ”¹ä¸º maven多环境配置
* é¡¹ç›®é…ç½®ä¿®æ”¹ä¸º application.yml ç»Ÿä¸€ç®¡ç†
* æ•°æ®æƒé™ä¿®æ”¹ä¸º é€‚配支持单表、多表
* ä½¿ç”¨ redisson å®žçŽ°åˆ†å¸ƒå¼é”æ³¨è§£ä¸Žå·¥å…·ç±»
* ä½¿ç”¨ redisson å®žçް spring-cache æ•´åˆ
* å¢žåŠ  mybatis-plus äºŒçº§ç¼“å­˜ redis å­˜å‚¨
pom.xml
@@ -14,23 +14,24 @@
    <properties>
        <ruoyi-vue-plus.version>2.3.2</ruoyi-vue-plus.version>
        <spring-boot.version>2.3.11.RELEASE</spring-boot.version>
        <spring-boot.version>2.4.7</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>
        <maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
        <druid.version>1.2.6</druid.version>
        <knife4j.version>3.0.2</knife4j.version>
        <fastjson.version>1.2.76</fastjson.version>
        <poi.version>4.1.2</poi.version>
        <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.6.5</hutool.version>
        <feign.version>2.2.6.RELEASE</feign.version>
        <hutool.version>5.7.2</hutool.version>
        <feign.version>3.0.3</feign.version>
        <feign-okhttp.version>11.0</feign-okhttp.version>
        <spring-boot-admin.version>2.3.1</spring-boot-admin.version>
        <spring-boot-admin.version>2.4.1</spring-boot-admin.version>
        <redisson.version>3.15.2</redisson.version>
        <lock4j.version>2.2.1</lock4j.version>
        <datasource.version>3.4.0</datasource.version>
    </properties>
    <!-- ä¾èµ–声明 -->
@@ -73,18 +74,18 @@
                <version>${velocity.version}</version>
            </dependency>
            <!-- é˜¿é‡ŒJSON解析器 -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>${fastjson.version}</version>
            </dependency>
            <!-- Token生成与解析-->
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt</artifactId>
                <version>${jwt.version}</version>
            </dependency>
            <!-- dynamic-datasource å¤šæ•°æ®æº-->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
                <version>${datasource.version}</version>
            </dependency>
            <dependency>
@@ -97,6 +98,7 @@
                <artifactId>mybatis-plus-extension</artifactId>
                <version>${mybatis-plus.version}</version>
            </dependency>
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
@@ -132,7 +134,11 @@
                <artifactId>redisson-spring-boot-starter</artifactId>
                <version>${redisson.version}</version>
            </dependency>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>lock4j-redisson-spring-boot-starter</artifactId>
                <version>${lock4j.version}</version>
            </dependency>
            <!-- å®šæ—¶ä»»åŠ¡-->
            <dependency>
                <groupId>com.ruoyi</groupId>
@@ -270,6 +276,45 @@
                <logging.level>warn</logging.level>
            </properties>
        </profile>
        <!-- jdk多版本配置 -->
        <profile>
            <id>jdk8</id>
            <activation>
                <activeByDefault>true</activeByDefault>
                <jdk>1.8</jdk>
            </activation>
            <properties>
                <java.version>1.8</java.version>
            </properties>
        </profile>
        <profile>
            <id>jdk11</id>
            <activation>
                <jdk>11</jdk>
            </activation>
            <properties>
                <java.version>11</java.version>
                <jaxb.version>3.0.1</jaxb.version>
            </properties>
            <dependencyManagement>
                <dependencies>
                    <!-- jdk11 ç¼ºå¤±ä¾èµ– jaxb-->
                    <dependency>
                        <groupId>com.sun.xml.bind</groupId>
                        <artifactId>jaxb-impl</artifactId>
                        <version>${jaxb.version}</version>
                    </dependency>
                </dependencies>
            </dependencyManagement>
            <dependencies>
                <!--jaxb-->
                <dependency>
                    <groupId>com.sun.xml.bind</groupId>
                    <artifactId>jaxb-impl</artifactId>
                </dependency>
            </dependencies>
        </profile>
    </profiles>
</project>
ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java
@@ -2,15 +2,14 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
/**
 * å¯åŠ¨ç¨‹åº
 *
 *
 * @author ruoyi
 */
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
@SpringBootApplication
public class RuoYiApplication
{
    public static void main(String[] args)
ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java
@@ -54,10 +54,10 @@
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            FileUtils.setAttachmentResponseHeader(response, realFileName);
            FileUtils.writeBytes(filePath, response.getOutputStream());
            FileUtils.writeToStream(filePath, response.getOutputStream());
            if (delete)
            {
                FileUtils.deleteFile(filePath);
                FileUtils.del(filePath);
            }
        }
        catch (Exception e)
@@ -111,7 +111,7 @@
            String downloadName = StrUtil.subAfter(downloadPath, "/",true);
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            FileUtils.setAttachmentResponseHeader(response, downloadName);
            FileUtils.writeBytes(downloadPath, response.getOutputStream());
            FileUtils.writeToStream(downloadPath, response.getOutputStream());
        }
        catch (Exception e)
        {
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java
@@ -56,7 +56,7 @@
        {
            SysDept d = (SysDept) it.next();
            if (d.getDeptId().intValue() == deptId
                    || ArrayUtils.contains(StrUtil.split(d.getAncestors(), ","), deptId + ""))
                    || ArrayUtils.contains(StrUtil.splitToArray(d.getAncestors(), ","), deptId + ""))
            {
                it.remove();
            }
ruoyi-admin/src/main/resources/application-dev.yml
@@ -1,21 +1,27 @@
# æ•°æ®æºé…ç½®
spring:
  autoconfigure:
    exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    # åŠ¨æ€æ•°æ®æºæ–‡æ¡£ https://www.kancloud.cn/tracy5546/dynamic-datasource/content
    dynamic:
      #设置默认的数据源或者数据源组,默认值即为 master
      primary: master
      datasource:
        # ä¸»åº“数据源
        master:
          driverClassName: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true
          username: root
          password: root
        # ä»Žåº“数据源
        slave:
          driverClassName: com.mysql.cj.jdbc.Driver
          url:
          username:
          password:
    druid:
      # ä¸»åº“数据源
      master:
        url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true
        username: root
        password: root
      # ä»Žåº“数据源
      slave:
        # ä»Žæ•°æ®æºå¼€å…³/默认关闭
        enabled: false
        url:
        username:
        password:
      # åˆå§‹è¿žæŽ¥æ•°
      initialSize: 5
      # æœ€å°è¿žæŽ¥æ± æ•°é‡
@@ -35,6 +41,8 @@
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      # æ³¨æ„è¿™ä¸ªå€¼å’Œdruid原生不一致,默认启动了stat
      filters: stat
      webStatFilter:
        enabled: true
      statViewServlet:
ruoyi-admin/src/main/resources/application-prod.yml
@@ -1,21 +1,27 @@
# æ•°æ®æºé…ç½®
spring:
  autoconfigure:
    exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    # åŠ¨æ€æ•°æ®æºæ–‡æ¡£ https://www.kancloud.cn/tracy5546/dynamic-datasource/content
    dynamic:
      #设置默认的数据源或者数据源组,默认值即为 master
      primary: master
      datasource:
        # ä¸»åº“数据源
        master:
          driverClassName: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true
          username: root
          password: root
        # ä»Žåº“数据源
        slave:
          driverClassName: com.mysql.cj.jdbc.Driver
          url:
          username:
          password:
    druid:
      # ä¸»åº“数据源
      master:
        url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true
        username: root
        password: root
      # ä»Žåº“数据源
      slave:
        # ä»Žæ•°æ®æºå¼€å…³/默认关闭
        enabled: false
        url:
        username:
        password:
      # åˆå§‹è¿žæŽ¥æ•°
      initialSize: 5
      # æœ€å°è¿žæŽ¥æ± æ•°é‡
@@ -35,6 +41,8 @@
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      # æ³¨æ„è¿™ä¸ªå€¼å’Œdruid原生不一致,默认启动了stat
      filters: stat
      webStatFilter:
        enabled: true
      statViewServlet:
@@ -58,7 +66,7 @@
  # redis é…ç½®
  redis:
    # åœ°å€
    host: 192.168.0.222
    host: localhost
    # ç«¯å£ï¼Œé»˜è®¤ä¸º6379
    port: 6379
    # æ•°æ®åº“索引
ruoyi-admin/src/main/resources/application.yml
@@ -11,7 +11,7 @@
  # æ–‡ä»¶è·¯å¾„
  profile: ./ruoyi/uploadPath
  # èŽ·å–ip地址开关
  addressEnabled: false
  addressEnabled: true
captcha:
  # éªŒè¯ç å¼€å…³
@@ -86,6 +86,17 @@
  thymeleaf:
    # å°†ç³»ç»Ÿæ¨¡æ¿æ”¾ç½®åˆ°æœ€å‰é¢ å¦åˆ™ä¼šä¸Ž springboot-admin é¡µé¢å†²çª
    template-resolver-order: 1
  jackson:
    # æ—¥æœŸæ ¼å¼åŒ–
    date-format: yyyy-MM-dd HH:mm:ss
    serialization:
      # æ ¼å¼åŒ–输出
      indent_output: false
      # å¿½ç•¥æ— æ³•转换的对象
      fail_on_empty_beans: false
    deserialization:
      # å…è®¸å¯¹è±¡å¿½ç•¥json中不存在的属性
      fail_on_unknown_properties: false
# token配置
token:
@@ -242,9 +253,16 @@
      enabled: true
  okhttp:
    enabled: true
  hystrix:
  circuitbreaker:
    enabled: true
--- # åˆ†å¸ƒå¼é” lock4j å…¨å±€é…ç½®
lock4j:
  # èŽ·å–åˆ†å¸ƒå¼é”è¶…æ—¶æ—¶é—´ï¼Œé»˜è®¤ä¸º 3000 æ¯«ç§’
  acquire-timeout: 3000
  # åˆ†å¸ƒå¼é”çš„超时时间,默认为 30 æ¯«ç§’
  expire: 30000
--- # å®šæ—¶ä»»åŠ¡é…ç½®
spring:
  quartz:
ruoyi-common/pom.xml
@@ -53,12 +53,6 @@
            <artifactId>jackson-databind</artifactId>
        </dependency>
        <!-- é˜¿é‡ŒJSON解析器 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>
        <!-- excel工具 -->
        <dependency>
            <groupId>org.apache.poi</groupId>
@@ -152,6 +146,17 @@
            <artifactId>redisson-spring-boot-starter</artifactId>
        </dependency>
        <!-- dynamic-datasource å¤šæ•°æ®æº-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>lock4j-redisson-spring-boot-starter</artifactId>
        </dependency>
    </dependencies>
</project>
ruoyi-common/src/main/java/com/ruoyi/common/annotation/RedisLock.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java
@@ -128,6 +128,11 @@
    public static final String RESOURCE_PREFIX = "/profile";
    /**
     * RMI è¿œç¨‹æ–¹æ³•调用
     */
    public static final String LOOKUP_RMI = "rmi://";
    /**
     * èµ„源映射路径 å‰ç¼€
     */
    public static final String REDIS_LOCK_KEY = "redis_lock:";
ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java
@@ -2,14 +2,8 @@
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import java.beans.PropertyEditorSupport;
import java.util.Date;
/**
 * web层通用数据处理
@@ -19,23 +13,6 @@
public class BaseController
{
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
    /**
     * å°†å‰å°ä¼ é€’过来的日期格式的字符串,自动转化为Date类型
     */
    @InitBinder
    public void initBinder(WebDataBinder binder)
    {
        // Date ç±»åž‹è½¬æ¢
        binder.registerCustomEditor(Date.class, new PropertyEditorSupport()
        {
            @Override
            public void setAsText(String text)
            {
                setValue(DateUtils.parseDate(text));
            }
        });
    }
    /**
     * å“åº”返回结果
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java
@@ -1,6 +1,5 @@
package com.ruoyi.common.core.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@@ -12,7 +11,7 @@
/**
 * Entity基类
 *
 *
 * @author ruoyi
 */
@@ -30,14 +29,12 @@
    private String createBy;
    /** åˆ›å»ºæ—¶é—´ */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    /** æ›´æ–°è€… */
    private String updateBy;
    /** æ›´æ–°æ—¶é—´ */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date updateTime;
    /** å¤‡æ³¨ */
ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/cache/MybatisPlusRedisCache.java
ÎļþÃû´Ó ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/MybatisPlusRedisCache.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.ruoyi.common.core.mybatisplus;
package com.ruoyi.common.core.mybatisplus.cache;
import cn.hutool.extra.spring.SpringUtil;
import com.ruoyi.common.core.redis.RedisCache;
ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/core/BaseMapperPlus.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package com.ruoyi.common.core.mybatisplus.core;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import java.util.Collection;
/**
 * è‡ªå®šä¹‰ Mapper æŽ¥å£, å®žçް è‡ªå®šä¹‰æ‰©å±•
 *
 * @author Lion Li
 * @since 2021-05-13
 */
public interface BaseMapperPlus<T> extends BaseMapper<T> {
    int insertAll(@Param("list") Collection<T> batchList);
}
ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/core/IServicePlus.java
ÎļþÃû´Ó ruoyi-common/src/main/java/com/ruoyi/common/core/page/IServicePlus.java ÐÞ¸Ä
@@ -1,9 +1,10 @@
package com.ruoyi.common.core.page;
package com.ruoyi.common.core.mybatisplus.core;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.core.page.PagePlus;
import java.io.Serializable;
import java.util.Collection;
@@ -182,7 +183,6 @@
     *
     * @param page         ç¿»é¡µå¯¹è±¡
     * @param queryWrapper å®žä½“对象封装操作类
     * @param kClass       vo类型
     */
    default <K> PagePlus<T, K> pageVo(PagePlus<T, K> page, Wrapper<T> queryWrapper, Class<K> kClass) {
        PagePlus<T, K> result = getBaseMapper().selectPage(page, queryWrapper);
@@ -210,7 +210,6 @@
     * æ— æ¡ä»¶ç¿»é¡µæŸ¥è¯¢
     *
     * @param page   ç¿»é¡µå¯¹è±¡
     * @param kClass vo类型
     */
    default <K> PagePlus<T, K> pageVo(PagePlus<T, K> page, Class<K> kClass) {
        return pageVo(page, Wrappers.emptyWrapper(), kClass);
@@ -226,5 +225,21 @@
        return pageVo(page, Wrappers.emptyWrapper(), convertor);
    }
    @Override
    default boolean saveBatch(Collection<T> entityList) {
        return saveBatch(entityList, DEFAULT_BATCH_SIZE);
    }
    @Override
    default boolean saveOrUpdateBatch(Collection<T> entityList) {
        return saveOrUpdateBatch(entityList, DEFAULT_BATCH_SIZE);
    }
    @Override
    default boolean updateBatchById(Collection<T> entityList) {
        return updateBatchById(entityList, DEFAULT_BATCH_SIZE);
    }
    boolean saveAll(Collection<T> entityList);
}
ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/core/ServicePlusImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,90 @@
package com.ruoyi.common.core.mybatisplus.core;
import com.baomidou.mybatisplus.core.toolkit.ClassUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ResolvableType;
import java.util.Collection;
/**
 * IServicePlus å®žçŽ°ç±»
 *
 * @author Lion Li
 */
@Slf4j
@SuppressWarnings("unchecked")
public class ServicePlusImpl<M extends BaseMapperPlus<T>, T> extends ServiceImpl<M, T> implements IServicePlus<T> {
    @Autowired
    protected M baseMapper;
    @Override
    public M getBaseMapper() {
        return baseMapper;
    }
    protected Class<T> entityClass = currentModelClass();
    @Override
    public Class<T> getEntityClass() {
        return entityClass;
    }
    protected Class<T> mapperClass = currentMapperClass();
    @Override
    protected Class<T> currentMapperClass() {
        return (Class<T>) this.getResolvableType().as(ServicePlusImpl.class).getGeneric(0).getType();
    }
    @Override
    protected Class<T> currentModelClass() {
        return (Class<T>) this.getResolvableType().as(ServicePlusImpl.class).getGeneric(1).getType();
    }
    @Override
    protected ResolvableType getResolvableType() {
        return ResolvableType.forClass(ClassUtils.getUserClass(getClass()));
    }
    /**
     * å•条执行性能差
     *
     * {@link #saveAll(Collection)}
     */
    @Deprecated
    @Override
    public boolean saveBatch(Collection<T> entityList, int batchSize) {
        return super.saveBatch(entityList, batchSize);
    }
    @Override
    public boolean saveOrUpdate(T entity) {
        return super.saveOrUpdate(entity);
    }
    /**
     * å•条执行性能差
     *
     * {@link #saveAll(Collection)}
     */
    @Deprecated
    @Override
    public boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize) {
        return super.saveOrUpdateBatch(entityList, batchSize);
    }
    @Override
    public boolean updateBatchById(Collection<T> entityList, int batchSize) {
        return super.updateBatchById(entityList, batchSize);
    }
    @Override
    public boolean saveAll(Collection<T> entityList) {
        return baseMapper.insertAll(entityList) == entityList.size();
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/core/mybatisplus/methods/InsertAll.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package com.ruoyi.common.core.mybatisplus.methods;
import cn.hutool.core.util.StrUtil;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
/**
 * å•sql批量插入
 *
 * @author Lion Li
 */
public class InsertAll extends AbstractMethod {
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        final String sql = "<script>insert into %s %s values %s</script>";
        final String fieldSql = prepareFieldSql(tableInfo);
        final String valueSql = prepareValuesSqlForMysqlBatch(tableInfo);
        final String sqlResult = String.format(sql, tableInfo.getTableName(), fieldSql, valueSql);
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass);
        return this.addInsertMappedStatement(mapperClass, modelClass, "insertAll", sqlSource, new NoKeyGenerator(), null, null);
    }
    private String prepareFieldSql(TableInfo tableInfo) {
        StringBuilder fieldSql = new StringBuilder();
        if (StrUtil.isNotBlank(tableInfo.getKeyColumn())) {
            fieldSql.append(tableInfo.getKeyColumn()).append(",");
        }
        tableInfo.getFieldList().forEach(x -> fieldSql.append(x.getColumn()).append(","));
        fieldSql.delete(fieldSql.length() - 1, fieldSql.length());
        fieldSql.insert(0, "(");
        fieldSql.append(")");
        return fieldSql.toString();
    }
    private String prepareValuesSqlForMysqlBatch(TableInfo tableInfo) {
        final StringBuilder valueSql = new StringBuilder();
        valueSql.append("<foreach collection=\"list\" item=\"item\" index=\"index\" open=\"(\" separator=\"),(\" close=\")\">");
        if (StrUtil.isNotBlank(tableInfo.getKeyColumn())) {
            valueSql.append("#{item.").append(tableInfo.getKeyProperty()).append("},");
        }
        tableInfo.getFieldList().forEach(x -> valueSql.append("#{item.").append(x.getProperty()).append("},"));
        valueSql.delete(valueSql.length() - 1, valueSql.length());
        valueSql.append("</foreach>");
        return valueSql.toString();
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/core/page/BaseMapperPlus.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisLockManager.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java
@@ -1,19 +1,25 @@
package com.ruoyi.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
 * æ•°æ®æº
 *
 * @author ruoyi
 *
 * @author Lion Li
 */
public enum DataSourceType
{
    /**
     * ä¸»åº“
     */
    MASTER,
@AllArgsConstructor
public enum DataSourceType {
    /**
     * ä¸»åº“
     */
    MASTER("master"),
    /**
     * ä»Žåº“
     */
    SLAVE
    /**
     * ä»Žåº“
     */
    SLAVE("slave");
    @Getter
    private final String source;
}
ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java
@@ -1,75 +1,77 @@
package com.ruoyi.common.filter;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import com.ruoyi.common.utils.http.HttpHelper;
/**
 * æž„建可重复读取inputStream的request
 *
 * @author ruoyi
 */
public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper
{
    private final byte[] body;
    public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException
    {
        super(request);
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        body = HttpHelper.getBodyString(request).getBytes("UTF-8");
    }
    @Override
    public BufferedReader getReader() throws IOException
    {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }
    @Override
    public ServletInputStream getInputStream() throws IOException
    {
        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
        return new ServletInputStream()
        {
            @Override
            public int read() throws IOException
            {
                return bais.read();
            }
            @Override
            public int available() throws IOException
            {
                return body.length;
            }
            @Override
            public boolean isFinished()
            {
                return false;
            }
            @Override
            public boolean isReady()
            {
                return false;
            }
            @Override
            public void setReadListener(ReadListener readListener)
            {
            }
        };
    }
}
package com.ruoyi.common.filter;
import cn.hutool.core.io.IoUtil;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
/**
 * æž„建可重复读取inputStream的request
 *
 * @author ruoyi
 */
public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper
{
    private final byte[] body;
    public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException
    {
        super(request);
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        body = IoUtil.readUtf8(request.getInputStream()).getBytes(StandardCharsets.UTF_8);
    }
    @Override
    public BufferedReader getReader() throws IOException
    {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }
    @Override
    public ServletInputStream getInputStream() throws IOException
    {
        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
        return new ServletInputStream()
        {
            @Override
            public int read() throws IOException
            {
                return bais.read();
            }
            @Override
            public int available() throws IOException
            {
                return body.length;
            }
            @Override
            public boolean isFinished()
            {
                return false;
            }
            @Override
            public boolean isReady()
            {
                return false;
            }
            @Override
            public void setReadListener(ReadListener readListener)
            {
            }
        };
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java
@@ -1,9 +1,9 @@
package com.ruoyi.common.filter;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HtmlUtil;
import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
@@ -13,6 +13,7 @@
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
 * XSS过滤处理
@@ -57,7 +58,7 @@
        }
        // ä¸ºç©ºï¼Œç›´æŽ¥è¿”回
        String json = IOUtils.toString(super.getInputStream(), "utf-8");
        String json = IoUtil.read(super.getInputStream(), StandardCharsets.UTF_8);
        if (Validator.isEmpty(json))
        {
            return super.getInputStream();
@@ -65,7 +66,8 @@
        // xss过滤
        json = HtmlUtil.cleanHtmlTag(json).trim();
        final ByteArrayInputStream bis = new ByteArrayInputStream(json.getBytes("utf-8"));
        final ByteArrayInputStream bis = IoUtil.toStream(json, StandardCharsets.UTF_8);
        return new ServletInputStream()
        {
            @Override
ruoyi-common/src/main/java/com/ruoyi/common/utils/JsonUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,101 @@
package com.ruoyi.common.utils;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
 * JSON å·¥å…·ç±»
 *
 * @author èŠ‹é“æºç 
 */
public class JsonUtils {
    private static ObjectMapper objectMapper = new ObjectMapper();
    /**
     * åˆå§‹åŒ– objectMapper å±žæ€§
     * <p>
     * é€šè¿‡è¿™æ ·çš„æ–¹å¼ï¼Œä½¿ç”¨ Spring åˆ›å»ºçš„ ObjectMapper Bean
     *
     * @param objectMapper ObjectMapper å¯¹è±¡
     */
    public static void init(ObjectMapper objectMapper) {
        JsonUtils.objectMapper = objectMapper;
    }
    public static String toJsonString(Object object) {
        if (Validator.isEmpty(object)) {
            return null;
        }
        try {
            return objectMapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
    public static <T> T parseObject(String text, Class<T> clazz) {
        if (StrUtil.isEmpty(text)) {
            return null;
        }
        try {
            return objectMapper.readValue(text, clazz);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    public static <T> T parseObject(byte[] bytes, Class<T> clazz) {
        if (ArrayUtil.isEmpty(bytes)) {
            return null;
        }
        try {
            return objectMapper.readValue(bytes, clazz);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    public static <T> T parseObject(String text, TypeReference<T> typeReference) {
        if (StrUtil.isBlank(text)) {
            return null;
        }
        try {
            return objectMapper.readValue(text, typeReference);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    public static <T> Map<String, T> parseMap(String text) {
        if (StrUtil.isBlank(text)) {
            return null;
        }
        try {
            return objectMapper.readValue(text, new TypeReference<Map<String, T>>() {});
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    public static <T> List<T> parseArray(String text, Class<T> clazz) {
        if (StrUtil.isEmpty(text)) {
            return new ArrayList<>();
        }
        try {
            return objectMapper.readValue(text, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java
@@ -70,20 +70,31 @@
        return page;
    }
    public static <T> Page<T> buildPage() {
        return buildPage(null, null);
    }
    /**
     * æž„建 MP æ™®é€šåˆ†é¡µå¯¹è±¡
     * @param <T> domain å®žä½“
     * @return åˆ†é¡µå¯¹è±¡
     */
    public static <T> Page<T> buildPage() {
    public static <T> Page<T> buildPage(String defaultOrderByColumn, String defaultIsAsc) {
        Integer pageNum = ServletUtils.getParameterToInt(PAGE_NUM, DEFAULT_PAGE_NUM);
        Integer pageSize = ServletUtils.getParameterToInt(PAGE_SIZE, DEFAULT_PAGE_SIZE);
        String orderByColumn = ServletUtils.getParameter(ORDER_BY_COLUMN);
        String isAsc = ServletUtils.getParameter(IS_ASC);
        String orderByColumn = ServletUtils.getParameter(ORDER_BY_COLUMN, defaultOrderByColumn);
        String isAsc = ServletUtils.getParameter(IS_ASC, defaultIsAsc);
        // å…¼å®¹å‰ç«¯æŽ’序类型
        if ("ascending".equals(isAsc)) {
            isAsc = "asc";
        } else if ("descending".equals(isAsc)) {
            isAsc = "desc";
        }
        Page<T> page = new Page<>(pageNum, pageSize);
        if (StrUtil.isNotBlank(orderByColumn)) {
            String orderBy = SqlUtil.escapeOrderBySql(orderByColumn);
            if ("asc".equals(isAsc)) {
            orderBy = StrUtil.toUnderlineCase(orderBy);
            if ("asc".equals(isAsc)) {
                page.addOrder(OrderItem.asc(orderBy));
            } else if ("desc".equals(isAsc)) {
                page.addOrder(OrderItem.desc(orderBy));
ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java
@@ -2,6 +2,9 @@
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
@@ -10,129 +13,118 @@
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
 * å®¢æˆ·ç«¯å·¥å…·ç±»
 *
 *
 * @author ruoyi
 */
public class ServletUtils
{
    /**
     * èŽ·å–String参数
     */
    public static String getParameter(String name)
    {
        return getRequest().getParameter(name);
    }
public class ServletUtils extends ServletUtil {
    /**
     * èŽ·å–String参数
     */
    public static String getParameter(String name) {
        return getRequest().getParameter(name);
    }
    /**
     * èŽ·å–String参数
     */
    public static String getParameter(String name, String defaultValue)
    {
        return Convert.toStr(getRequest().getParameter(name), defaultValue);
    }
    /**
     * èŽ·å–String参数
     */
    public static String getParameter(String name, String defaultValue) {
        return Convert.toStr(getRequest().getParameter(name), defaultValue);
    }
    /**
     * èŽ·å–Integer参数
     */
    public static Integer getParameterToInt(String name)
    {
        return Convert.toInt(getRequest().getParameter(name));
    }
    /**
     * èŽ·å–Integer参数
     */
    public static Integer getParameterToInt(String name) {
        return Convert.toInt(getRequest().getParameter(name));
    }
    /**
     * èŽ·å–Integer参数
     */
    public static Integer getParameterToInt(String name, Integer defaultValue)
    {
        return Convert.toInt(getRequest().getParameter(name), defaultValue);
    }
    /**
     * èŽ·å–Integer参数
     */
    public static Integer getParameterToInt(String name, Integer defaultValue) {
        return Convert.toInt(getRequest().getParameter(name), defaultValue);
    }
    /**
     * èŽ·å–request
     */
    public static HttpServletRequest getRequest()
    {
        return getRequestAttributes().getRequest();
    }
    /**
     * èŽ·å–request
     */
    public static HttpServletRequest getRequest() {
        return getRequestAttributes().getRequest();
    }
    /**
     * èŽ·å–response
     */
    public static HttpServletResponse getResponse()
    {
        return getRequestAttributes().getResponse();
    }
    /**
     * èŽ·å–response
     */
    public static HttpServletResponse getResponse() {
        return getRequestAttributes().getResponse();
    }
    /**
     * èŽ·å–session
     */
    public static HttpSession getSession()
    {
        return getRequest().getSession();
    }
    /**
     * èŽ·å–session
     */
    public static HttpSession getSession() {
        return getRequest().getSession();
    }
    public static ServletRequestAttributes getRequestAttributes()
    {
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
        return (ServletRequestAttributes) attributes;
    }
    public static ServletRequestAttributes getRequestAttributes() {
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
        return (ServletRequestAttributes) attributes;
    }
    /**
     * å°†å­—符串渲染到客户端
     *
     * @param response æ¸²æŸ“对象
     * @param string å¾…渲染的字符串
     * @return null
     */
    public static String renderString(HttpServletResponse response, String string)
    {
        try
        {
            response.setStatus(200);
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            response.getWriter().print(string);
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * å°†å­—符串渲染到客户端
     *
     * @param response æ¸²æŸ“对象
     * @param string   å¾…渲染的字符串
     * @return null
     */
    public static String renderString(HttpServletResponse response, String string) {
        try {
            response.setStatus(HttpStatus.HTTP_OK);
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
            response.getWriter().print(string);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * æ˜¯å¦æ˜¯Ajax异步请求
     *
     * @param request
     */
    public static boolean isAjaxRequest(HttpServletRequest request)
    {
        String accept = request.getHeader("accept");
        if (accept != null && accept.indexOf("application/json") != -1)
        {
            return true;
        }
    /**
     * æ˜¯å¦æ˜¯Ajax异步请求
     *
     * @param request
     */
    public static boolean isAjaxRequest(HttpServletRequest request) {
        String xRequestedWith = request.getHeader("X-Requested-With");
        if (xRequestedWith != null && xRequestedWith.indexOf("XMLHttpRequest") != -1)
        {
            return true;
        }
        String accept = request.getHeader("accept");
        if (accept != null && accept.indexOf("application/json") != -1) {
            return true;
        }
        String uri = request.getRequestURI();
        if (StrUtil.equalsAnyIgnoreCase(uri, ".json", ".xml"))
        {
            return true;
        }
        String xRequestedWith = request.getHeader("X-Requested-With");
        if (xRequestedWith != null && xRequestedWith.indexOf("XMLHttpRequest") != -1) {
            return true;
        }
        String ajax = request.getParameter("__ajax");
        if (StrUtil.equalsAnyIgnoreCase(ajax, "json", "xml"))
        {
            return true;
        }
        return false;
    }
        String uri = request.getRequestURI();
        if (StrUtil.equalsAnyIgnoreCase(uri, ".json", ".xml")) {
            return true;
        }
        String ajax = request.getParameter("__ajax");
        if (StrUtil.equalsAnyIgnoreCase(ajax, "json", "xml")) {
            return true;
        }
        return false;
    }
    public static String getClientIP() {
        return getClientIP(getRequest());
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java
@@ -1,5 +1,6 @@
package com.ruoyi.common.utils.file;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
@@ -11,91 +12,16 @@
/**
 * æ–‡ä»¶å¤„理工具类
 *
 *
 * @author ruoyi
 */
public class FileUtils extends org.apache.commons.io.FileUtils
public class FileUtils extends FileUtil
{
    public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+";
    /**
     * è¾“出指定文件的byte数组
     *
     * @param filePath æ–‡ä»¶è·¯å¾„
     * @param os è¾“出流
     * @return
     */
    public static void writeBytes(String filePath, OutputStream os) throws IOException
    {
        FileInputStream fis = null;
        try
        {
            File file = new File(filePath);
            if (!file.exists())
            {
                throw new FileNotFoundException(filePath);
            }
            fis = new FileInputStream(file);
            byte[] b = new byte[1024];
            int length;
            while ((length = fis.read(b)) > 0)
            {
                os.write(b, 0, length);
            }
        }
        catch (IOException e)
        {
            throw e;
        }
        finally
        {
            if (os != null)
            {
                try
                {
                    os.close();
                }
                catch (IOException e1)
                {
                    e1.printStackTrace();
                }
            }
            if (fis != null)
            {
                try
                {
                    fis.close();
                }
                catch (IOException e1)
                {
                    e1.printStackTrace();
                }
            }
        }
    }
    /**
     * åˆ é™¤æ–‡ä»¶
     *
     * @param filePath æ–‡ä»¶
     * @return
     */
    public static boolean deleteFile(String filePath)
    {
        boolean flag = false;
        File file = new File(filePath);
        // è·¯å¾„为文件且不为空则进行删除
        if (file.isFile() && file.exists())
        {
            file.delete();
            flag = true;
        }
        return flag;
    }
    /**
     * æ–‡ä»¶åç§°éªŒè¯
     *
     *
     * @param filename æ–‡ä»¶åç§°
     * @return true æ­£å¸¸ false éžæ³•
     */
@@ -130,7 +56,7 @@
    /**
     * ä¸‹è½½æ–‡ä»¶åé‡æ–°ç¼–码
     *
     *
     * @param request è¯·æ±‚对象
     * @param fileName æ–‡ä»¶å
     * @return ç¼–码后的文件名
@@ -142,7 +68,7 @@
        if (agent.contains("MSIE"))
        {
            // IE浏览器
            filename = URLEncoder.encode(filename, "utf-8");
            filename = URLEncoder.encode(filename, StandardCharsets.UTF_8.toString());
            filename = filename.replace("+", " ");
        }
        else if (agent.contains("Firefox"))
@@ -153,12 +79,12 @@
        else if (agent.contains("Chrome"))
        {
            // google浏览器
            filename = URLEncoder.encode(filename, "utf-8");
            filename = URLEncoder.encode(filename, StandardCharsets.UTF_8.toString());
        }
        else
        {
            // å…¶å®ƒæµè§ˆå™¨
            filename = URLEncoder.encode(filename, "utf-8");
            filename = URLEncoder.encode(filename, StandardCharsets.UTF_8.toString());
        }
        return filename;
    }
ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java
@@ -1,56 +1,55 @@
package com.ruoyi.common.utils.ip;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import cn.hutool.http.HtmlUtil;
import cn.hutool.http.HttpUtil;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.http.HttpUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.utils.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
/**
 * èŽ·å–åœ°å€ç±»
 *
 *
 * @author ruoyi
 */
public class AddressUtils
{
    private static final Logger log = LoggerFactory.getLogger(AddressUtils.class);
@Slf4j
public class AddressUtils {
    // IP地址查询
    public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";
    // IP地址查询
    public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";
    // æœªçŸ¥åœ°å€
    public static final String UNKNOWN = "XX XX";
    // æœªçŸ¥åœ°å€
    public static final String UNKNOWN = "XX XX";
    public static String getRealAddressByIP(String ip)
    {
        String address = UNKNOWN;
        // å†…网不查询
        if (IpUtils.internalIp(ip))
        {
            return "内网IP";
        }
        if (RuoYiConfig.isAddressEnabled())
        {
            try
            {
                String rspStr = HttpUtils.sendGet(IP_URL, "ip=" + ip + "&json=true", Constants.GBK);
                if (StrUtil.isEmpty(rspStr))
                {
                    log.error("获取地理位置异常 {}", ip);
                    return UNKNOWN;
                }
                JSONObject obj = JSONObject.parseObject(rspStr);
                String region = obj.getString("pro");
                String city = obj.getString("city");
                return String.format("%s %s", region, city);
            }
            catch (Exception e)
            {
                log.error("获取地理位置异常 {}", ip);
            }
        }
        return address;
    }
    public static String getRealAddressByIP(String ip) {
        String address = UNKNOWN;
        // å†…网不查询
        ip = "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip);
        if (NetUtil.isInnerIP(ip)) {
            return "内网IP";
        }
        if (RuoYiConfig.isAddressEnabled()) {
            try {
                String rspStr = HttpUtil.createGet(IP_URL)
                    .body("ip=" + ip + "&json=true", Constants.GBK)
                    .execute()
                    .body();
                if (StrUtil.isEmpty(rspStr)) {
                    log.error("获取地理位置异常 {}", ip);
                    return UNKNOWN;
                }
                Map<String, String> obj = JsonUtils.parseMap(rspStr);
                String region = obj.get("pro");
                String city = obj.get("city");
                return String.format("%s %s", region, city);
            } catch (Exception e) {
                log.error("获取地理位置异常 {}", ip);
            }
        }
        return address;
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java
ÎļþÒÑɾ³ý
ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java
@@ -1,406 +1,54 @@
package com.ruoyi.common.utils.reflect;
import cn.hutool.core.convert.Convert;
import com.ruoyi.common.utils.DateUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.poi.ss.usermodel.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.*;
import java.util.Date;
/**
 * åå°„工具类. æä¾›è°ƒç”¨getter/setter方法, è®¿é—®ç§æœ‰å˜é‡, è°ƒç”¨ç§æœ‰æ–¹æ³•, èŽ·å–æ³›åž‹ç±»åž‹Class, è¢«AOP过的真实类等工具函数.
 *
 * @author ruoyi
 */
@SuppressWarnings("rawtypes")
public class ReflectUtils
{
    private static final String SETTER_PREFIX = "set";
    private static final String GETTER_PREFIX = "get";
    private static final String CGLIB_CLASS_SEPARATOR = "$$";
    private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class);
    /**
     * è°ƒç”¨Getter方法.
     * æ”¯æŒå¤šçº§ï¼Œå¦‚:对象名.对象名.方法
     */
    @SuppressWarnings("unchecked")
    public static <E> E invokeGetter(Object obj, String propertyName)
    {
        Object object = obj;
        for (String name : StringUtils.split(propertyName, "."))
        {
            String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name);
            object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
        }
        return (E) object;
    }
    /**
     * è°ƒç”¨Setter方法, ä»…匹配方法名。
     * æ”¯æŒå¤šçº§ï¼Œå¦‚:对象名.对象名.方法
     */
    public static <E> void invokeSetter(Object obj, String propertyName, E value)
    {
        Object object = obj;
        String[] names = StringUtils.split(propertyName, ".");
        for (int i = 0; i < names.length; i++)
        {
            if (i < names.length - 1)
            {
                String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);
                object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
            }
            else
            {
                String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);
                invokeMethodByName(object, setterMethodName, new Object[] { value });
            }
        }
    }
    /**
     * ç›´æŽ¥è¯»å–对象属性值, æ— è§†private/protected修饰符, ä¸ç»è¿‡getter函数.
     */
    @SuppressWarnings("unchecked")
    public static <E> E getFieldValue(final Object obj, final String fieldName)
    {
        Field field = getAccessibleField(obj, fieldName);
        if (field == null)
        {
            logger.debug("在 [" + obj.getClass() + "] ä¸­ï¼Œæ²¡æœ‰æ‰¾åˆ° [" + fieldName + "] å­—段 ");
            return null;
        }
        E result = null;
        try
        {
            result = (E) field.get(obj);
        }
        catch (IllegalAccessException e)
        {
            logger.error("不可能抛出的异常{}", e.getMessage());
        }
        return result;
    }
    /**
     * ç›´æŽ¥è®¾ç½®å¯¹è±¡å±žæ€§å€¼, æ— è§†private/protected修饰符, ä¸ç»è¿‡setter函数.
     */
    public static <E> void setFieldValue(final Object obj, final String fieldName, final E value)
    {
        Field field = getAccessibleField(obj, fieldName);
        if (field == null)
        {
            // throw new IllegalArgumentException("在 [" + obj.getClass() + "] ä¸­ï¼Œæ²¡æœ‰æ‰¾åˆ° [" + fieldName + "] å­—段 ");
            logger.debug("在 [" + obj.getClass() + "] ä¸­ï¼Œæ²¡æœ‰æ‰¾åˆ° [" + fieldName + "] å­—段 ");
            return;
        }
        try
        {
            field.set(obj, value);
        }
        catch (IllegalAccessException e)
        {
            logger.error("不可能抛出的异常: {}", e.getMessage());
        }
    }
    /**
     * ç›´æŽ¥è°ƒç”¨å¯¹è±¡æ–¹æ³•, æ— è§†private/protected修饰符.
     * ç”¨äºŽä¸€æ¬¡æ€§è°ƒç”¨çš„æƒ…况,否则应使用getAccessibleMethod()函数获得Method后反复调用.
     * åŒæ—¶åŒ¹é…æ–¹æ³•名+参数类型,
     */
    @SuppressWarnings("unchecked")
    public static <E> E invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes,
            final Object[] args)
    {
        if (obj == null || methodName == null)
        {
            return null;
        }
        Method method = getAccessibleMethod(obj, methodName, parameterTypes);
        if (method == null)
        {
            logger.debug("在 [" + obj.getClass() + "] ä¸­ï¼Œæ²¡æœ‰æ‰¾åˆ° [" + methodName + "] æ–¹æ³• ");
            return null;
        }
        try
        {
            return (E) method.invoke(obj, args);
        }
        catch (Exception e)
        {
            String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";
            throw convertReflectionExceptionToUnchecked(msg, e);
        }
    }
    /**
     * ç›´æŽ¥è°ƒç”¨å¯¹è±¡æ–¹æ³•, æ— è§†private/protected修饰符,
     * ç”¨äºŽä¸€æ¬¡æ€§è°ƒç”¨çš„æƒ…况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用.
     * åªåŒ¹é…å‡½æ•°åï¼Œå¦‚果有多个同名函数调用第一个。
     */
    @SuppressWarnings("unchecked")
    public static <E> E invokeMethodByName(final Object obj, final String methodName, final Object[] args)
    {
        Method method = getAccessibleMethodByName(obj, methodName, args.length);
        if (method == null)
        {
            // å¦‚果为空不报错,直接返回空。
            logger.debug("在 [" + obj.getClass() + "] ä¸­ï¼Œæ²¡æœ‰æ‰¾åˆ° [" + methodName + "] æ–¹æ³• ");
            return null;
        }
        try
        {
            // ç±»åž‹è½¬æ¢ï¼ˆå°†å‚数数据类型转换为目标方法参数类型)
            Class<?>[] cs = method.getParameterTypes();
            for (int i = 0; i < cs.length; i++)
            {
                if (args[i] != null && !args[i].getClass().equals(cs[i]))
                {
                    if (cs[i] == String.class)
                    {
                        args[i] = Convert.toStr(args[i]);
                        if (StringUtils.endsWith((String) args[i], ".0"))
                        {
                            args[i] = StringUtils.substringBefore((String) args[i], ".0");
                        }
                    }
                    else if (cs[i] == Integer.class)
                    {
                        args[i] = Convert.toInt(args[i]);
                    }
                    else if (cs[i] == Long.class)
                    {
                        args[i] = Convert.toLong(args[i]);
                    }
                    else if (cs[i] == Double.class)
                    {
                        args[i] = Convert.toDouble(args[i]);
                    }
                    else if (cs[i] == Float.class)
                    {
                        args[i] = Convert.toFloat(args[i]);
                    }
                    else if (cs[i] == Date.class)
                    {
                        if (args[i] instanceof String)
                        {
                            args[i] = DateUtils.parseDate(args[i]);
                        }
                        else
                        {
                            args[i] = DateUtil.getJavaDate((Double) args[i]);
                        }
                    }
                    else if (cs[i] == boolean.class || cs[i] == Boolean.class)
                    {
                        args[i] = Convert.toBool(args[i]);
                    }
                }
            }
            return (E) method.invoke(obj, args);
        }
        catch (Exception e)
        {
            String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";
            throw convertReflectionExceptionToUnchecked(msg, e);
        }
    }
    /**
     * å¾ªçŽ¯å‘ä¸Šè½¬åž‹, èŽ·å–å¯¹è±¡çš„DeclaredField, å¹¶å¼ºåˆ¶è®¾ç½®ä¸ºå¯è®¿é—®.
     * å¦‚向上转型到Object仍无法找到, è¿”回null.
     */
    public static Field getAccessibleField(final Object obj, final String fieldName)
    {
        // ä¸ºç©ºä¸æŠ¥é”™ã€‚直接返回 null
        if (obj == null)
        {
            return null;
        }
        Validate.notBlank(fieldName, "fieldName can't be blank");
        for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass())
        {
            try
            {
                Field field = superClass.getDeclaredField(fieldName);
                makeAccessible(field);
                return field;
            }
            catch (NoSuchFieldException e)
            {
                continue;
            }
        }
        return null;
    }
    /**
     * å¾ªçŽ¯å‘ä¸Šè½¬åž‹, èŽ·å–å¯¹è±¡çš„DeclaredMethod,并强制设置为可访问.
     * å¦‚向上转型到Object仍无法找到, è¿”回null.
     * åŒ¹é…å‡½æ•°å+参数类型。
     * ç”¨äºŽæ–¹æ³•需要被多次调用的情况. å…ˆä½¿ç”¨æœ¬å‡½æ•°å…ˆå–å¾—Method,然后调用Method.invoke(Object obj, Object... args)
     */
    public static Method getAccessibleMethod(final Object obj, final String methodName,
            final Class<?>... parameterTypes)
    {
        // ä¸ºç©ºä¸æŠ¥é”™ã€‚直接返回 null
        if (obj == null)
        {
            return null;
        }
        Validate.notBlank(methodName, "methodName can't be blank");
        for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass())
        {
            try
            {
                Method method = searchType.getDeclaredMethod(methodName, parameterTypes);
                makeAccessible(method);
                return method;
            }
            catch (NoSuchMethodException e)
            {
                continue;
            }
        }
        return null;
    }
    /**
     * å¾ªçŽ¯å‘ä¸Šè½¬åž‹, èŽ·å–å¯¹è±¡çš„DeclaredMethod,并强制设置为可访问.
     * å¦‚向上转型到Object仍无法找到, è¿”回null.
     * åªåŒ¹é…å‡½æ•°åã€‚
     * ç”¨äºŽæ–¹æ³•需要被多次调用的情况. å…ˆä½¿ç”¨æœ¬å‡½æ•°å…ˆå–å¾—Method,然后调用Method.invoke(Object obj, Object... args)
     */
    public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum)
    {
        // ä¸ºç©ºä¸æŠ¥é”™ã€‚直接返回 null
        if (obj == null)
        {
            return null;
        }
        Validate.notBlank(methodName, "methodName can't be blank");
        for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass())
        {
            Method[] methods = searchType.getDeclaredMethods();
            for (Method method : methods)
            {
                if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum)
                {
                    makeAccessible(method);
                    return method;
                }
            }
        }
        return null;
    }
    /**
     * æ”¹å˜private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
     */
    public static void makeAccessible(Method method)
    {
        if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
                && !method.isAccessible())
        {
            method.setAccessible(true);
        }
    }
    /**
     * æ”¹å˜private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
     */
    public static void makeAccessible(Field field)
    {
        if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())
                || Modifier.isFinal(field.getModifiers())) && !field.isAccessible())
        {
            field.setAccessible(true);
        }
    }
    /**
     * é€šè¿‡åå°„, èŽ·å¾—Class定义中声明的泛型参数的类型, æ³¨æ„æ³›åž‹å¿…须定义在父类处
     * å¦‚无法找到, è¿”回Object.class.
     */
    @SuppressWarnings("unchecked")
    public static <T> Class<T> getClassGenricType(final Class clazz)
    {
        return getClassGenricType(clazz, 0);
    }
    /**
     * é€šè¿‡åå°„, èŽ·å¾—Class定义中声明的父类的泛型参数的类型.
     * å¦‚无法找到, è¿”回Object.class.
     */
    public static Class getClassGenricType(final Class clazz, final int index)
    {
        Type genType = clazz.getGenericSuperclass();
        if (!(genType instanceof ParameterizedType))
        {
            logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType");
            return Object.class;
        }
        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
        if (index >= params.length || index < 0)
        {
            logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
                    + params.length);
            return Object.class;
        }
        if (!(params[index] instanceof Class))
        {
            logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
            return Object.class;
        }
        return (Class) params[index];
    }
    public static Class<?> getUserClass(Object instance)
    {
        if (instance == null)
        {
            throw new RuntimeException("Instance must not be null");
        }
        Class clazz = instance.getClass();
        if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR))
        {
            Class<?> superClass = clazz.getSuperclass();
            if (superClass != null && !Object.class.equals(superClass))
            {
                return superClass;
            }
        }
        return clazz;
    }
    /**
     * å°†åå°„æ—¶çš„checked exception转换为unchecked exception.
     */
    public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e)
    {
        if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException
                || e instanceof NoSuchMethodException)
        {
            return new IllegalArgumentException(msg, e);
        }
        else if (e instanceof InvocationTargetException)
        {
            return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException());
        }
        return new RuntimeException(msg, e);
    }
}
package com.ruoyi.common.utils.reflect;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import java.lang.reflect.Method;
import java.util.List;
/**
 * åå°„工具类. æä¾›è°ƒç”¨getter/setter方法, è®¿é—®ç§æœ‰å˜é‡, è°ƒç”¨ç§æœ‰æ–¹æ³•, èŽ·å–æ³›åž‹ç±»åž‹Class, è¢«AOP过的真实类等工具函数.
 *
 * @author Lion Li
 */
@SuppressWarnings("rawtypes")
public class ReflectUtils extends ReflectUtil {
    private static final String SETTER_PREFIX = "set";
    private static final String GETTER_PREFIX = "get";
    /**
     * è°ƒç”¨Getter方法.
     * æ”¯æŒå¤šçº§ï¼Œå¦‚:对象名.对象名.方法
     */
    @SuppressWarnings("unchecked")
    public static <E> E invokeGetter(Object obj, String propertyName) {
        Object object = obj;
        for (String name : StrUtil.split(propertyName, ".")) {
            String getterMethodName = GETTER_PREFIX + StrUtil.upperFirst(name);
            object = invoke(object, getterMethodName);
        }
        return (E) object;
    }
    /**
     * è°ƒç”¨Setter方法, ä»…匹配方法名。
     * æ”¯æŒå¤šçº§ï¼Œå¦‚:对象名.对象名.方法
     */
    public static <E> void invokeSetter(Object obj, String propertyName, E value) {
        Object object = obj;
        List<String> names = StrUtil.split(propertyName, ".");
        for (int i = 0; i < names.size(); i++) {
            if (i < names.size() - 1) {
                String getterMethodName = GETTER_PREFIX + StrUtil.upperFirst(names.get(i));
                object = invoke(object, getterMethodName);
            } else {
                String setterMethodName = SETTER_PREFIX + StrUtil.upperFirst(names.get(i));
                Method method = getMethodByName(object.getClass(), setterMethodName);
                invoke(object, method, value);
            }
        }
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java
@@ -1,146 +1,65 @@
package com.ruoyi.common.utils.spring;
import cn.hutool.core.lang.Validator;
import cn.hutool.extra.spring.SpringUtil;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
 * spring工具类 æ–¹ä¾¿åœ¨éžspring管理环境中获取bean
 *
 * @author ruoyi
 * spring工具类
 *
 * @author Lion Li
 */
@Component
public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware
{
    /** Spring应用上下文环境 */
    private static ConfigurableListableBeanFactory beanFactory;
public final class SpringUtils extends SpringUtil {
    private static ApplicationContext applicationContext;
    /**
     * å¦‚æžœBeanFactory包含一个与所给名称匹配的bean定义,则返回true
     *
     * @param name
     * @return boolean
     */
    public static boolean containsBean(String name) {
        return getBeanFactory().containsBean(name);
    }
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
    {
        SpringUtils.beanFactory = beanFactory;
    }
    /**
     * åˆ¤æ–­ä»¥ç»™å®šåå­—注册的bean定义是一个singleton还是一个prototype。
     * å¦‚果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
     *
     * @param name
     * @return boolean
     */
    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
        return getBeanFactory().isSingleton(name);
    }
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
    {
        SpringUtils.applicationContext = applicationContext;
    }
    /**
     * @param name
     * @return Class æ³¨å†Œå¯¹è±¡çš„类型
     */
    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
        return getBeanFactory().getType(name);
    }
    /**
     * èŽ·å–å¯¹è±¡
     *
     * @param name
     * @return Object ä¸€ä¸ªä»¥æ‰€ç»™åå­—注册的bean的实例
     * @throws org.springframework.beans.BeansException
     *
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) throws BeansException
    {
        return (T) beanFactory.getBean(name);
    }
    /**
     * å¦‚果给定的bean名字在bean定义中有别名,则返回这些别名
     *
     * @param name
     */
    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
        return getBeanFactory().getAliases(name);
    }
    /**
     * èŽ·å–ç±»åž‹ä¸ºrequiredType的对象
     *
     * @param clz
     * @return
     * @throws org.springframework.beans.BeansException
     *
     */
    public static <T> T getBean(Class<T> clz) throws BeansException
    {
        T result = (T) beanFactory.getBean(clz);
        return result;
    }
    /**
     * èŽ·å–aop代理对象
     *
     * @param invoker
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T getAopProxy(T invoker) {
        return (T) AopContext.currentProxy();
    }
    /**
     * å¦‚æžœBeanFactory包含一个与所给名称匹配的bean定义,则返回true
     *
     * @param name
     * @return boolean
     */
    public static boolean containsBean(String name)
    {
        return beanFactory.containsBean(name);
    }
    /**
     * åˆ¤æ–­ä»¥ç»™å®šåå­—注册的bean定义是一个singleton还是一个prototype。 å¦‚果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
     *
     * @param name
     * @return boolean
     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
     *
     */
    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException
    {
        return beanFactory.isSingleton(name);
    }
    /**
     * @param name
     * @return Class æ³¨å†Œå¯¹è±¡çš„类型
     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
     *
     */
    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException
    {
        return beanFactory.getType(name);
    }
    /**
     * å¦‚果给定的bean名字在bean定义中有别名,则返回这些别名
     *
     * @param name
     * @return
     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
     *
     */
    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException
    {
        return beanFactory.getAliases(name);
    }
    /**
     * èŽ·å–aop代理对象
     *
     * @param invoker
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T getAopProxy(T invoker)
    {
        return (T) AopContext.currentProxy();
    }
    /**
     * èŽ·å–å½“å‰çš„çŽ¯å¢ƒé…ç½®ï¼Œæ— é…ç½®è¿”å›žnull
     *
     * @return å½“前的环境配置
     */
    public static String[] getActiveProfiles()
    {
        return applicationContext.getEnvironment().getActiveProfiles();
    }
    /**
     * èŽ·å–å½“å‰çš„çŽ¯å¢ƒé…ç½®ï¼Œå½“æœ‰å¤šä¸ªçŽ¯å¢ƒé…ç½®æ—¶ï¼ŒåªèŽ·å–ç¬¬ä¸€ä¸ª
     *
     * @return å½“前的环境配置
     */
    public static String getActiveProfile()
    {
        final String[] activeProfiles = getActiveProfiles();
        return Validator.isNotEmpty(activeProfiles) ? activeProfiles[0] : null;
    }
}
ruoyi-demo/src/main/java/com/ruoyi/demo/bo/TestDemoAddBo.java
@@ -5,7 +5,7 @@
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.util.Date;
/**
@@ -40,4 +40,8 @@
    @NotBlank(message = "值不能为空")
    private String value;
    /** åˆ›å»ºæ—¶é—´ */
    @ApiModelProperty("创建时间")
    private Date createTime;
}
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisLockController.java
@@ -1,8 +1,10 @@
package com.ruoyi.demo.controller;
import com.ruoyi.common.annotation.RedisLock;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import com.baomidou.lock.annotation.Lock4j;
import com.baomidou.lock.executor.RedissonLockExecutor;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.redis.RedisLockManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
@@ -10,7 +12,7 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
import java.time.LocalTime;
/**
@@ -24,44 +26,47 @@
public class RedisLockController {
    @Autowired
    private RedisLockManager redisLockManager;
    private LockTemplate lockTemplate;
    /**
     * #p0 æ ‡è¯†å–第一个参数为redis锁的key
     * æµ‹è¯•lock4j æ³¨è§£
     */
    @GetMapping("/testLock1")
    @RedisLock(expireTime = 10, key = "#p0")
    public AjaxResult<String> testLock1(String key, String value) {
    @Lock4j(keys = {"#key"})
    @GetMapping("/testLock4j")
    public  AjaxResult<String> testLock4j(String key,String value){
        System.out.println("start:"+key+",time:"+ LocalTime.now().toString());
        try {
            // åŒæ—¶è¯·æ±‚排队
//            Thread.sleep(5000);
            // é”è¶…时测试
            Thread.sleep(11000);
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("end :"+key+",time:"+LocalTime.now().toString());
        return AjaxResult.success("操作成功",value);
    }
    /**
     * æµ‹è¯•锁工具类
     * æµ‹è¯•lock4j å·¥å…·
     */
    @GetMapping("/testLock2")
    public AjaxResult<Void> testLock(String key, Long time) {
        try {
            boolean flag = redisLockManager.getLock(key, time, TimeUnit.SECONDS);
            if (flag) {
                log.info("获取锁成功: " + key);
                Thread.sleep(3000);
                redisLockManager.unLock(key);
                log.info("释放锁成功: " + key);
            } else {
                log.error("获取锁失败: " + key);
            }
        } catch (InterruptedException e) {
            log.error(e.getMessage());
    @GetMapping("/testLock4jLockTemaplate")
    public  AjaxResult<String> testLock4jLockTemaplate(String key,String value){
        final LockInfo lockInfo = lockTemplate.lock(key, 30000L, 5000L, RedissonLockExecutor.class);
        if (null == lockInfo) {
            throw new RuntimeException("业务处理中,请稍后再试");
        }
        return AjaxResult.success();
        // èŽ·å–é”æˆåŠŸï¼Œå¤„ç†ä¸šåŠ¡
        try {
            try {
                Thread.sleep(8000);
            } catch (InterruptedException e) {
                //
            }
            System.out.println("执行简单方法1 , å½“前线程:" + Thread.currentThread().getName());
        } finally {
            //释放锁
            lockTemplate.releaseLock(lockInfo);
        }
        //结束
        return AjaxResult.success("操作成功",value);
    }
    /**
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestBatchController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package com.ruoyi.demo.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.demo.domain.TestDemo;
import com.ruoyi.demo.service.ITestDemoService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
 * æµ‹è¯•批量方法
 *
 * @author Lion Li
 * @date 2021-05-30
 */
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@RestController
@RequestMapping("/demo/batch")
public class TestBatchController extends BaseController {
    private final ITestDemoService iTestDemoService;
    /**
     * æ–°å¢žæ‰¹é‡æ–¹æ³•
     */
    @PostMapping()
    public AjaxResult<Void> add() {
        List<TestDemo> list = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            list.add(new TestDemo().setOrderNum(-1L).setTestKey("批量新增").setValue("测试新增"));
        }
        return toAjax(iTestDemoService.saveAll(list) ? 1 : 0);
    }
    /**
     * ä¿®æ”¹æ‰¹é‡æ–¹æ³•
     */
    @DeleteMapping()
    public AjaxResult<Void> edit() {
        return toAjax(iTestDemoService.remove(new LambdaQueryWrapper<TestDemo>()
            .eq(TestDemo::getOrderNum, -1L)) ? 1 : 0);
    }
}
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestDemo.java
@@ -1,6 +1,8 @@
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;
@@ -23,8 +25,11 @@
    private static final long serialVersionUID=1L;
    /** ä¸»é”® */
    /**
     * ä¸»é”®
     */
    @TableId(value = "id")
    @JsonSerialize(using = ToStringSerializer.class)
    private Long id;
    /** éƒ¨é—¨id */
@@ -64,7 +69,6 @@
    private String updateBy;
    /** åˆ é™¤æ ‡å¿— */
    @TableLogic
    private Long delFlag;
}
ruoyi-demo/src/main/java/com/ruoyi/demo/domain/TestTree.java
@@ -1,13 +1,14 @@
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;
import java.io.Serializable;
import java.util.Date;
import java.math.BigDecimal;
import com.ruoyi.common.annotation.Excel;
/**
 * æµ‹è¯•树表对象 test_tree
@@ -26,6 +27,7 @@
    /** ä¸»é”® */
    @TableId(value = "id")
    @JsonSerialize(using = ToStringSerializer.class)
    private Long id;
    /** çˆ¶id */
ruoyi-demo/src/main/java/com/ruoyi/demo/feign/FeignTestService.java
@@ -1,5 +1,6 @@
package com.ruoyi.demo.feign;
import com.ruoyi.demo.feign.constant.FeignTestConstant;
import com.ruoyi.demo.feign.fallback.FeignTestFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
@@ -7,10 +8,15 @@
/**
 * feign测试service
 *
 * è§„范接口 Service æ— æ„Ÿè°ƒç”¨
 * å¸¸é‡ç®¡ç†è¯·æ±‚路径 æ›´åŠ è§„èŒƒ
 * è‡ªå®šä¹‰å®¹é”™å¤„理 å®‰å…¨å¯é 
 * @author Lion Li
 */
@FeignClient(name = "baidu",url = "http://www.baidu.com",fallback = FeignTestFallback.class)
@FeignClient(
    name = FeignTestConstant.BAIDU_NAME,
    url = FeignTestConstant.BAIDU_URL,
    fallback = FeignTestFallback.class)
public interface FeignTestService {
    @GetMapping("/s")
ruoyi-demo/src/main/java/com/ruoyi/demo/feign/constant/FeignTestConstant.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,9 @@
package com.ruoyi.demo.feign.constant;
public class FeignTestConstant {
    public static final String BAIDU_NAME = "baidu";
    public static final String BAIDU_URL = "http://www.baidu.com";
}
ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestDemoMapper.java
@@ -1,7 +1,7 @@
package com.ruoyi.demo.mapper;
import com.ruoyi.common.core.mybatisplus.MybatisPlusRedisCache;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.cache.MybatisPlusRedisCache;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.demo.domain.TestDemo;
import org.apache.ibatis.annotations.CacheNamespace;
@@ -11,6 +11,7 @@
 * @author Lion Li
 * @date 2021-05-30
 */
// å¦‚使需切换数据源 è¯·å‹¿ä½¿ç”¨ç¼“å­˜ ä¼šé€ æˆæ•°æ®ä¸ä¸€è‡´çŽ°è±¡
@CacheNamespace(implementation = MybatisPlusRedisCache.class, eviction = MybatisPlusRedisCache.class)
public interface TestDemoMapper extends BaseMapperPlus<TestDemo> {
ruoyi-demo/src/main/java/com/ruoyi/demo/mapper/TestTreeMapper.java
@@ -1,9 +1,7 @@
package com.ruoyi.demo.mapper;
import com.ruoyi.common.core.mybatisplus.MybatisPlusRedisCache;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.demo.domain.TestTree;
import org.apache.ibatis.annotations.CacheNamespace;
/**
 * æµ‹è¯•树表Mapper接口
@@ -11,7 +9,7 @@
 * @author Lion Li
 * @date 2021-05-30
 */
@CacheNamespace(implementation = MybatisPlusRedisCache.class, eviction = MybatisPlusRedisCache.class)
//@CacheNamespace(implementation = MybatisPlusRedisCache.class, eviction = MybatisPlusRedisCache.class)
public interface TestTreeMapper extends BaseMapperPlus<TestTree> {
}
ruoyi-demo/src/main/java/com/ruoyi/demo/service/ITestDemoService.java
@@ -5,7 +5,7 @@
import com.ruoyi.demo.bo.TestDemoQueryBo;
import com.ruoyi.demo.bo.TestDemoAddBo;
import com.ruoyi.demo.bo.TestDemoEditBo;
import com.ruoyi.common.core.page.IServicePlus;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import java.util.Collection;
@@ -18,6 +18,7 @@
 * @date 2021-05-30
 */
public interface ITestDemoService extends IServicePlus<TestDemo> {
    /**
     * æŸ¥è¯¢å•个
     * @return
ruoyi-demo/src/main/java/com/ruoyi/demo/service/ITestTreeService.java
@@ -1,6 +1,6 @@
package com.ruoyi.demo.service;
import com.ruoyi.common.core.page.IServicePlus;
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;
ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestDemoServiceImpl.java
@@ -4,8 +4,8 @@
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.service.impl.ServiceImpl;
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;
@@ -29,7 +29,7 @@
 * @date 2021-05-30
 */
@Service
public class TestDemoServiceImpl extends ServiceImpl<TestDemoMapper, TestDemo> implements ITestDemoService {
public class TestDemoServiceImpl extends ServicePlusImpl<TestDemoMapper, TestDemo> implements ITestDemoService {
    @Override
    public TestDemoVo queryById(Long id) {
ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/TestTreeServiceImpl.java
@@ -4,8 +4,8 @@
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.service.impl.ServiceImpl;
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;
@@ -26,13 +26,14 @@
 * @date 2021-05-30
 */
@Service
public class TestTreeServiceImpl extends ServiceImpl<TestTreeMapper, TestTree> implements ITestTreeService {
public class TestTreeServiceImpl extends ServicePlusImpl<TestTreeMapper, TestTree> implements ITestTreeService {
    @Override
    public TestTreeVo queryById(Long id) {
        return getVoById(id, TestTreeVo.class);
    }
//    @DataSource(DataSourceType.SLAVE) // åˆ‡æ¢ä»Žåº“查询
    @DataScope(isUser = true)
    @Override
    public List<TestTreeVo> queryList(TestTreeQueryBo bo) {
ruoyi-demo/src/main/java/com/ruoyi/demo/vo/TestDemoVo.java
@@ -1,10 +1,12 @@
package com.ruoyi.demo.vo;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.ruoyi.common.annotation.Excel;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@@ -21,8 +23,13 @@
    private static final long serialVersionUID = 1L;
    /** ä¸»é”® */
    /**
     * ä¸»é”®
     * å¦‚果是自定义id æˆ–者 é›ªèбid
     * éœ€è¦å¢žåŠ åºåˆ—åŒ–ä¸ºå­—ç¬¦ä¸²æ³¨è§£ å› ä¸ºLong到前端会失真
     */
    @ApiModelProperty("主键")
    @JsonSerialize(using = ToStringSerializer.class)
    private Long id;
    /** éƒ¨é—¨id */
@@ -52,7 +59,7 @@
    /** åˆ›å»ºæ—¶é—´ */
    @Excel(name = "创建时间" , width = 30, dateFormat = "yyyy-MM-dd")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
//    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty("创建时间")
    private Date createTime;
@@ -63,7 +70,7 @@
    /** æ›´æ–°æ—¶é—´ */
    @Excel(name = "更新时间" , width = 30, dateFormat = "yyyy-MM-dd")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
//    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty("更新时间")
    private Date updateTime;
ruoyi-demo/src/main/java/com/ruoyi/demo/vo/TestTreeVo.java
@@ -1,10 +1,13 @@
package com.ruoyi.demo.vo;
import com.ruoyi.common.annotation.Excel;
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;
@@ -23,6 +26,7 @@
    /** ä¸»é”® */
    @ApiModelProperty("主键")
    @JsonSerialize(using = ToStringSerializer.class)
    private Long id;
    /** çˆ¶id */
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java
@@ -1,15 +1,13 @@
package com.ruoyi.framework.aspectj;
import cn.hutool.core.lang.Validator;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.ruoyi.common.annotation.DataSource;
import com.ruoyi.framework.datasource.DynamicDataSourceContextHolder;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@@ -18,56 +16,47 @@
/**
 * å¤šæ•°æ®æºå¤„理
 *
 *
 * @author ruoyi
 */
@Aspect
@Order(1)
@Order(-500)
@Component
public class DataSourceAspect
{
    protected Logger logger = LoggerFactory.getLogger(getClass());
public class DataSourceAspect {
    @Pointcut("@annotation(com.ruoyi.common.annotation.DataSource)"
            + "|| @within(com.ruoyi.common.annotation.DataSource)")
    public void dsPointCut()
    {
    @Pointcut("@annotation(com.ruoyi.common.annotation.DataSource)"
        + "|| @within(com.ruoyi.common.annotation.DataSource)")
    public void dsPointCut() {
    }
    }
    @Around("dsPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        DataSource dataSource = getDataSource(point);
    @Around("dsPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable
    {
        DataSource dataSource = getDataSource(point);
        if (Validator.isNotNull(dataSource)) {
            DynamicDataSourceContextHolder.poll();
            String source = dataSource.value().getSource();
            DynamicDataSourceContextHolder.push(source);
        }
        if (Validator.isNotNull(dataSource))
        {
            DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());
        }
        try {
            return point.proceed();
        } finally {
            // é”€æ¯æ•°æ®æº åœ¨æ‰§è¡Œæ–¹æ³•之后
            DynamicDataSourceContextHolder.clear();
        }
    }
        try
        {
            return point.proceed();
        }
        finally
        {
            // é”€æ¯æ•°æ®æº åœ¨æ‰§è¡Œæ–¹æ³•之后
            DynamicDataSourceContextHolder.clearDataSourceType();
        }
    }
    /**
     * èŽ·å–éœ€è¦åˆ‡æ¢çš„æ•°æ®æº
     */
    public DataSource getDataSource(ProceedingJoinPoint point) {
        MethodSignature signature = (MethodSignature) point.getSignature();
        DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);
        if (Objects.nonNull(dataSource)) {
            return dataSource;
        }
    /**
     * èŽ·å–éœ€è¦åˆ‡æ¢çš„æ•°æ®æº
     */
    public DataSource getDataSource(ProceedingJoinPoint point)
    {
        MethodSignature signature = (MethodSignature) point.getSignature();
        DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);
        if (Objects.nonNull(dataSource))
        {
            return dataSource;
        }
        return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
    }
        return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
    }
}
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java
@@ -2,16 +2,14 @@
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.BusinessStatus;
import com.ruoyi.common.enums.HttpMethod;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import com.ruoyi.framework.web.service.AsyncService;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.domain.SysOperLog;
import org.aspectj.lang.JoinPoint;
@@ -32,7 +30,6 @@
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
/**
@@ -93,10 +90,10 @@
            SysOperLog operLog = new SysOperLog();
            operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
            // è¯·æ±‚的地址
            String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
            String ip = ServletUtils.getClientIP();
            operLog.setOperIp(ip);
            // è¿”回参数
            operLog.setJsonResult(JSON.toJSONString(jsonResult));
            operLog.setJsonResult(JsonUtils.toJsonString(jsonResult));
            operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());
            if (loginUser != null)
@@ -118,7 +115,7 @@
            // å¤„理设置注解上的参数
            getControllerMethodDescription(joinPoint, controllerLog, operLog);
            // ä¿å­˜æ•°æ®åº“
            AsyncManager.me().execute(AsyncFactory.recordOper(operLog));
            SpringUtils.getBean(AsyncService.class).recordOper(operLog);
        }
        catch (Exception exp)
        {
@@ -194,19 +191,16 @@
     */
    private String argsArrayToString(Object[] paramsArray)
    {
        String params = "";
        StringBuilder params = new StringBuilder();
        if (paramsArray != null && paramsArray.length > 0)
        {
            for (int i = 0; i < paramsArray.length; i++)
            {
                if (Validator.isNotNull(paramsArray[i]) && !isFilterObject(paramsArray[i]))
                {
                    Object jsonObj = JSON.toJSON(paramsArray[i]);
                    params += jsonObj.toString() + " ";
                }
            }
            for (Object o : paramsArray) {
                if (Validator.isNotNull(o) && !isFilterObject(o)) {
                    params.append(JsonUtils.toJsonString(o)).append(" ");
                }
            }
        }
        return params.trim();
        return params.toString().trim();
    }
    /**
@@ -226,19 +220,17 @@
        else if (Collection.class.isAssignableFrom(clazz))
        {
            Collection collection = (Collection) o;
            for (Iterator iter = collection.iterator(); iter.hasNext();)
            {
                return iter.next() instanceof MultipartFile;
            }
            for (Object value : collection) {
                return value instanceof MultipartFile;
            }
        }
        else if (Map.class.isAssignableFrom(clazz))
        {
            Map map = (Map) o;
            for (Iterator iter = map.entrySet().iterator(); iter.hasNext();)
            {
                Map.Entry entry = (Map.Entry) iter.next();
                return entry.getValue() instanceof MultipartFile;
            }
            for (Object value : map.entrySet()) {
                Map.Entry entry = (Map.Entry) value;
                return entry.getValue() instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
                || o instanceof BindingResult;
ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RedisLockAspect.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/config/AsyncConfig.java
@@ -2,6 +2,8 @@
import com.ruoyi.common.exception.CustomException;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
import org.springframework.scheduling.annotation.EnableAsync;
@@ -9,7 +11,7 @@
import java.util.Arrays;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
/**
 * å¼‚步配置
@@ -20,13 +22,16 @@
@Configuration
public class AsyncConfig extends AsyncConfigurerSupport {
    @Autowired
    @Qualifier("scheduledExecutorService")
    private ScheduledExecutorService scheduledExecutorService;
    /**
     * å¼‚步执行需要使用权限框架自带的包装线程池  ä¿è¯æƒé™ä¿¡æ¯çš„传递
     */
    @Override
    public Executor getAsyncExecutor() {
        return new DelegatingSecurityContextExecutorService(
            Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()));
        return new DelegatingSecurityContextExecutorService(scheduledExecutorService);
    }
    /**
ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java
@@ -1,82 +1,22 @@
package com.ruoyi.framework.config;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.sql.DataSource;
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.util.Utils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.util.Utils;
import com.ruoyi.common.enums.DataSourceType;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.framework.config.properties.DruidProperties;
import com.ruoyi.framework.datasource.DynamicDataSource;
import javax.servlet.*;
import java.io.IOException;
/**
 * druid é…ç½®å¤šæ•°æ®æº
 *
 *
 * @author ruoyi
 */
@Configuration
public class DruidConfig
{
    @Bean
    @ConfigurationProperties("spring.datasource.druid.master")
    public DataSource masterDataSource(DruidProperties druidProperties)
    {
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        return druidProperties.dataSource(dataSource);
    }
    @Bean
    @ConfigurationProperties("spring.datasource.druid.slave")
    @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
    public DataSource slaveDataSource(DruidProperties druidProperties)
    {
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        return druidProperties.dataSource(dataSource);
    }
    @Bean(name = "dynamicDataSource")
    @Primary
    public DynamicDataSource dataSource(DataSource masterDataSource)
    {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
        setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");
        return new DynamicDataSource(masterDataSource, targetDataSources);
    }
    /**
     * è®¾ç½®æ•°æ®æº
     *
     * @param targetDataSources å¤‡é€‰æ•°æ®æºé›†åˆ
     * @param sourceName æ•°æ®æºåç§°
     * @param beanName bean名称
     */
    public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName)
    {
        try
        {
            DataSource dataSource = SpringUtils.getBean(beanName);
            targetDataSources.put(sourceName, dataSource);
        }
        catch (Exception e)
        {
        }
    }
public class DruidConfig {
    /**
     * åŽ»é™¤ç›‘æŽ§é¡µé¢åº•éƒ¨çš„å¹¿å‘Š
ruoyi-framework/src/main/java/com/ruoyi/framework/config/FeignConfig.java
@@ -1,7 +1,6 @@
package com.ruoyi.framework.config;
import feign.*;
import feign.hystrix.HystrixFeign;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
@@ -36,11 +35,6 @@
    }
    @Bean
    public Feign.Builder feignBuilder() {
        return HystrixFeign.builder();
    }
    @Bean
    public Contract feignContract() {
        return new SpringMvcContract();
    }
@@ -60,4 +54,4 @@
        return new Retryer.Default();
    }
}
}
ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java
@@ -30,7 +30,7 @@
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setDispatcherTypes(DispatcherType.REQUEST);
        registration.setFilter(new XssFilter());
        registration.addUrlPatterns(StrUtil.split(xssProperties.getUrlPatterns(), ","));
        registration.addUrlPatterns(StrUtil.splitToArray(xssProperties.getUrlPatterns(), ","));
        registration.setName("xssFilter");
        registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
        Map<String, String> initParameters = new HashMap<String, String>();
ruoyi-framework/src/main/java/com/ruoyi/framework/config/JacksonConfig.java
@@ -2,35 +2,47 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.ruoyi.common.utils.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import java.time.LocalDateTime;
import java.util.TimeZone;
/**
 * å½“Mybatis plus设置为雪花ID时
 * ä½¿ç”¨æ­¤ç±»ï¼Œä¼šæŠŠæ‰€æœ‰æ•°å­—返回变为字符串返回适配前端Long型失真问题
 * jackson é…ç½®
 *
 * @author Ming LI
 * @author Lion Li
 */
@Slf4j
@Configuration
public class JacksonConfig {
    @Bean
    @Primary
    @ConditionalOnMissingBean(ObjectMapper.class)
    @ConditionalOnProperty(value = "mybatis-plus.global-config.dbConfig.idType", havingValue = "ASSIGN_ID")
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
        // å…¨å±€é…ç½®åºåˆ—化返回 JSON å¤„理
        SimpleModule simpleModule = new SimpleModule();
        //JSON Long ==> String
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        objectMapper.registerModule(simpleModule);
        return objectMapper;
    public BeanPostProcessor objectMapperBeanPostProcessor() {
        return new BeanPostProcessor() {
            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                if (!(bean instanceof ObjectMapper)) {
                    return bean;
                }
                ObjectMapper objectMapper = (ObjectMapper) bean;
                // å…¨å±€é…ç½®åºåˆ—化返回 JSON å¤„理
                SimpleModule simpleModule = new SimpleModule();
                simpleModule.addSerializer(LocalDateTime.class, LocalDateTimeSerializer.INSTANCE);
                simpleModule.addDeserializer(LocalDateTime.class, LocalDateTimeDeserializer.INSTANCE);
                objectMapper.registerModule(simpleModule);
                objectMapper.setTimeZone(TimeZone.getDefault());
                JsonUtils.init(objectMapper);
                log.info("初始化 jackson é…ç½®");
                return bean;
            }
        };
    }
}
ruoyi-framework/src/main/java/com/ruoyi/framework/config/MybatisPlusConfig.java
@@ -2,14 +2,20 @@
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.injector.ISqlInjector;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.ruoyi.common.core.mybatisplus.methods.InsertAll;
import com.ruoyi.framework.mybatisplus.CreateAndUpdateMetaObjectHandler;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import java.util.List;
/**
 * mybatis-plus配置类
@@ -94,10 +100,17 @@
     * sql注入器配置
     * https://baomidou.com/guide/sql-injector.html
     */
//    @Bean
//    public ISqlInjector sqlInjector() {
//        return new DefaultSqlInjector();
//    }
    @Bean
    public ISqlInjector sqlInjector() {
        return new DefaultSqlInjector() {
            @Override
            public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
                List<AbstractMethod> methodList = super.getMethodList(mapperClass);
                methodList.add(new InsertAll());
                return methodList;
            }
        };
    }
    /**
     * TenantLineInnerInterceptor å¤šç§Ÿæˆ·æ’ä»¶
ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java
@@ -29,9 +29,6 @@
    {
        /** æœ¬åœ°æ–‡ä»¶ä¸Šä¼ è·¯å¾„ */
        registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**").addResourceLocations("file:" + RuoYiConfig.getProfile() + "/");
        /** swagger配置 */
        registry.addResourceHandler("/swagger-ui/**").addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/");
    }
    /**
@@ -53,7 +50,7 @@
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        // è®¾ç½®è®¿é—®æºåœ°å€
        config.addAllowedOrigin("*");
        config.addAllowedOriginPattern("*");
        // è®¾ç½®è®¿é—®æºè¯·æ±‚头
        config.addAllowedHeader("*");
        // è®¾ç½®è®¿é—®æºè¯·æ±‚方法
ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
@@ -21,7 +21,7 @@
/**
 * spring security配置
 *
 *
 * @author ruoyi
 */
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@@ -32,7 +32,7 @@
     */
    @Autowired
    private UserDetailsService userDetailsService;
    /**
     * è®¤è¯å¤±è´¥å¤„理类
     */
@@ -59,7 +59,7 @@
    @Autowired
    private AdminServerProperties adminServerProperties;
    /**
     * è§£å†³ æ— æ³•直接注入 AuthenticationManager
     *
@@ -112,7 +112,7 @@
                .antMatchers("/profile/**").anonymous()
                .antMatchers("/common/download**").anonymous()
                .antMatchers("/common/download/resource**").anonymous()
                .antMatchers("/swagger-ui.html").anonymous()
                .antMatchers("/doc.html").anonymous()
                .antMatchers("/swagger-resources/**").anonymous()
                .antMatchers("/webjars/**").anonymous()
                .antMatchers("/*/api-docs").anonymous()
@@ -135,7 +135,7 @@
        httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class);
    }
    /**
     * å¼ºæ•£åˆ—哈希加密实现
     */
ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java
@@ -1,55 +1,56 @@
package com.ruoyi.framework.interceptor;
import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.ServletUtils;
/**
 * é˜²æ­¢é‡å¤æäº¤æ‹¦æˆªå™¨
 *
 * @author ruoyi
 */
@Component
public abstract class RepeatSubmitInterceptor extends HandlerInterceptorAdapter
{
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
    {
        if (handler instanceof HandlerMethod)
        {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
            if (annotation != null)
            {
                if (this.isRepeatSubmit(request))
                {
                    AjaxResult ajaxResult = AjaxResult.error("不允许重复提交,请稍后再试");
                    ServletUtils.renderString(response, JSONObject.toJSONString(ajaxResult));
                    return false;
                }
            }
            return true;
        }
        else
        {
            return super.preHandle(request, response, handler);
        }
    }
    /**
     * éªŒè¯æ˜¯å¦é‡å¤æäº¤ç”±å­ç±»å®žçŽ°å…·ä½“çš„é˜²é‡å¤æäº¤çš„è§„åˆ™
     *
     * @param request
     * @return
     * @throws Exception
     */
    public abstract boolean isRepeatSubmit(HttpServletRequest request);
}
package com.ruoyi.framework.interceptor;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.ServletUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
/**
 * é˜²æ­¢é‡å¤æäº¤æ‹¦æˆªå™¨
 *
 * @author ruoyi
 */
@Component
public abstract class RepeatSubmitInterceptor extends HandlerInterceptorAdapter
{
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
    {
        if (handler instanceof HandlerMethod)
        {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
            if (annotation != null)
            {
                if (this.isRepeatSubmit(request))
                {
                    AjaxResult ajaxResult = AjaxResult.error("不允许重复提交,请稍后再试");
                    ServletUtils.renderString(response, JsonUtils.toJsonString(ajaxResult));
                    return false;
                }
            }
            return true;
        }
        else
        {
            return super.preHandle(request, response, handler);
        }
    }
    /**
     * éªŒè¯æ˜¯å¦é‡å¤æäº¤ç”±å­ç±»å®žçŽ°å…·ä½“çš„é˜²é‡å¤æäº¤çš„è§„åˆ™
     *
     * @param request
     * @return
     * @throws Exception
     */
    public abstract boolean isRepeatSubmit(HttpServletRequest request);
}
ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java
@@ -1,126 +1,133 @@
package com.ruoyi.framework.interceptor.impl;
import cn.hutool.core.lang.Validator;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.filter.RepeatedlyRequestWrapper;
import com.ruoyi.common.utils.http.HttpHelper;
import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
 * åˆ¤æ–­è¯·æ±‚url和数据是否和上一次相同,
 * å¦‚果和上次相同,则是重复提交表单。 æœ‰æ•ˆæ—¶é—´ä¸º10秒内。
 *
 * @author ruoyi
 */
@Component
public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
{
    public final String REPEAT_PARAMS = "repeatParams";
    public final String REPEAT_TIME = "repeatTime";
    // ä»¤ç‰Œè‡ªå®šä¹‰æ ‡è¯†
    @Value("${token.header}")
    private String header;
    @Autowired
    private RedisCache redisCache;
    /**
     * é—´é𔿗¶é—´ï¼Œå•位:秒 é»˜è®¤10秒
     *
     * ä¸¤æ¬¡ç›¸åŒå‚数的请求,如果间隔时间大于该参数,系统不会认定为重复提交的数据
     */
    private int intervalTime = 10;
    public void setIntervalTime(int intervalTime)
    {
        this.intervalTime = intervalTime;
    }
    @SuppressWarnings("unchecked")
    @Override
    public boolean isRepeatSubmit(HttpServletRequest request)
    {
        String nowParams = "";
        if (request instanceof RepeatedlyRequestWrapper)
        {
            RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request;
            nowParams = HttpHelper.getBodyString(repeatedlyRequest);
        }
        // body参数为空,获取Parameter的数据
        if (Validator.isEmpty(nowParams))
        {
            nowParams = JSONObject.toJSONString(request.getParameterMap());
        }
        Map<String, Object> nowDataMap = new HashMap<String, Object>();
        nowDataMap.put(REPEAT_PARAMS, nowParams);
        nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());
        // è¯·æ±‚地址(作为存放cache的key值)
        String url = request.getRequestURI();
        // å”¯ä¸€å€¼ï¼ˆæ²¡æœ‰æ¶ˆæ¯å¤´åˆ™ä½¿ç”¨è¯·æ±‚地址)
        String submitKey = request.getHeader(header);
        if (Validator.isEmpty(submitKey))
        {
            submitKey = url;
        }
        // å”¯ä¸€æ ‡è¯†ï¼ˆæŒ‡å®škey + æ¶ˆæ¯å¤´ï¼‰
        String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + submitKey;
        Object sessionObj = redisCache.getCacheObject(cacheRepeatKey);
        if (sessionObj != null)
        {
            Map<String, Object> sessionMap = (Map<String, Object>) sessionObj;
            if (sessionMap.containsKey(url))
            {
                Map<String, Object> preDataMap = (Map<String, Object>) sessionMap.get(url);
                if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap))
                {
                    return true;
                }
            }
        }
        Map<String, Object> cacheMap = new HashMap<String, Object>();
        cacheMap.put(url, nowDataMap);
        redisCache.setCacheObject(cacheRepeatKey, cacheMap, intervalTime, TimeUnit.SECONDS);
        return false;
    }
    /**
     * åˆ¤æ–­å‚数是否相同
     */
    private boolean compareParams(Map<String, Object> nowMap, Map<String, Object> preMap)
    {
        String nowParams = (String) nowMap.get(REPEAT_PARAMS);
        String preParams = (String) preMap.get(REPEAT_PARAMS);
        return nowParams.equals(preParams);
    }
    /**
     * åˆ¤æ–­ä¸¤æ¬¡é—´é𔿗¶é—´
     */
    private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap)
    {
        long time1 = (Long) nowMap.get(REPEAT_TIME);
        long time2 = (Long) preMap.get(REPEAT_TIME);
        if ((time1 - time2) < (this.intervalTime * 1000))
        {
            return true;
        }
        return false;
    }
}
package com.ruoyi.framework.interceptor.impl;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Validator;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.filter.RepeatedlyRequestWrapper;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
 * åˆ¤æ–­è¯·æ±‚url和数据是否和上一次相同,
 * å¦‚果和上次相同,则是重复提交表单。 æœ‰æ•ˆæ—¶é—´ä¸º10秒内。
 *
 * @author ruoyi
 */
@Slf4j
@Component
public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
{
    public final String REPEAT_PARAMS = "repeatParams";
    public final String REPEAT_TIME = "repeatTime";
    // ä»¤ç‰Œè‡ªå®šä¹‰æ ‡è¯†
    @Value("${token.header}")
    private String header;
    @Autowired
    private RedisCache redisCache;
    /**
     * é—´é𔿗¶é—´ï¼Œå•位:秒 é»˜è®¤10秒
     *
     * ä¸¤æ¬¡ç›¸åŒå‚数的请求,如果间隔时间大于该参数,系统不会认定为重复提交的数据
     */
    private int intervalTime = 10;
    public void setIntervalTime(int intervalTime)
    {
        this.intervalTime = intervalTime;
    }
    @SuppressWarnings("unchecked")
    @Override
    public boolean isRepeatSubmit(HttpServletRequest request)
    {
        String nowParams = "";
        if (request instanceof RepeatedlyRequestWrapper)
        {
            RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request;
            try {
                nowParams = IoUtil.readUtf8(repeatedlyRequest.getInputStream());
            } catch (IOException e) {
                log.warn("读取流出现问题!");
            }
        }
        // body参数为空,获取Parameter的数据
        if (Validator.isEmpty(nowParams))
        {
            nowParams = JsonUtils.toJsonString(request.getParameterMap());
        }
        Map<String, Object> nowDataMap = new HashMap<String, Object>();
        nowDataMap.put(REPEAT_PARAMS, nowParams);
        nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());
        // è¯·æ±‚地址(作为存放cache的key值)
        String url = request.getRequestURI();
        // å”¯ä¸€å€¼ï¼ˆæ²¡æœ‰æ¶ˆæ¯å¤´åˆ™ä½¿ç”¨è¯·æ±‚地址)
        String submitKey = request.getHeader(header);
        if (Validator.isEmpty(submitKey))
        {
            submitKey = url;
        }
        // å”¯ä¸€æ ‡è¯†ï¼ˆæŒ‡å®škey + æ¶ˆæ¯å¤´ï¼‰
        String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + submitKey;
        Object sessionObj = redisCache.getCacheObject(cacheRepeatKey);
        if (sessionObj != null)
        {
            Map<String, Object> sessionMap = (Map<String, Object>) sessionObj;
            if (sessionMap.containsKey(url))
            {
                Map<String, Object> preDataMap = (Map<String, Object>) sessionMap.get(url);
                if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap))
                {
                    return true;
                }
            }
        }
        Map<String, Object> cacheMap = new HashMap<String, Object>();
        cacheMap.put(url, nowDataMap);
        redisCache.setCacheObject(cacheRepeatKey, cacheMap, intervalTime, TimeUnit.SECONDS);
        return false;
    }
    /**
     * åˆ¤æ–­å‚数是否相同
     */
    private boolean compareParams(Map<String, Object> nowMap, Map<String, Object> preMap)
    {
        String nowParams = (String) nowMap.get(REPEAT_PARAMS);
        String preParams = (String) preMap.get(REPEAT_PARAMS);
        return nowParams.equals(preParams);
    }
    /**
     * åˆ¤æ–­ä¸¤æ¬¡é—´é𔿗¶é—´
     */
    private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap)
    {
        long time1 = (Long) nowMap.get(REPEAT_TIME);
        long time2 = (Long) preMap.get(REPEAT_TIME);
        if ((time1 - time2) < (this.intervalTime * 1000))
        {
            return true;
        }
        return false;
    }
}
ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java
@@ -1,39 +1,41 @@
package com.ruoyi.framework.manager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.utils.Threads;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import javax.annotation.PreDestroy;
import java.util.concurrent.ScheduledExecutorService;
/**
 * ç¡®ä¿åº”用退出时能关闭后台线程
 *
 * @author ruoyi
 * @author Lion Li
 */
@Slf4j(topic = "sys-user")
@Component
public class ShutdownManager
{
    private static final Logger logger = LoggerFactory.getLogger("sys-user");
public class ShutdownManager {
    @PreDestroy
    public void destroy()
    {
        shutdownAsyncManager();
    }
    @Autowired
    @Qualifier("scheduledExecutorService")
    private ScheduledExecutorService scheduledExecutorService;
    /**
     * åœæ­¢å¼‚步执行任务
     */
    private void shutdownAsyncManager()
    {
        try
        {
            logger.info("====关闭后台任务任务线程池====");
            AsyncManager.me().shutdown();
        }
        catch (Exception e)
        {
            logger.error(e.getMessage(), e);
        }
    }
    @PreDestroy
    public void destroy() {
        shutdownAsyncManager();
    }
    /**
     * åœæ­¢å¼‚步执行任务
     */
    private void shutdownAsyncManager() {
        try {
            log.info("====关闭后台任务任务线程池====");
            Threads.shutdownAndAwaitTermination(scheduledExecutorService);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }
}
ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java
ÎļþÒÑɾ³ý
ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java
@@ -2,8 +2,8 @@
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpStatus;
import com.alibaba.fastjson.JSON;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.ServletUtils;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
@@ -16,7 +16,7 @@
/**
 * è®¤è¯å¤±è´¥å¤„理类 è¿”回未授权
 *
 *
 * @author ruoyi
 */
@Component
@@ -30,6 +30,6 @@
    {
        int code = HttpStatus.HTTP_UNAUTHORIZED;
        String msg = StrUtil.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI());
        ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg)));
        ServletUtils.renderString(response, JsonUtils.toJsonString(AjaxResult.error(code, msg)));
    }
}
ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java
@@ -2,13 +2,12 @@
import cn.hutool.core.lang.Validator;
import cn.hutool.http.HttpStatus;
import com.alibaba.fastjson.JSON;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import com.ruoyi.framework.web.service.AsyncService;
import com.ruoyi.framework.web.service.TokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
@@ -22,33 +21,33 @@
/**
 * è‡ªå®šä¹‰é€€å‡ºå¤„理类 è¿”回成功
 *
 *
 * @author ruoyi
 */
@Configuration
public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler
{
    @Autowired
    private TokenService tokenService;
public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {
    /**
     * é€€å‡ºå¤„理
     *
     * @return
     */
    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
            throws IOException, ServletException
    {
        LoginUser loginUser = tokenService.getLoginUser(request);
        if (Validator.isNotNull(loginUser))
        {
            String userName = loginUser.getUsername();
            // åˆ é™¤ç”¨æˆ·ç¼“存记录
            tokenService.delLoginUser(loginUser.getToken());
            // è®°å½•用户退出日志
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, "退出成功"));
        }
        ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(HttpStatus.HTTP_OK, "退出成功")));
    }
    @Autowired
    private TokenService tokenService;
    @Autowired
    private AsyncService asyncService;
    /**
     * é€€å‡ºå¤„理
     */
    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
        throws IOException, ServletException {
        LoginUser loginUser = tokenService.getLoginUser(request);
        if (Validator.isNotNull(loginUser)) {
            String userName = loginUser.getUsername();
            // åˆ é™¤ç”¨æˆ·ç¼“存记录
            tokenService.delLoginUser(loginUser.getToken());
            // è®°å½•用户退出日志
            asyncService.recordLogininfor(userName, Constants.LOGOUT, "退出成功", request);
        }
        ServletUtils.renderString(response, JsonUtils.toJsonString(AjaxResult.error(HttpStatus.HTTP_OK, "退出成功")));
    }
}
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/AsyncService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,96 @@
package com.ruoyi.framework.web.service;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.ip.AddressUtils;
import com.ruoyi.system.domain.SysLogininfor;
import com.ruoyi.system.domain.SysOperLog;
import com.ruoyi.system.service.ISysLogininforService;
import com.ruoyi.system.service.ISysOperLogService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
/**
 * å¼‚步工厂(产生任务用)
 *
 * @author Lion Li
 */
@Slf4j(topic = "sys-user")
@Async
@Component
public class AsyncService {
    @Autowired
    private ISysLogininforService iSysLogininforService;
    @Autowired
    private ISysOperLogService iSysOperLogService;
    /**
     * è®°å½•登录信息
     *
     * @param username ç”¨æˆ·å
     * @param status   çŠ¶æ€
     * @param message  æ¶ˆæ¯
     * @param args     åˆ—表
     */
    public void recordLogininfor(final String username, final String status, final String message,
                                 HttpServletRequest request, final Object... args) {
        final UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent"));
        final String ip = ServletUtils.getClientIP(request);
        String address = AddressUtils.getRealAddressByIP(ip);
        StringBuilder s = new StringBuilder();
        s.append(getBlock(ip));
        s.append(address);
        s.append(getBlock(username));
        s.append(getBlock(status));
        s.append(getBlock(message));
        // æ‰“印信息到日志
        log.info(s.toString(), args);
        // èŽ·å–å®¢æˆ·ç«¯æ“ä½œç³»ç»Ÿ
        String os = userAgent.getOs().getName();
        // èŽ·å–å®¢æˆ·ç«¯æµè§ˆå™¨
        String browser = userAgent.getBrowser().getName();
        // å°è£…对象
        SysLogininfor logininfor = new SysLogininfor();
        logininfor.setUserName(username);
        logininfor.setIpaddr(ip);
        logininfor.setLoginLocation(address);
        logininfor.setBrowser(browser);
        logininfor.setOs(os);
        logininfor.setMsg(message);
        // æ—¥å¿—状态
        if (Constants.LOGIN_SUCCESS.equals(status) || Constants.LOGOUT.equals(status)) {
            logininfor.setStatus(Constants.SUCCESS);
        } else if (Constants.LOGIN_FAIL.equals(status)) {
            logininfor.setStatus(Constants.FAIL);
        }
        // æ’入数据
        iSysLogininforService.insertLogininfor(logininfor);
    }
    /**
     * æ“ä½œæ—¥å¿—记录
     *
     * @param operLog æ“ä½œæ—¥å¿—信息
     */
    public void recordOper(final SysOperLog operLog) {
        // è¿œç¨‹æŸ¥è¯¢æ“ä½œåœ°ç‚¹
        operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp()));
        iSysOperLogService.insertOperlog(operLog);
    }
    private String getBlock(Object msg) {
        if (msg == null) {
            msg = "";
        }
        return "[" + msg.toString() + "]";
    }
}
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java
@@ -11,10 +11,7 @@
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.framework.config.properties.CaptchaProperties;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import com.ruoyi.system.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
@@ -24,6 +21,7 @@
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
/**
 * ç™»å½•校验方法
@@ -48,6 +46,9 @@
    @Autowired
    private ISysUserService userService;
    @Autowired
    private AsyncService asyncService;
    /**
     * ç™»å½•验证
     *
@@ -59,16 +60,17 @@
     */
    public String login(String username, String password, String code, String uuid)
    {
        HttpServletRequest request = ServletUtils.getRequest();
        if(captchaProperties.getEnabled()) {
            String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
            String captcha = redisCache.getCacheObject(verifyKey);
            redisCache.deleteObject(verifyKey);
            if (captcha == null) {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
                asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"), request);
                throw new CaptchaExpireException();
            }
            if (!code.equalsIgnoreCase(captcha)) {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
                asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"), request);
                throw new CaptchaException();
            }
        }
@@ -84,16 +86,16 @@
        {
            if (e instanceof BadCredentialsException)
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
                asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"), request);
                throw new UserPasswordNotMatchException();
            }
            else
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
                asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage(), request);
                throw new CustomException(e.getMessage());
            }
        }
        AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
        asyncService.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"), request);
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        recordLoginInfo(loginUser.getUser());
        // ç”Ÿæˆtoken
@@ -105,7 +107,7 @@
     */
    public void recordLoginInfo(SysUser user)
    {
        user.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
        user.setLoginIp(ServletUtils.getClientIP());
        user.setLoginDate(DateUtils.getNowDate());
        user.setUpdateBy(user.getUserName());
        userService.updateUserProfile(user);
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java
@@ -9,7 +9,6 @@
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.ip.AddressUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.framework.config.properties.TokenProperties;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
@@ -131,7 +130,7 @@
     */
    public void setUserAgent(LoginUser loginUser) {
        UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
        String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
        String ip = ServletUtils.getClientIP();
        loginUser.setIpaddr(ip);
        loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
        loginUser.setBrowser(userAgent.getBrowser().getName());
ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java
@@ -1,6 +1,6 @@
package com.ruoyi.generator.mapper;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.generator.domain.GenTableColumn;
import java.util.List;
ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java
@@ -1,7 +1,7 @@
package com.ruoyi.generator.mapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.generator.domain.GenTable;
import org.apache.ibatis.annotations.Param;
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.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
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 ServiceImpl<GenTableColumnMapper, GenTableColumn> implements IGenTableColumnService {
public class GenTableColumnServiceImpl extends ServicePlusImpl<GenTableColumnMapper, GenTableColumn> implements IGenTableColumnService {
    /**
     * æŸ¥è¯¢ä¸šåŠ¡å­—æ®µåˆ—è¡¨
ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java
@@ -1,17 +1,17 @@
package com.ruoyi.generator.service;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
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;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.file.FileUtils;
@@ -50,7 +50,7 @@
 */
@Slf4j
@Service
public class GenTableServiceImpl extends ServiceImpl<GenTableMapper, GenTable> implements IGenTableService {
public class GenTableServiceImpl extends ServicePlusImpl<GenTableMapper, GenTable> implements IGenTableService {
    @Autowired
    private GenTableColumnMapper genTableColumnMapper;
@@ -130,7 +130,7 @@
    @Override
    @Transactional
    public void updateGenTable(GenTable genTable) {
        String options = JSON.toJSONString(genTable.getParams());
        String options = JsonUtils.toJsonString(genTable.getParams());
        genTable.setOptions(options);
        int row = baseMapper.updateById(genTable);
        if (row > 0) {
@@ -263,12 +263,8 @@
                StringWriter sw = new StringWriter();
                Template tpl = Velocity.getTemplate(template, Constants.UTF8);
                tpl.merge(context, sw);
                try {
                    String path = getGenPath(table, template);
                    FileUtils.writeStringToFile(new File(path), sw.toString(), Constants.UTF8);
                } catch (IOException e) {
                    throw new CustomException("渲染模板失败,表名:" + table.getTableName());
                }
                String path = getGenPath(table, template);
                FileUtils.writeUtf8String(sw.toString(), path);
            }
        }
    }
@@ -365,13 +361,12 @@
    @Override
    public void validateEdit(GenTable genTable) {
        if (GenConstants.TPL_TREE.equals(genTable.getTplCategory())) {
            String options = JSON.toJSONString(genTable.getParams());
            JSONObject paramsObj = JSONObject.parseObject(options);
            if (Validator.isEmpty(paramsObj.getString(GenConstants.TREE_CODE))) {
            Map<String, Object> paramsObj = genTable.getParams();
            if (Validator.isEmpty(paramsObj.get(GenConstants.TREE_CODE))) {
                throw new CustomException("树编码字段不能为空");
            } else if (Validator.isEmpty(paramsObj.getString(GenConstants.TREE_PARENT_CODE))) {
            } else if (Validator.isEmpty(paramsObj.get(GenConstants.TREE_PARENT_CODE))) {
                throw new CustomException("树父编码字段不能为空");
            } else if (Validator.isEmpty(paramsObj.getString(GenConstants.TREE_NAME))) {
            } else if (Validator.isEmpty(paramsObj.get(GenConstants.TREE_NAME))) {
                throw new CustomException("树名称字段不能为空");
            } else if (GenConstants.TPL_SUB.equals(genTable.getTplCategory())) {
                if (Validator.isEmpty(genTable.getSubTableName())) {
@@ -429,13 +424,13 @@
     * @param genTable è®¾ç½®åŽçš„生成对象
     */
    public void setTableFromOptions(GenTable genTable) {
        JSONObject paramsObj = JSONObject.parseObject(genTable.getOptions());
        Map<String, Object> paramsObj = JsonUtils.parseMap(genTable.getOptions());
        if (Validator.isNotNull(paramsObj)) {
            String treeCode = paramsObj.getString(GenConstants.TREE_CODE);
            String treeParentCode = paramsObj.getString(GenConstants.TREE_PARENT_CODE);
            String treeName = paramsObj.getString(GenConstants.TREE_NAME);
            String parentMenuId = paramsObj.getString(GenConstants.PARENT_MENU_ID);
            String parentMenuName = paramsObj.getString(GenConstants.PARENT_MENU_NAME);
            String treeCode = Convert.toStr(paramsObj.get(GenConstants.TREE_CODE));
            String treeParentCode = Convert.toStr(paramsObj.get(GenConstants.TREE_PARENT_CODE));
            String treeName = Convert.toStr(paramsObj.get(GenConstants.TREE_NAME));
            String parentMenuId = Convert.toStr(paramsObj.get(GenConstants.PARENT_MENU_ID));
            String parentMenuName = Convert.toStr(paramsObj.get(GenConstants.PARENT_MENU_NAME));
            genTable.setTreeCode(treeCode);
            genTable.setTreeParentCode(treeParentCode);
@@ -448,7 +443,7 @@
    /**
     * èŽ·å–ä»£ç ç”Ÿæˆåœ°å€
     *
     * @param table    ä¸šåŠ¡è¡¨ä¿¡æ¯
     * @param table ä¸šåŠ¡è¡¨ä¿¡æ¯
     * @param template æ¨¡æ¿æ–‡ä»¶è·¯å¾„
     * @return ç”Ÿæˆåœ°å€
     */
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.page.IServicePlus;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.generator.domain.GenTableColumn;
import java.util.List;
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.page.IServicePlus;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.generator.domain.GenTable;
ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java
@@ -11,7 +11,7 @@
/**
 * ä»£ç ç”Ÿæˆå™¨ å·¥å…·ç±»
 *
 *
 * @author ruoyi
 */
public class GenUtils
@@ -61,7 +61,7 @@
            column.setHtmlType(GenConstants.HTML_INPUT);
            // å¦‚果是浮点型 ç»Ÿä¸€ç”¨BigDecimal
            String[] str = StrUtil.split(StrUtil.subBetween(column.getColumnType(), "(", ")"), ",");
            String[] str = StrUtil.splitToArray(StrUtil.subBetween(column.getColumnType(), "(", ")"), ",");
            if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0)
            {
                column.setJavaType(GenConstants.TYPE_BIGDECIMAL);
@@ -132,7 +132,7 @@
    /**
     * æ ¡éªŒæ•°ç»„是否包含指定值
     *
     *
     * @param arr æ•°ç»„
     * @param targetValue å€¼
     * @return æ˜¯å¦åŒ…含
@@ -144,7 +144,7 @@
    /**
     * èŽ·å–æ¨¡å—å
     *
     *
     * @param packageName åŒ…名
     * @return æ¨¡å—名
     */
@@ -158,7 +158,7 @@
    /**
     * èŽ·å–ä¸šåŠ¡å
     *
     *
     * @param tableName è¡¨å
     * @return ä¸šåŠ¡å
     */
@@ -172,7 +172,7 @@
    /**
     * è¡¨åè½¬æ¢æˆJava类名
     *
     *
     * @param tableName è¡¨åç§°
     * @return ç±»å
     */
@@ -182,7 +182,7 @@
        String tablePrefix = GenConfig.getTablePrefix();
        if (autoRemovePre && StrUtil.isNotEmpty(tablePrefix))
        {
            String[] searchList = StrUtil.split(tablePrefix, ",");
            String[] searchList = StrUtil.splitToArray(tablePrefix, ",");
            tableName = replaceFirst(tableName, searchList);
        }
        return StrUtil.upperFirst(StrUtil.toCamelCase(tableName));
@@ -190,7 +190,7 @@
    /**
     * æ‰¹é‡æ›¿æ¢å‰ç¼€
     *
     *
     * @param replacementm æ›¿æ¢å€¼
     * @param searchList æ›¿æ¢åˆ—表
     * @return
@@ -211,7 +211,7 @@
    /**
     * å…³é”®å­—替换
     *
     *
     * @param text éœ€è¦è¢«æ›¿æ¢çš„名字
     * @return æ›¿æ¢åŽçš„名字
     */
@@ -222,7 +222,7 @@
    /**
     * èŽ·å–æ•°æ®åº“ç±»åž‹å­—æ®µ
     *
     *
     * @param columnType åˆ—类型
     * @return æˆªå–后的列类型
     */
@@ -240,7 +240,7 @@
    /**
     * èŽ·å–å­—æ®µé•¿åº¦
     *
     *
     * @param columnType åˆ—类型
     * @return æˆªå–后的列类型
     */
ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java
@@ -1,10 +1,11 @@
package com.ruoyi.generator.util;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.constant.GenConstants;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.generator.domain.GenTable;
import com.ruoyi.generator.domain.GenTableColumn;
import org.apache.velocity.VelocityContext;
@@ -12,10 +13,11 @@
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
/**
 * æ¨¡æ¿å¤„理工具类
 *
 *
 * @author ruoyi
 */
public class VelocityUtils
@@ -75,7 +77,7 @@
    public static void setMenuVelocityContext(VelocityContext context, GenTable genTable)
    {
        String options = genTable.getOptions();
        JSONObject paramsObj = JSONObject.parseObject(options);
        Map<String, Object> paramsObj = JsonUtils.parseMap(options);
        String parentMenuId = getParentMenuId(paramsObj);
        context.put("parentMenuId", parentMenuId);
    }
@@ -83,7 +85,7 @@
    public static void setTreeVelocityContext(VelocityContext context, GenTable genTable)
    {
        String options = genTable.getOptions();
        JSONObject paramsObj = JSONObject.parseObject(options);
        Map<String, Object> paramsObj = JsonUtils.parseMap(options);
        String treeCode = getTreecode(paramsObj);
        String treeParentCode = getTreeParentCode(paramsObj);
        String treeName = getTreeName(paramsObj);
@@ -94,11 +96,11 @@
        context.put("expandColumn", getExpandColumn(genTable));
        if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE))
        {
            context.put("tree_parent_code", paramsObj.getString(GenConstants.TREE_PARENT_CODE));
            context.put("tree_parent_code", paramsObj.get(GenConstants.TREE_PARENT_CODE));
        }
        if (paramsObj.containsKey(GenConstants.TREE_NAME))
        {
            context.put("tree_name", paramsObj.getString(GenConstants.TREE_NAME));
            context.put("tree_name", paramsObj.get(GenConstants.TREE_NAME));
        }
    }
@@ -300,11 +302,11 @@
     * @param paramsObj ç”Ÿæˆå…¶ä»–选项
     * @return ä¸Šçº§èœå•ID字段
     */
    public static String getParentMenuId(JSONObject paramsObj)
    public static String getParentMenuId(Map<String, Object> paramsObj)
    {
        if (Validator.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.PARENT_MENU_ID))
        {
            return paramsObj.getString(GenConstants.PARENT_MENU_ID);
            return Convert.toStr(paramsObj.get(GenConstants.PARENT_MENU_ID));
        }
        return DEFAULT_PARENT_MENU_ID;
    }
@@ -315,11 +317,11 @@
     * @param paramsObj ç”Ÿæˆå…¶ä»–选项
     * @return æ ‘编码
     */
    public static String getTreecode(JSONObject paramsObj)
    public static String getTreecode(Map<String, Object> paramsObj)
    {
        if (paramsObj.containsKey(GenConstants.TREE_CODE))
        if (Validator.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_CODE))
        {
            return StrUtil.toCamelCase(paramsObj.getString(GenConstants.TREE_CODE));
            return StrUtil.toCamelCase(Convert.toStr(paramsObj.get(GenConstants.TREE_CODE)));
        }
        return StrUtil.EMPTY;
    }
@@ -330,11 +332,11 @@
     * @param paramsObj ç”Ÿæˆå…¶ä»–选项
     * @return æ ‘父编码
     */
    public static String getTreeParentCode(JSONObject paramsObj)
    public static String getTreeParentCode(Map<String, Object> paramsObj)
    {
        if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE))
        if (Validator.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_PARENT_CODE))
        {
            return StrUtil.toCamelCase(paramsObj.getString(GenConstants.TREE_PARENT_CODE));
            return StrUtil.toCamelCase(Convert.toStr(paramsObj.get(GenConstants.TREE_PARENT_CODE)));
        }
        return StrUtil.EMPTY;
    }
@@ -345,11 +347,11 @@
     * @param paramsObj ç”Ÿæˆå…¶ä»–选项
     * @return æ ‘名称
     */
    public static String getTreeName(JSONObject paramsObj)
    public static String getTreeName(Map<String, Object> paramsObj)
    {
        if (paramsObj.containsKey(GenConstants.TREE_NAME))
        if (Validator.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_NAME))
        {
            return StrUtil.toCamelCase(paramsObj.getString(GenConstants.TREE_NAME));
            return StrUtil.toCamelCase(Convert.toStr(paramsObj.get(GenConstants.TREE_NAME)));
        }
        return StrUtil.EMPTY;
    }
@@ -363,8 +365,8 @@
    public static int getExpandColumn(GenTable genTable)
    {
        String options = genTable.getOptions();
        JSONObject paramsObj = JSONObject.parseObject(options);
        String treeName = paramsObj.getString(GenConstants.TREE_NAME);
        Map<String, Object> paramsObj = JsonUtils.parseMap(options);
        String treeName = Convert.toStr(paramsObj.get(GenConstants.TREE_NAME));
        int num = 0;
        for (GenTableColumn column : genTable.getColumns())
        {
ruoyi-generator/src/main/resources/vm/java/addBo.java.vm
@@ -2,7 +2,6 @@
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.util.Date;
import javax.validation.constraints.*;
@@ -27,9 +26,6 @@
    /** $column.columnComment */
    @ApiModelProperty("$column.columnComment")
#if($column.javaType == 'Date')
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
#end
#if($column.isRequired==1)
#if($column.javaType == 'String')
    @NotBlank(message = "$column.columnComment不能为空")
ruoyi-generator/src/main/resources/vm/java/domain.java.vm
@@ -7,7 +7,6 @@
import java.io.Serializable;
import java.util.Date;
import java.math.BigDecimal;
import com.ruoyi.common.annotation.Excel;
/**
 * ${functionName}对象 ${tableName}
ruoyi-generator/src/main/resources/vm/java/editBo.java.vm
@@ -2,7 +2,6 @@
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.util.Date;
import javax.validation.constraints.*;
@@ -26,9 +25,6 @@
    /** $column.columnComment */
    @ApiModelProperty("$column.columnComment")
#if($column.javaType == 'Date')
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
#end
#if($column.isRequired==1)
#if($column.javaType == 'String')
    @NotBlank(message = "$column.columnComment不能为空")
ruoyi-generator/src/main/resources/vm/java/mapper.java.vm
@@ -1,8 +1,8 @@
package ${packageName}.mapper;
import ${packageName}.domain.${ClassName};
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.MybatisPlusRedisCache;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.cache.MybatisPlusRedisCache;
import org.apache.ibatis.annotations.CacheNamespace;
/**
@@ -11,6 +11,7 @@
 * @author ${author}
 * @date ${datetime}
 */
// å¦‚使需切换数据源 è¯·å‹¿ä½¿ç”¨ç¼“å­˜ ä¼šé€ æˆæ•°æ®ä¸ä¸€è‡´çŽ°è±¡
@CacheNamespace(implementation = MybatisPlusRedisCache.class, eviction = MybatisPlusRedisCache.class)
public interface ${ClassName}Mapper extends BaseMapperPlus<${ClassName}> {
ruoyi-generator/src/main/resources/vm/java/queryBo.java.vm
@@ -50,9 +50,6 @@
#foreach ($column in $columns)
#if(!$table.isSuperColumn($column.javaField) && $column.query)
    /** $column.columnComment */
#if($column.javaType == 'Date')
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
#end
    @ApiModelProperty("$column.columnComment")
    private $column.javaType $column.javaField;
#end
ruoyi-generator/src/main/resources/vm/java/service.java.vm
@@ -5,7 +5,7 @@
import ${packageName}.bo.${ClassName}QueryBo;
import ${packageName}.bo.${ClassName}AddBo;
import ${packageName}.bo.${ClassName}EditBo;
import com.ruoyi.common.core.page.IServicePlus;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
#if($table.crud || $table.sub)
import com.ruoyi.common.core.page.TableDataInfo;
#end
@@ -58,5 +58,5 @@
     * @param isValid æ˜¯å¦æ ¡éªŒ,true-删除前校验,false-不校验
     * @return
     */
    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
    Boolean deleteWithValidByIds(Collection<${pkColumn.javaType}> ids, Boolean isValid);
}
ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm
@@ -8,7 +8,7 @@
import com.ruoyi.common.core.page.TableDataInfo;
#end
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
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;
@@ -30,7 +30,7 @@
 * @date ${datetime}
 */
@Service
public class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}> implements I${ClassName}Service {
public class ${ClassName}ServiceImpl extends ServicePlusImpl<${ClassName}Mapper, ${ClassName}> implements I${ClassName}Service {
    @Override
    public ${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField}){
@@ -101,7 +101,7 @@
    }
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
    public Boolean deleteWithValidByIds(Collection<${pkColumn.javaType}> ids, Boolean isValid) {
        if(isValid){
            //TODO åšä¸€äº›ä¸šåŠ¡ä¸Šçš„æ ¡éªŒ,判断是否需要校验
        }
ruoyi-generator/src/main/resources/vm/java/vo.java.vm
@@ -1,7 +1,6 @@
package ${packageName}.vo;
import com.ruoyi.common.annotation.Excel;
import com.fasterxml.jackson.annotation.JsonFormat;
#foreach ($import in $importList)
import ${import};
#end
@@ -41,7 +40,6 @@
    @Excel(name = "${comment}" , readConverterExp = "$column.readConverterExp()")
#elseif($column.javaType == 'Date')
    @Excel(name = "${comment}" , width = 30, dateFormat = "yyyy-MM-dd")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
#else
    @Excel(name = "${comment}")
#end
ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm
@@ -1,548 +1,546 @@
<template>
  <div class="app-container">
    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
#foreach($column in $columns)
#if($column.query)
#set($dictType=$column.dictType)
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if($column.htmlType == "input")
      <el-form-item label="${comment}" prop="${column.javaField}">
        <el-input
          v-model="queryParams.${column.javaField}"
          placeholder="请输入${comment}"
          clearable
          size="small"
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType)
      <el-form-item label="${comment}" prop="${column.javaField}">
        <el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable size="small">
          <el-option
            v-for="dict in ${column.javaField}Options"
            :key="dict.dictValue"
            :label="dict.dictLabel"
            :value="dict.dictValue"
          />
        </el-select>
      </el-form-item>
#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $dictType)
      <el-form-item label="${comment}" prop="${column.javaField}">
        <el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable size="small">
          <el-option label="请选择字典生成" value="" />
        </el-select>
      </el-form-item>
#elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN")
      <el-form-item label="${comment}" prop="${column.javaField}">
        <el-date-picker clearable size="small"
          v-model="queryParams.${column.javaField}"
          type="date"
          value-format="yyyy-MM-dd"
          placeholder="选择${comment}">
        </el-date-picker>
      </el-form-item>
#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
      <el-form-item label="${comment}">
        <el-date-picker
          v-model="daterange${AttrName}"
          size="small"
          style="width: 240px"
          value-format="yyyy-MM-dd"
          type="daterange"
          range-separator="-"
          start-placeholder="开始日期"
          end-placeholder="结束日期"
        ></el-date-picker>
      </el-form-item>
#end
#end
#end
      <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="handleAdd"
          v-hasPermi="['${moduleName}:${businessName}:add']"
        >新增</el-button>
      </el-col>
      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
    </el-row>
    <el-table
      v-loading="loading"
      :data="${businessName}List"
      row-key="${treeCode}"
      default-expand-all
      :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
    >
#foreach($column in $columns)
#set($javaField=$column.javaField)
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if($column.pk)
#elseif($column.list && $column.htmlType == "datetime")
      <el-table-column label="${comment}" align="center" prop="${javaField}" width="180">
        <template slot-scope="scope">
          <span>{{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}</span>
        </template>
      </el-table-column>
#elseif($column.list && "" != $column.dictType)
      <el-table-column label="${comment}" align="center" prop="${javaField}" :formatter="${javaField}Format" />
#elseif($column.list && "" != $javaField)
#if(${foreach.index} == 1)
      <el-table-column label="${comment}" prop="${javaField}" />
#else
      <el-table-column label="${comment}" align="center" prop="${javaField}" />
#end
#end
#end
      <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="handleUpdate(scope.row)"
            v-hasPermi="['${moduleName}:${businessName}:edit']"
          >修改</el-button>
          <el-button
            size="mini"
            type="text"
            icon="el-icon-plus"
            @click="handleAdd(scope.row)"
            v-hasPermi="['${moduleName}:${businessName}:add']"
          >新增</el-button>
          <el-button
            size="mini"
            type="text"
            icon="el-icon-delete"
            @click="handleDelete(scope.row)"
            v-hasPermi="['${moduleName}:${businessName}:remove']"
          >删除</el-button>
        </template>
      </el-table-column>
    </el-table>
    <!-- æ·»åŠ æˆ–ä¿®æ”¹${functionName}对话框 -->
    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
#foreach($column in $columns)
#set($field=$column.javaField)
#if($column.insert && !$column.pk)
#if(($column.usableColumn) || (!$column.superColumn))
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#set($dictType=$column.dictType)
#if("" != $treeParentCode && $column.javaField == $treeParentCode)
        <el-form-item label="${comment}" prop="${treeParentCode}">
          <treeselect v-model="form.${treeParentCode}" :options="${businessName}Options" :normalizer="normalizer" placeholder="请选择${comment}" />
        </el-form-item>
#elseif($column.htmlType == "input")
        <el-form-item label="${comment}" prop="${field}">
          <el-input v-model="form.${field}" placeholder="请输入${comment}" />
        </el-form-item>
#elseif($column.htmlType == "imageUpload")
        <el-form-item label="${comment}">
          <imageUpload v-model="form.${field}"/>
        </el-form-item>
#elseif($column.htmlType == "fileUpload")
        <el-form-item label="${comment}">
          <fileUpload v-model="form.${field}"/>
        </el-form-item>
#elseif($column.htmlType == "editor")
        <el-form-item label="${comment}">
          <editor v-model="form.${field}" :min-height="192"/>
        </el-form-item>
#elseif($column.htmlType == "select" && "" != $dictType)
        <el-form-item label="${comment}" prop="${field}">
          <el-select v-model="form.${field}" placeholder="请选择${comment}">
            <el-option
              v-for="dict in ${field}Options"
              :key="dict.dictValue"
              :label="dict.dictLabel"
              #if($column.javaType == "Integer" || $column.javaType == "Long"):value="parseInt(dict.dictValue)"#else:value="dict.dictValue"#end
            ></el-option>
          </el-select>
        </el-form-item>
#elseif($column.htmlType == "select" && $dictType)
        <el-form-item label="${comment}" prop="${field}">
          <el-select v-model="form.${field}" placeholder="请选择${comment}">
            <el-option label="请选择字典生成" value="" />
          </el-select>
        </el-form-item>
#elseif($column.htmlType == "checkbox" && "" != $dictType)
        <el-form-item label="${comment}">
          <el-checkbox-group v-model="form.${field}">
            <el-checkbox
              v-for="dict in ${field}Options"
              :key="dict.dictValue"
              :label="dict.dictValue">
              {{dict.dictLabel}}
            </el-checkbox>
          </el-checkbox-group>
        </el-form-item>
#elseif($column.htmlType == "checkbox" && $dictType)
        <el-form-item label="${comment}">
          <el-checkbox-group v-model="form.${field}">
            <el-checkbox>请选择字典生成</el-checkbox>
          </el-checkbox-group>
        </el-form-item>
#elseif($column.htmlType == "radio" && "" != $dictType)
        <el-form-item label="${comment}">
          <el-radio-group v-model="form.${field}">
            <el-radio
              v-for="dict in ${field}Options"
              :key="dict.dictValue"
              #if($column.javaType == "Integer" || $column.javaType == "Long"):label="parseInt(dict.dictValue)"#else:label="dict.dictValue"#end
            >{{dict.dictLabel}}</el-radio>
          </el-radio-group>
        </el-form-item>
#elseif($column.htmlType == "radio" && $dictType)
        <el-form-item label="${comment}">
          <el-radio-group v-model="form.${field}">
            <el-radio label="1">请选择字典生成</el-radio>
          </el-radio-group>
        </el-form-item>
#elseif($column.htmlType == "datetime")
        <el-form-item label="${comment}" prop="${field}">
          <el-date-picker clearable size="small"
            v-model="form.${field}"
            type="datetime"
            value-format="yyyy-MM-dd HH:mm:ss"
            placeholder="选择${comment}">
          </el-date-picker>
        </el-form-item>
#elseif($column.htmlType == "textarea")
        <el-form-item label="${comment}" prop="${field}">
          <el-input v-model="form.${field}" type="textarea" placeholder="请输入内容" />
        </el-form-item>
#end
#end
#end
#end
      </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 { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName}, export${BusinessName} } from "@/api/${moduleName}/${businessName}";
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "imageUpload")
import ImageUpload from '@/components/ImageUpload';
#break
#end
#end
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "fileUpload")
import FileUpload from '@/components/FileUpload';
#break
#end
#end
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "editor")
import Editor from '@/components/Editor';
#break
#end
#end
export default {
  name: "${BusinessName}",
  components: {
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "imageUpload")
    ImageUpload,
#break
#end
#end
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "fileUpload")
    FileUpload,
#break
#end
#end
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "editor")
    Editor,
#break
#end
#end
    Treeselect
  },
  data() {
    return {
      //按钮loading
      buttonLoading: false,
      // é®ç½©å±‚
      loading: true,
      // æ˜¾ç¤ºæœç´¢æ¡ä»¶
      showSearch: true,
      // ${functionName}表格数据
      ${businessName}List: [],
      // ${functionName}树选项
      ${businessName}Options: [],
      // å¼¹å‡ºå±‚标题
      title: "",
      // æ˜¯å¦æ˜¾ç¤ºå¼¹å‡ºå±‚
      open: false,
#foreach ($column in $columns)
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if(${column.dictType} != '')
      // $comment字典
      ${column.javaField}Options: [],
#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
      // $comment时间范围
      daterange${AttrName}: [],
#end
#end
      // æŸ¥è¯¢å‚æ•°
      queryParams: {
#foreach ($column in $columns)
#if($column.query)
        $column.javaField: null#if($velocityCount != $columns.size()),#end
#end
#end
      },
      // è¡¨å•参数
      form: {},
      // è¡¨å•校验
      rules: {
#foreach ($column in $columns)
#if($column.required)
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
        $column.javaField: [
          { required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select")"change"#else"blur"#end }
        ]#if($velocityCount != $columns.size()),#end
#end
#end
      }
    };
  },
  created() {
    this.getList();
#foreach ($column in $columns)
#if(${column.dictType} != '')
    this.getDicts("${column.dictType}").then(response => {
      this.${column.javaField}Options = response.data;
    });
#end
#end
  },
  methods: {
    /** æŸ¥è¯¢${functionName}列表 */
    getList() {
      this.loading = true;
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
      this.queryParams.params = {};
#break
#end
#end
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
      if (null != this.daterange${AttrName} && '' != this.daterange${AttrName}) {
        this.queryParams.params["begin${AttrName}"] = this.daterange${AttrName}[0];
        this.queryParams.params["end${AttrName}"] = this.daterange${AttrName}[1];
      }
#end
#end
      list${BusinessName}(this.queryParams).then(response => {
        this.${businessName}List = this.handleTree(response.data, "${treeCode}", "${treeParentCode}");
        this.loading = false;
      });
    },
    /** è½¬æ¢${functionName}数据结构 */
    normalizer(node) {
      if (node.children && !node.children.length) {
        delete node.children;
      }
      return {
        id: node.${treeCode},
        label: node.${treeName},
        children: node.children
      };
    },
    /** æŸ¥è¯¢${functionName}下拉树结构 */
    getTreeselect() {
      list${BusinessName}().then(response => {
        this.${businessName}Options = [];
        const data = { ${treeCode}: 0, ${treeName}: '顶级节点', children: [] };
        data.children = this.handleTree(response.data, "${treeCode}", "${treeParentCode}");
        this.${businessName}Options.push(data);
      });
    },
#foreach ($column in $columns)
#if(${column.dictType} != '')
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
    // $comment字典翻译
    ${column.javaField}Format(row, column) {
      return this.selectDictLabel#if($column.htmlType == "checkbox")s#end(this.${column.javaField}Options, row.${column.javaField});
    },
#end
#end
    // å–消按钮
    cancel() {
      this.open = false;
      this.reset();
    },
    // è¡¨å•重置
    reset() {
      this.form = {
#foreach ($column in $columns)
#if($column.htmlType == "radio")
        $column.javaField: #if($column.javaType == "Integer" || $column.javaType == "Long")0#else"0"#end#if($velocityCount != $columns.size()),#end
#elseif($column.htmlType == "checkbox")
        $column.javaField: []#if($velocityCount != $columns.size()),#end
#else
        $column.javaField: null#if($velocityCount != $columns.size()),#end
#end
#end
      };
      this.resetForm("form");
    },
    /** æœç´¢æŒ‰é’®æ“ä½œ */
    handleQuery() {
      this.getList();
    },
    /** é‡ç½®æŒ‰é’®æ“ä½œ */
    resetQuery() {
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
      this.daterange${AttrName} = [];
#end
#end
      this.resetForm("queryForm");
      this.handleQuery();
    },
    /** æ–°å¢žæŒ‰é’®æ“ä½œ */
    handleAdd(row) {
      this.reset();
      this.getTreeselect();
      if (row != null && row.${treeCode}) {
        this.form.${treeParentCode} = row.${treeCode};
      } else {
        this.form.${treeParentCode} = 0;
      }
      this.open = true;
      this.title = "添加${functionName}";
    },
    /** ä¿®æ”¹æŒ‰é’®æ“ä½œ */
    handleUpdate(row) {
      this.loading = true;
      this.reset();
      this.getTreeselect();
      if (row != null) {
        this.form.${treeParentCode} = row.${treeCode};
      }
      get${BusinessName}(row.${pkColumn.javaField}).then(response => {
        this.loading = false;
        this.form = response.data;
#foreach ($column in $columns)
#if($column.htmlType == "checkbox")
        this.form.$column.javaField = this.form.${column.javaField}.split(",");
#end
#end
        this.open = true;
        this.title = "修改${functionName}";
      });
    },
    /** æäº¤æŒ‰é’® */
    submitForm() {
      this.#[[$]]#refs["form"].validate(valid => {
        if (valid) {
          this.buttonLoading = true;
#foreach ($column in $columns)
#if($column.htmlType == "checkbox")
          this.form.$column.javaField = this.form.${column.javaField}.join(",");
#end
#end
          if (this.form.${pkColumn.javaField} != null) {
            update${BusinessName}(this.form).then(response => {
              this.buttonLoading = false;
              this.msgSuccess("修改成功");
              this.open = false;
              this.getList();
            });
          } else {
            add${BusinessName}(this.form).then(response => {
              this.buttonLoading = false;
              this.msgSuccess("新增成功");
              this.open = false;
              this.getList();
            });
          }
        }
      });
    },
    /** åˆ é™¤æŒ‰é’®æ“ä½œ */
    handleDelete(row) {
      this.$confirm('是否确认删除${functionName}编号为"' + row.${pkColumn.javaField} + '"的数据项?', "警告", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning"
        }).then(() => {
          this.loading = true;
          return del${BusinessName}(row.${pkColumn.javaField});
        }).then(() => {
          this.loading = false;
          this.getList();
          this.msgSuccess("删除成功");
        }).catch(() => {});
    }
  }
};
</script>
<template>
  <div class="app-container">
    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
#foreach($column in $columns)
#if($column.query)
#set($dictType=$column.dictType)
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if($column.htmlType == "input")
      <el-form-item label="${comment}" prop="${column.javaField}">
        <el-input
          v-model="queryParams.${column.javaField}"
          placeholder="请输入${comment}"
          clearable
          size="small"
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType)
      <el-form-item label="${comment}" prop="${column.javaField}">
        <el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable size="small">
          <el-option
            v-for="dict in ${column.javaField}Options"
            :key="dict.dictValue"
            :label="dict.dictLabel"
            :value="dict.dictValue"
          />
        </el-select>
      </el-form-item>
#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $dictType)
      <el-form-item label="${comment}" prop="${column.javaField}">
        <el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable size="small">
          <el-option label="请选择字典生成" value="" />
        </el-select>
      </el-form-item>
#elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN")
      <el-form-item label="${comment}" prop="${column.javaField}">
        <el-date-picker clearable size="small"
          v-model="queryParams.${column.javaField}"
          type="date"
          value-format="yyyy-MM-dd"
          placeholder="选择${comment}">
        </el-date-picker>
      </el-form-item>
#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
      <el-form-item label="${comment}">
        <el-date-picker
          v-model="daterange${AttrName}"
          size="small"
          style="width: 240px"
          value-format="yyyy-MM-dd"
          type="daterange"
          range-separator="-"
          start-placeholder="开始日期"
          end-placeholder="结束日期"
        ></el-date-picker>
      </el-form-item>
#end
#end
#end
      <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="handleAdd"
          v-hasPermi="['${moduleName}:${businessName}:add']"
        >新增</el-button>
      </el-col>
      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
    </el-row>
    <el-table
      v-loading="loading"
      :data="${businessName}List"
      row-key="${treeCode}"
      default-expand-all
      :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
    >
#foreach($column in $columns)
#set($javaField=$column.javaField)
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if($column.pk)
#elseif($column.list && $column.htmlType == "datetime")
      <el-table-column label="${comment}" align="center" prop="${javaField}" width="180">
        <template slot-scope="scope">
          <span>{{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}</span>
        </template>
      </el-table-column>
#elseif($column.list && "" != $column.dictType)
      <el-table-column label="${comment}" align="center" prop="${javaField}" :formatter="${javaField}Format" />
#elseif($column.list && "" != $javaField)
#if(${foreach.index} == 1)
      <el-table-column label="${comment}" prop="${javaField}" />
#else
      <el-table-column label="${comment}" align="center" prop="${javaField}" />
#end
#end
#end
      <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="handleUpdate(scope.row)"
            v-hasPermi="['${moduleName}:${businessName}:edit']"
          >修改</el-button>
          <el-button
            size="mini"
            type="text"
            icon="el-icon-plus"
            @click="handleAdd(scope.row)"
            v-hasPermi="['${moduleName}:${businessName}:add']"
          >新增</el-button>
          <el-button
            size="mini"
            type="text"
            icon="el-icon-delete"
            @click="handleDelete(scope.row)"
            v-hasPermi="['${moduleName}:${businessName}:remove']"
          >删除</el-button>
        </template>
      </el-table-column>
    </el-table>
    <!-- æ·»åŠ æˆ–ä¿®æ”¹${functionName}对话框 -->
    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
#foreach($column in $columns)
#set($field=$column.javaField)
#if($column.insert && !$column.pk)
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#set($dictType=$column.dictType)
#if("" != $treeParentCode && $column.javaField == $treeParentCode)
        <el-form-item label="${comment}" prop="${treeParentCode}">
          <treeselect v-model="form.${treeParentCode}" :options="${businessName}Options" :normalizer="normalizer" placeholder="请选择${comment}" />
        </el-form-item>
#elseif($column.htmlType == "input")
        <el-form-item label="${comment}" prop="${field}">
          <el-input v-model="form.${field}" placeholder="请输入${comment}" />
        </el-form-item>
#elseif($column.htmlType == "imageUpload")
        <el-form-item label="${comment}">
          <imageUpload v-model="form.${field}"/>
        </el-form-item>
#elseif($column.htmlType == "fileUpload")
        <el-form-item label="${comment}">
          <fileUpload v-model="form.${field}"/>
        </el-form-item>
#elseif($column.htmlType == "editor")
        <el-form-item label="${comment}">
          <editor v-model="form.${field}" :min-height="192"/>
        </el-form-item>
#elseif($column.htmlType == "select" && "" != $dictType)
        <el-form-item label="${comment}" prop="${field}">
          <el-select v-model="form.${field}" placeholder="请选择${comment}">
            <el-option
              v-for="dict in ${field}Options"
              :key="dict.dictValue"
              :label="dict.dictLabel"
              #if($column.javaType == "Integer" || $column.javaType == "Long"):value="parseInt(dict.dictValue)"#else:value="dict.dictValue"#end
            ></el-option>
          </el-select>
        </el-form-item>
#elseif($column.htmlType == "select" && $dictType)
        <el-form-item label="${comment}" prop="${field}">
          <el-select v-model="form.${field}" placeholder="请选择${comment}">
            <el-option label="请选择字典生成" value="" />
          </el-select>
        </el-form-item>
#elseif($column.htmlType == "checkbox" && "" != $dictType)
        <el-form-item label="${comment}">
          <el-checkbox-group v-model="form.${field}">
            <el-checkbox
              v-for="dict in ${field}Options"
              :key="dict.dictValue"
              :label="dict.dictValue">
              {{dict.dictLabel}}
            </el-checkbox>
          </el-checkbox-group>
        </el-form-item>
#elseif($column.htmlType == "checkbox" && $dictType)
        <el-form-item label="${comment}">
          <el-checkbox-group v-model="form.${field}">
            <el-checkbox>请选择字典生成</el-checkbox>
          </el-checkbox-group>
        </el-form-item>
#elseif($column.htmlType == "radio" && "" != $dictType)
        <el-form-item label="${comment}">
          <el-radio-group v-model="form.${field}">
            <el-radio
              v-for="dict in ${field}Options"
              :key="dict.dictValue"
              #if($column.javaType == "Integer" || $column.javaType == "Long"):label="parseInt(dict.dictValue)"#else:label="dict.dictValue"#end
            >{{dict.dictLabel}}</el-radio>
          </el-radio-group>
        </el-form-item>
#elseif($column.htmlType == "radio" && $dictType)
        <el-form-item label="${comment}">
          <el-radio-group v-model="form.${field}">
            <el-radio label="1">请选择字典生成</el-radio>
          </el-radio-group>
        </el-form-item>
#elseif($column.htmlType == "datetime")
        <el-form-item label="${comment}" prop="${field}">
          <el-date-picker clearable size="small"
            v-model="form.${field}"
            type="datetime"
            value-format="yyyy-MM-dd HH:mm:ss"
            placeholder="选择${comment}">
          </el-date-picker>
        </el-form-item>
#elseif($column.htmlType == "textarea")
        <el-form-item label="${comment}" prop="${field}">
          <el-input v-model="form.${field}" type="textarea" placeholder="请输入内容" />
        </el-form-item>
#end
#end
#end
      </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 { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName}, export${BusinessName} } from "@/api/${moduleName}/${businessName}";
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
#foreach($column in $columns)
#if($column.insert && !$column.pk && $column.htmlType == "imageUpload")
import ImageUpload from '@/components/ImageUpload';
#break
#end
#end
#foreach($column in $columns)
#if($column.insert && !$column.pk && $column.htmlType == "fileUpload")
import FileUpload from '@/components/FileUpload';
#break
#end
#end
#foreach($column in $columns)
#if($column.insert && !$column.pk && $column.htmlType == "editor")
import Editor from '@/components/Editor';
#break
#end
#end
export default {
  name: "${BusinessName}",
  components: {
#foreach($column in $columns)
#if($column.insert && !$column.pk && $column.htmlType == "imageUpload")
    ImageUpload,
#break
#end
#end
#foreach($column in $columns)
#if($column.insert && !$column.pk && $column.htmlType == "fileUpload")
    FileUpload,
#break
#end
#end
#foreach($column in $columns)
#if($column.insert && !$column.pk && $column.htmlType == "editor")
    Editor,
#break
#end
#end
    Treeselect
  },
  data() {
    return {
      //按钮loading
      buttonLoading: false,
      // é®ç½©å±‚
      loading: true,
      // æ˜¾ç¤ºæœç´¢æ¡ä»¶
      showSearch: true,
      // ${functionName}表格数据
      ${businessName}List: [],
      // ${functionName}树选项
      ${businessName}Options: [],
      // å¼¹å‡ºå±‚标题
      title: "",
      // æ˜¯å¦æ˜¾ç¤ºå¼¹å‡ºå±‚
      open: false,
#foreach ($column in $columns)
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if(${column.dictType} != '')
      // $comment字典
      ${column.javaField}Options: [],
#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
      // $comment时间范围
      daterange${AttrName}: [],
#end
#end
      // æŸ¥è¯¢å‚æ•°
      queryParams: {
#foreach ($column in $columns)
#if($column.query)
        $column.javaField: null#if($velocityCount != $columns.size()),#end
#end
#end
      },
      // è¡¨å•参数
      form: {},
      // è¡¨å•校验
      rules: {
#foreach ($column in $columns)
#if($column.required)
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
        $column.javaField: [
          { required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select")"change"#else"blur"#end }
        ]#if($velocityCount != $columns.size()),#end
#end
#end
      }
    };
  },
  created() {
    this.getList();
#foreach ($column in $columns)
#if(${column.dictType} != '')
    this.getDicts("${column.dictType}").then(response => {
      this.${column.javaField}Options = response.data;
    });
#end
#end
  },
  methods: {
    /** æŸ¥è¯¢${functionName}列表 */
    getList() {
      this.loading = true;
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
      this.queryParams.params = {};
#break
#end
#end
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
      if (null != this.daterange${AttrName} && '' != this.daterange${AttrName}) {
        this.queryParams.params["begin${AttrName}"] = this.daterange${AttrName}[0];
        this.queryParams.params["end${AttrName}"] = this.daterange${AttrName}[1];
      }
#end
#end
      list${BusinessName}(this.queryParams).then(response => {
        this.${businessName}List = this.handleTree(response.data, "${treeCode}", "${treeParentCode}");
        this.loading = false;
      });
    },
    /** è½¬æ¢${functionName}数据结构 */
    normalizer(node) {
      if (node.children && !node.children.length) {
        delete node.children;
      }
      return {
        id: node.${treeCode},
        label: node.${treeName},
        children: node.children
      };
    },
    /** æŸ¥è¯¢${functionName}下拉树结构 */
    getTreeselect() {
      list${BusinessName}().then(response => {
        this.${businessName}Options = [];
        const data = { ${treeCode}: 0, ${treeName}: '顶级节点', children: [] };
        data.children = this.handleTree(response.data, "${treeCode}", "${treeParentCode}");
        this.${businessName}Options.push(data);
      });
    },
#foreach ($column in $columns)
#if(${column.dictType} != '')
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
    // $comment字典翻译
    ${column.javaField}Format(row, column) {
      return this.selectDictLabel#if($column.htmlType == "checkbox")s#end(this.${column.javaField}Options, row.${column.javaField});
    },
#end
#end
    // å–消按钮
    cancel() {
      this.open = false;
      this.reset();
    },
    // è¡¨å•重置
    reset() {
      this.form = {
#foreach ($column in $columns)
#if($column.htmlType == "radio")
        $column.javaField: #if($column.javaType == "Integer" || $column.javaType == "Long")0#else"0"#end#if($velocityCount != $columns.size()),#end
#elseif($column.htmlType == "checkbox")
        $column.javaField: []#if($velocityCount != $columns.size()),#end
#else
        $column.javaField: null#if($velocityCount != $columns.size()),#end
#end
#end
      };
      this.resetForm("form");
    },
    /** æœç´¢æŒ‰é’®æ“ä½œ */
    handleQuery() {
      this.getList();
    },
    /** é‡ç½®æŒ‰é’®æ“ä½œ */
    resetQuery() {
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
      this.daterange${AttrName} = [];
#end
#end
      this.resetForm("queryForm");
      this.handleQuery();
    },
    /** æ–°å¢žæŒ‰é’®æ“ä½œ */
    handleAdd(row) {
      this.reset();
      this.getTreeselect();
      if (row != null && row.${treeCode}) {
        this.form.${treeParentCode} = row.${treeCode};
      } else {
        this.form.${treeParentCode} = 0;
      }
      this.open = true;
      this.title = "添加${functionName}";
    },
    /** ä¿®æ”¹æŒ‰é’®æ“ä½œ */
    handleUpdate(row) {
      this.loading = true;
      this.reset();
      this.getTreeselect();
      if (row != null) {
        this.form.${treeParentCode} = row.${treeCode};
      }
      get${BusinessName}(row.${pkColumn.javaField}).then(response => {
        this.loading = false;
        this.form = response.data;
#foreach ($column in $columns)
#if($column.htmlType == "checkbox")
        this.form.$column.javaField = this.form.${column.javaField}.split(",");
#end
#end
        this.open = true;
        this.title = "修改${functionName}";
      });
    },
    /** æäº¤æŒ‰é’® */
    submitForm() {
      this.#[[$]]#refs["form"].validate(valid => {
        if (valid) {
          this.buttonLoading = true;
#foreach ($column in $columns)
#if($column.htmlType == "checkbox")
          this.form.$column.javaField = this.form.${column.javaField}.join(",");
#end
#end
          if (this.form.${pkColumn.javaField} != null) {
            update${BusinessName}(this.form).then(response => {
              this.buttonLoading = false;
              this.msgSuccess("修改成功");
              this.open = false;
              this.getList();
            });
          } else {
            add${BusinessName}(this.form).then(response => {
              this.buttonLoading = false;
              this.msgSuccess("新增成功");
              this.open = false;
              this.getList();
            });
          }
        }
      });
    },
    /** åˆ é™¤æŒ‰é’®æ“ä½œ */
    handleDelete(row) {
      this.$confirm('是否确认删除${functionName}编号为"' + row.${pkColumn.javaField} + '"的数据项?', "警告", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning"
        }).then(() => {
          this.loading = true;
          return del${BusinessName}(row.${pkColumn.javaField});
        }).then(() => {
          this.loading = false;
          this.getList();
          this.msgSuccess("删除成功");
        }).catch(() => {});
    }
  }
};
</script>
ruoyi-generator/src/main/resources/vm/vue/index.vue.vm
@@ -174,7 +174,6 @@
#foreach($column in $columns)
#set($field=$column.javaField)
#if($column.insert && !$column.pk)
#if(($column.usableColumn) || (!$column.superColumn))
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
@@ -266,7 +265,6 @@
#end
#end
#end
#end
#if($table.sub)
        <el-divider content-position="center">${subTable.functionName}信息</el-divider>
        <el-row :gutter="10" class="mb8">
@@ -311,19 +309,19 @@
<script>
import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName}, export${BusinessName} } from "@/api/${moduleName}/${businessName}";
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "imageUpload")
#if($column.insert && !$column.pk && $column.htmlType == "imageUpload")
import ImageUpload from '@/components/ImageUpload';
#break
#end
#end
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "fileUpload")
#if($column.insert && !$column.pk && $column.htmlType == "fileUpload")
import FileUpload from '@/components/FileUpload';
#break
#end
#end
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "editor")
#if($column.insert && !$column.pk && $column.htmlType == "editor")
import Editor from '@/components/Editor';
#break
#end
@@ -333,19 +331,19 @@
  name: "${BusinessName}",
  components: {
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "imageUpload")
#if($column.insert && !$column.pk && $column.htmlType == "imageUpload")
    ImageUpload,
#break
#end
#end
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "fileUpload")
#if($column.insert && !$column.pk && $column.htmlType == "fileUpload")
    FileUpload,
#break
#end
#end
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "editor")
#if($column.insert && !$column.pk && $column.htmlType == "editor")
    Editor,
#break
#end
ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java
@@ -1,134 +1,144 @@
package com.ruoyi.quartz.controller;
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.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.exception.job.TaskException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.quartz.domain.SysJob;
import com.ruoyi.quartz.service.ISysJobService;
import com.ruoyi.quartz.util.CronUtils;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
 * è°ƒåº¦ä»»åŠ¡ä¿¡æ¯æ“ä½œå¤„ç†
 *
 * @author ruoyi
 */
@RestController
@RequestMapping("/monitor/job")
public class SysJobController extends BaseController
{
    @Autowired
    private ISysJobService jobService;
    /**
     * æŸ¥è¯¢å®šæ—¶ä»»åŠ¡åˆ—è¡¨
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:list')")
    @GetMapping("/list")
    public TableDataInfo list(SysJob sysJob)
    {
        return jobService.selectPageJobList(sysJob);
    }
    /**
     * å¯¼å‡ºå®šæ—¶ä»»åŠ¡åˆ—è¡¨
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:export')")
    @Log(title = "定时任务", businessType = BusinessType.EXPORT)
    @GetMapping("/export")
    public AjaxResult export(SysJob sysJob)
    {
        List<SysJob> list = jobService.selectJobList(sysJob);
        ExcelUtil<SysJob> util = new ExcelUtil<SysJob>(SysJob.class);
        return util.exportExcel(list, "定时任务");
    }
    /**
     * èŽ·å–å®šæ—¶ä»»åŠ¡è¯¦ç»†ä¿¡æ¯
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:query')")
    @GetMapping(value = "/{jobId}")
    public AjaxResult getInfo(@PathVariable("jobId") Long jobId)
    {
        return AjaxResult.success(jobService.selectJobById(jobId));
    }
    /**
     * æ–°å¢žå®šæ—¶ä»»åŠ¡
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:add')")
    @Log(title = "定时任务", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@RequestBody SysJob sysJob) throws SchedulerException, TaskException
    {
        if (!CronUtils.isValid(sysJob.getCronExpression()))
        {
            return AjaxResult.error("cron表达式不正确");
        }
        sysJob.setCreateBy(SecurityUtils.getUsername());
        return toAjax(jobService.insertJob(sysJob));
    }
    /**
     * ä¿®æ”¹å®šæ—¶ä»»åŠ¡
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:edit')")
    @Log(title = "定时任务", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@RequestBody SysJob sysJob) throws SchedulerException, TaskException
    {
        if (!CronUtils.isValid(sysJob.getCronExpression()))
        {
            return AjaxResult.error("cron表达式不正确");
        }
        sysJob.setUpdateBy(SecurityUtils.getUsername());
        return toAjax(jobService.updateJob(sysJob));
    }
    /**
     * å®šæ—¶ä»»åŠ¡çŠ¶æ€ä¿®æ”¹
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')")
    @Log(title = "定时任务", businessType = BusinessType.UPDATE)
    @PutMapping("/changeStatus")
    public AjaxResult changeStatus(@RequestBody SysJob job) throws SchedulerException
    {
        SysJob newJob = jobService.selectJobById(job.getJobId());
        newJob.setStatus(job.getStatus());
        return toAjax(jobService.changeStatus(newJob));
    }
    /**
     * å®šæ—¶ä»»åŠ¡ç«‹å³æ‰§è¡Œä¸€æ¬¡
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')")
    @Log(title = "定时任务", businessType = BusinessType.UPDATE)
    @PutMapping("/run")
    public AjaxResult run(@RequestBody SysJob job) throws SchedulerException
    {
        jobService.run(job);
        return AjaxResult.success();
    }
    /**
     * åˆ é™¤å®šæ—¶ä»»åŠ¡
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:remove')")
    @Log(title = "定时任务", businessType = BusinessType.DELETE)
    @DeleteMapping("/{jobIds}")
    public AjaxResult remove(@PathVariable Long[] jobIds) throws SchedulerException, TaskException
    {
        jobService.deleteJobByIds(jobIds);
        return AjaxResult.success();
    }
}
package com.ruoyi.quartz.controller;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.Constants;
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.job.TaskException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.quartz.domain.SysJob;
import com.ruoyi.quartz.service.ISysJobService;
import com.ruoyi.quartz.util.CronUtils;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
 * è°ƒåº¦ä»»åŠ¡ä¿¡æ¯æ“ä½œå¤„ç†
 *
 * @author ruoyi
 */
@RestController
@RequestMapping("/monitor/job")
public class SysJobController extends BaseController
{
    @Autowired
    private ISysJobService jobService;
    /**
     * æŸ¥è¯¢å®šæ—¶ä»»åŠ¡åˆ—è¡¨
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:list')")
    @GetMapping("/list")
    public TableDataInfo list(SysJob sysJob)
    {
        return jobService.selectPageJobList(sysJob);
    }
    /**
     * å¯¼å‡ºå®šæ—¶ä»»åŠ¡åˆ—è¡¨
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:export')")
    @Log(title = "定时任务", businessType = BusinessType.EXPORT)
    @GetMapping("/export")
    public AjaxResult export(SysJob sysJob)
    {
        List<SysJob> list = jobService.selectJobList(sysJob);
        ExcelUtil<SysJob> util = new ExcelUtil<SysJob>(SysJob.class);
        return util.exportExcel(list, "定时任务");
    }
    /**
     * èŽ·å–å®šæ—¶ä»»åŠ¡è¯¦ç»†ä¿¡æ¯
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:query')")
    @GetMapping(value = "/{jobId}")
    public AjaxResult getInfo(@PathVariable("jobId") Long jobId)
    {
        return AjaxResult.success(jobService.selectJobById(jobId));
    }
    /**
     * æ–°å¢žå®šæ—¶ä»»åŠ¡
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:add')")
    @Log(title = "定时任务", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@RequestBody SysJob sysJob) throws SchedulerException, TaskException
    {
        if (!CronUtils.isValid(sysJob.getCronExpression()))
        {
            return AjaxResult.error("新增任务'" + sysJob.getJobName() + "'失败,Cron表达式不正确");
        }
        else if (StrUtil.containsIgnoreCase(sysJob.getInvokeTarget(), Constants.LOOKUP_RMI))
        {
            return AjaxResult.error("新增任务'" + sysJob.getJobName() + "'失败,目标字符串不允许'rmi://'调用");
        }
        sysJob.setCreateBy(SecurityUtils.getUsername());
        return toAjax(jobService.insertJob(sysJob));
    }
    /**
     * ä¿®æ”¹å®šæ—¶ä»»åŠ¡
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:edit')")
    @Log(title = "定时任务", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@RequestBody SysJob sysJob) throws SchedulerException, TaskException
    {
        if (!CronUtils.isValid(sysJob.getCronExpression()))
        {
            return AjaxResult.error("修改任务'" + sysJob.getJobName() + "'失败,Cron表达式不正确");
        }
        else if (StrUtil.containsIgnoreCase(sysJob.getInvokeTarget(), Constants.LOOKUP_RMI))
        {
            return AjaxResult.error("修改任务'" + sysJob.getJobName() + "'失败,目标字符串不允许'rmi://'调用");
        }
        sysJob.setUpdateBy(SecurityUtils.getUsername());
        return toAjax(jobService.updateJob(sysJob));
    }
    /**
     * å®šæ—¶ä»»åŠ¡çŠ¶æ€ä¿®æ”¹
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')")
    @Log(title = "定时任务", businessType = BusinessType.UPDATE)
    @PutMapping("/changeStatus")
    public AjaxResult changeStatus(@RequestBody SysJob job) throws SchedulerException
    {
        SysJob newJob = jobService.selectJobById(job.getJobId());
        newJob.setStatus(job.getStatus());
        return toAjax(jobService.changeStatus(newJob));
    }
    /**
     * å®šæ—¶ä»»åŠ¡ç«‹å³æ‰§è¡Œä¸€æ¬¡
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')")
    @Log(title = "定时任务", businessType = BusinessType.UPDATE)
    @PutMapping("/run")
    public AjaxResult run(@RequestBody SysJob job) throws SchedulerException
    {
        jobService.run(job);
        return AjaxResult.success();
    }
    /**
     * åˆ é™¤å®šæ—¶ä»»åŠ¡
     */
    @PreAuthorize("@ss.hasPermi('monitor:job:remove')")
    @Log(title = "定时任务", businessType = BusinessType.DELETE)
    @DeleteMapping("/{jobIds}")
    public AjaxResult remove(@PathVariable Long[] jobIds) throws SchedulerException, TaskException
    {
        jobService.deleteJobByIds(jobIds);
        return AjaxResult.success();
    }
}
ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobLogMapper.java
@@ -1,13 +1,13 @@
package com.ruoyi.quartz.mapper;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.quartz.domain.SysJobLog;
/**
 * è°ƒåº¦ä»»åŠ¡æ—¥å¿—ä¿¡æ¯ æ•°æ®å±‚
 *
 * @author ruoyi
 */
public interface SysJobLogMapper extends BaseMapperPlus<SysJobLog> {
}
package com.ruoyi.quartz.mapper;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.quartz.domain.SysJobLog;
/**
 * è°ƒåº¦ä»»åŠ¡æ—¥å¿—ä¿¡æ¯ æ•°æ®å±‚
 *
 * @author ruoyi
 */
public interface SysJobLogMapper extends BaseMapperPlus<SysJobLog> {
}
ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobMapper.java
@@ -1,13 +1,13 @@
package com.ruoyi.quartz.mapper;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.quartz.domain.SysJob;
/**
 * è°ƒåº¦ä»»åŠ¡ä¿¡æ¯ æ•°æ®å±‚
 *
 * @author ruoyi
 */
public interface SysJobMapper extends BaseMapperPlus<SysJob> {
}
package com.ruoyi.quartz.mapper;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.quartz.domain.SysJob;
/**
 * è°ƒåº¦ä»»åŠ¡ä¿¡æ¯ æ•°æ®å±‚
 *
 * @author ruoyi
 */
public interface SysJobMapper extends BaseMapperPlus<SysJob> {
}
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobLogService.java
@@ -1,62 +1,62 @@
package com.ruoyi.quartz.service;
import com.ruoyi.common.core.page.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.quartz.domain.SysJobLog;
import java.util.List;
/**
 * å®šæ—¶ä»»åŠ¡è°ƒåº¦æ—¥å¿—ä¿¡æ¯ä¿¡æ¯ æœåС层
 *
 * @author ruoyi
 */
public interface ISysJobLogService extends IServicePlus<SysJobLog> {
    TableDataInfo<SysJobLog> selectPageJobLogList(SysJobLog jobLog);
    /**
     * èŽ·å–quartz调度器日志的计划任务
     *
     * @param jobLog è°ƒåº¦æ—¥å¿—信息
     * @return è°ƒåº¦ä»»åŠ¡æ—¥å¿—é›†åˆ
     */
    public List<SysJobLog> selectJobLogList(SysJobLog jobLog);
    /**
     * é€šè¿‡è°ƒåº¦ä»»åŠ¡æ—¥å¿—ID查询调度信息
     *
     * @param jobLogId è°ƒåº¦ä»»åŠ¡æ—¥å¿—ID
     * @return è°ƒåº¦ä»»åŠ¡æ—¥å¿—å¯¹è±¡ä¿¡æ¯
     */
    public SysJobLog selectJobLogById(Long jobLogId);
    /**
     * æ–°å¢žä»»åŠ¡æ—¥å¿—
     *
     * @param jobLog è°ƒåº¦æ—¥å¿—信息
     */
    public void addJobLog(SysJobLog jobLog);
    /**
     * æ‰¹é‡åˆ é™¤è°ƒåº¦æ—¥å¿—信息
     *
     * @param logIds éœ€è¦åˆ é™¤çš„æ—¥å¿—ID
     * @return ç»“æžœ
     */
    public int deleteJobLogByIds(Long[] logIds);
    /**
     * åˆ é™¤ä»»åŠ¡æ—¥å¿—
     *
     * @param jobId è°ƒåº¦æ—¥å¿—ID
     * @return ç»“æžœ
     */
    public int deleteJobLogById(Long jobId);
    /**
     * æ¸…空任务日志
     */
    public void cleanJobLog();
}
package com.ruoyi.quartz.service;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.quartz.domain.SysJobLog;
import java.util.List;
/**
 * å®šæ—¶ä»»åŠ¡è°ƒåº¦æ—¥å¿—ä¿¡æ¯ä¿¡æ¯ æœåС层
 *
 * @author ruoyi
 */
public interface ISysJobLogService extends IServicePlus<SysJobLog> {
    TableDataInfo<SysJobLog> selectPageJobLogList(SysJobLog jobLog);
    /**
     * èŽ·å–quartz调度器日志的计划任务
     *
     * @param jobLog è°ƒåº¦æ—¥å¿—信息
     * @return è°ƒåº¦ä»»åŠ¡æ—¥å¿—é›†åˆ
     */
    public List<SysJobLog> selectJobLogList(SysJobLog jobLog);
    /**
     * é€šè¿‡è°ƒåº¦ä»»åŠ¡æ—¥å¿—ID查询调度信息
     *
     * @param jobLogId è°ƒåº¦ä»»åŠ¡æ—¥å¿—ID
     * @return è°ƒåº¦ä»»åŠ¡æ—¥å¿—å¯¹è±¡ä¿¡æ¯
     */
    public SysJobLog selectJobLogById(Long jobLogId);
    /**
     * æ–°å¢žä»»åŠ¡æ—¥å¿—
     *
     * @param jobLog è°ƒåº¦æ—¥å¿—信息
     */
    public void addJobLog(SysJobLog jobLog);
    /**
     * æ‰¹é‡åˆ é™¤è°ƒåº¦æ—¥å¿—信息
     *
     * @param logIds éœ€è¦åˆ é™¤çš„æ—¥å¿—ID
     * @return ç»“æžœ
     */
    public int deleteJobLogByIds(Long[] logIds);
    /**
     * åˆ é™¤ä»»åŠ¡æ—¥å¿—
     *
     * @param jobId è°ƒåº¦æ—¥å¿—ID
     * @return ç»“æžœ
     */
    public int deleteJobLogById(Long jobId);
    /**
     * æ¸…空任务日志
     */
    public void cleanJobLog();
}
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobService.java
@@ -1,106 +1,106 @@
package com.ruoyi.quartz.service;
import com.ruoyi.common.core.page.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.job.TaskException;
import com.ruoyi.quartz.domain.SysJob;
import org.quartz.SchedulerException;
import java.util.List;
/**
 * å®šæ—¶ä»»åŠ¡è°ƒåº¦ä¿¡æ¯ä¿¡æ¯ æœåС层
 *
 * @author ruoyi
 */
public interface ISysJobService extends IServicePlus<SysJob> {
    /**
     * èŽ·å–quartz调度器的计划任务
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     * @return è°ƒåº¦ä»»åŠ¡é›†åˆ
     */
    public TableDataInfo<SysJob> selectPageJobList(SysJob job);
    public List<SysJob> selectJobList(SysJob job);
    /**
     * é€šè¿‡è°ƒåº¦ä»»åŠ¡ID查询调度信息
     *
     * @param jobId è°ƒåº¦ä»»åŠ¡ID
     * @return è°ƒåº¦ä»»åŠ¡å¯¹è±¡ä¿¡æ¯
     */
    public SysJob selectJobById(Long jobId);
    /**
     * æš‚停任务
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     * @return ç»“æžœ
     */
    public int pauseJob(SysJob job) throws SchedulerException;
    /**
     * æ¢å¤ä»»åŠ¡
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     * @return ç»“æžœ
     */
    public int resumeJob(SysJob job) throws SchedulerException;
    /**
     * åˆ é™¤ä»»åŠ¡åŽï¼Œæ‰€å¯¹åº”çš„trigger也将被删除
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     * @return ç»“æžœ
     */
    public int deleteJob(SysJob job) throws SchedulerException;
    /**
     * æ‰¹é‡åˆ é™¤è°ƒåº¦ä¿¡æ¯
     *
     * @param jobIds éœ€è¦åˆ é™¤çš„任务ID
     * @return ç»“æžœ
     */
    public void deleteJobByIds(Long[] jobIds) throws SchedulerException;
    /**
     * ä»»åŠ¡è°ƒåº¦çŠ¶æ€ä¿®æ”¹
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     * @return ç»“æžœ
     */
    public int changeStatus(SysJob job) throws SchedulerException;
    /**
     * ç«‹å³è¿è¡Œä»»åŠ¡
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     * @return ç»“æžœ
     */
    public void run(SysJob job) throws SchedulerException;
    /**
     * æ–°å¢žä»»åŠ¡
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     * @return ç»“æžœ
     */
    public int insertJob(SysJob job) throws SchedulerException, TaskException;
    /**
     * æ›´æ–°ä»»åŠ¡
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     * @return ç»“æžœ
     */
    public int updateJob(SysJob job) throws SchedulerException, TaskException;
    /**
     * æ ¡éªŒcron表达式是否有效
     *
     * @param cronExpression è¡¨è¾¾å¼
     * @return ç»“æžœ
     */
    public boolean checkCronExpressionIsValid(String cronExpression);
}
package com.ruoyi.quartz.service;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.job.TaskException;
import com.ruoyi.quartz.domain.SysJob;
import org.quartz.SchedulerException;
import java.util.List;
/**
 * å®šæ—¶ä»»åŠ¡è°ƒåº¦ä¿¡æ¯ä¿¡æ¯ æœåС层
 *
 * @author ruoyi
 */
public interface ISysJobService extends IServicePlus<SysJob> {
    /**
     * èŽ·å–quartz调度器的计划任务
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     * @return è°ƒåº¦ä»»åŠ¡é›†åˆ
     */
    public TableDataInfo<SysJob> selectPageJobList(SysJob job);
    public List<SysJob> selectJobList(SysJob job);
    /**
     * é€šè¿‡è°ƒåº¦ä»»åŠ¡ID查询调度信息
     *
     * @param jobId è°ƒåº¦ä»»åŠ¡ID
     * @return è°ƒåº¦ä»»åŠ¡å¯¹è±¡ä¿¡æ¯
     */
    public SysJob selectJobById(Long jobId);
    /**
     * æš‚停任务
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     * @return ç»“æžœ
     */
    public int pauseJob(SysJob job) throws SchedulerException;
    /**
     * æ¢å¤ä»»åŠ¡
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     * @return ç»“æžœ
     */
    public int resumeJob(SysJob job) throws SchedulerException;
    /**
     * åˆ é™¤ä»»åŠ¡åŽï¼Œæ‰€å¯¹åº”çš„trigger也将被删除
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     * @return ç»“æžœ
     */
    public int deleteJob(SysJob job) throws SchedulerException;
    /**
     * æ‰¹é‡åˆ é™¤è°ƒåº¦ä¿¡æ¯
     *
     * @param jobIds éœ€è¦åˆ é™¤çš„任务ID
     * @return ç»“æžœ
     */
    public void deleteJobByIds(Long[] jobIds) throws SchedulerException;
    /**
     * ä»»åŠ¡è°ƒåº¦çŠ¶æ€ä¿®æ”¹
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     * @return ç»“æžœ
     */
    public int changeStatus(SysJob job) throws SchedulerException;
    /**
     * ç«‹å³è¿è¡Œä»»åŠ¡
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     * @return ç»“æžœ
     */
    public void run(SysJob job) throws SchedulerException;
    /**
     * æ–°å¢žä»»åŠ¡
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     * @return ç»“æžœ
     */
    public int insertJob(SysJob job) throws SchedulerException, TaskException;
    /**
     * æ›´æ–°ä»»åŠ¡
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     * @return ç»“æžœ
     */
    public int updateJob(SysJob job) throws SchedulerException, TaskException;
    /**
     * æ ¡éªŒcron表达式是否有效
     *
     * @param cronExpression è¡¨è¾¾å¼
     * @return ç»“æžœ
     */
    public boolean checkCronExpressionIsValid(String cronExpression);
}
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobLogServiceImpl.java
@@ -1,114 +1,114 @@
package com.ruoyi.quartz.service.impl;
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.core.page.TableDataInfo;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.quartz.domain.SysJobLog;
import com.ruoyi.quartz.mapper.SysJobLogMapper;
import com.ruoyi.quartz.service.ISysJobLogService;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
 * å®šæ—¶ä»»åŠ¡è°ƒåº¦æ—¥å¿—ä¿¡æ¯ æœåС层
 *
 * @author ruoyi
 */
@Service
public class SysJobLogServiceImpl extends ServiceImpl<SysJobLogMapper, SysJobLog> implements ISysJobLogService {
    @Override
    public TableDataInfo<SysJobLog> selectPageJobLogList(SysJobLog jobLog) {
        Map<String, Object> params = jobLog.getParams();
        LambdaQueryWrapper<SysJobLog> lqw = new LambdaQueryWrapper<SysJobLog>()
                .like(StrUtil.isNotBlank(jobLog.getJobName()), SysJobLog::getJobName, jobLog.getJobName())
                .eq(StrUtil.isNotBlank(jobLog.getJobGroup()), SysJobLog::getJobGroup, jobLog.getJobGroup())
                .eq(StrUtil.isNotBlank(jobLog.getStatus()), SysJobLog::getStatus, jobLog.getStatus())
                .like(StrUtil.isNotBlank(jobLog.getInvokeTarget()), SysJobLog::getInvokeTarget, jobLog.getInvokeTarget())
                .apply(Validator.isNotEmpty(params.get("beginTime")),
                        "date_format(create_time,'%y%m%d') >= date_format({0},'%y%m%d')",
                        params.get("beginTime"))
                .apply(Validator.isNotEmpty(params.get("endTime")),
                        "date_format(create_time,'%y%m%d') <= date_format({0},'%y%m%d')",
                        params.get("endTime"));
        return PageUtils.buildDataInfo(page(PageUtils.buildPage(), lqw));
    }
    /**
     * èŽ·å–quartz调度器日志的计划任务
     *
     * @param jobLog è°ƒåº¦æ—¥å¿—信息
     * @return è°ƒåº¦ä»»åŠ¡æ—¥å¿—é›†åˆ
     */
    @Override
    public List<SysJobLog> selectJobLogList(SysJobLog jobLog) {
        Map<String, Object> params = jobLog.getParams();
        return list(new LambdaQueryWrapper<SysJobLog>()
                .like(StrUtil.isNotBlank(jobLog.getJobName()), SysJobLog::getJobName, jobLog.getJobName())
                .eq(StrUtil.isNotBlank(jobLog.getJobGroup()), SysJobLog::getJobGroup, jobLog.getJobGroup())
                .eq(StrUtil.isNotBlank(jobLog.getStatus()), SysJobLog::getStatus, jobLog.getStatus())
                .like(StrUtil.isNotBlank(jobLog.getInvokeTarget()), SysJobLog::getInvokeTarget, jobLog.getInvokeTarget())
                .apply(Validator.isNotEmpty(params.get("beginTime")),
                        "date_format(create_time,'%y%m%d') >= date_format({0},'%y%m%d')",
                        params.get("beginTime"))
                .apply(Validator.isNotEmpty(params.get("endTime")),
                        "date_format(create_time,'%y%m%d') <= date_format({0},'%y%m%d')",
                        params.get("endTime")));
    }
    /**
     * é€šè¿‡è°ƒåº¦ä»»åŠ¡æ—¥å¿—ID查询调度信息
     *
     * @param jobLogId è°ƒåº¦ä»»åŠ¡æ—¥å¿—ID
     * @return è°ƒåº¦ä»»åŠ¡æ—¥å¿—å¯¹è±¡ä¿¡æ¯
     */
    @Override
    public SysJobLog selectJobLogById(Long jobLogId) {
        return getById(jobLogId);
    }
    /**
     * æ–°å¢žä»»åŠ¡æ—¥å¿—
     *
     * @param jobLog è°ƒåº¦æ—¥å¿—信息
     */
    @Override
    public void addJobLog(SysJobLog jobLog) {
        baseMapper.insert(jobLog);
    }
    /**
     * æ‰¹é‡åˆ é™¤è°ƒåº¦æ—¥å¿—信息
     *
     * @param logIds éœ€è¦åˆ é™¤çš„æ•°æ®ID
     * @return ç»“æžœ
     */
    @Override
    public int deleteJobLogByIds(Long[] logIds) {
        return baseMapper.deleteBatchIds(Arrays.asList(logIds));
    }
    /**
     * åˆ é™¤ä»»åŠ¡æ—¥å¿—
     *
     * @param jobId è°ƒåº¦æ—¥å¿—ID
     */
    @Override
    public int deleteJobLogById(Long jobId) {
        return baseMapper.deleteById(jobId);
    }
    /**
     * æ¸…空任务日志
     */
    @Override
    public void cleanJobLog() {
        remove(new LambdaQueryWrapper<>());
    }
}
package com.ruoyi.quartz.service.impl;
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.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.quartz.domain.SysJobLog;
import com.ruoyi.quartz.mapper.SysJobLogMapper;
import com.ruoyi.quartz.service.ISysJobLogService;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
 * å®šæ—¶ä»»åŠ¡è°ƒåº¦æ—¥å¿—ä¿¡æ¯ æœåС层
 *
 * @author ruoyi
 */
@Service
public class SysJobLogServiceImpl extends ServicePlusImpl<SysJobLogMapper, SysJobLog> implements ISysJobLogService {
    @Override
    public TableDataInfo<SysJobLog> selectPageJobLogList(SysJobLog jobLog) {
        Map<String, Object> params = jobLog.getParams();
        LambdaQueryWrapper<SysJobLog> lqw = new LambdaQueryWrapper<SysJobLog>()
                .like(StrUtil.isNotBlank(jobLog.getJobName()), SysJobLog::getJobName, jobLog.getJobName())
                .eq(StrUtil.isNotBlank(jobLog.getJobGroup()), SysJobLog::getJobGroup, jobLog.getJobGroup())
                .eq(StrUtil.isNotBlank(jobLog.getStatus()), SysJobLog::getStatus, jobLog.getStatus())
                .like(StrUtil.isNotBlank(jobLog.getInvokeTarget()), SysJobLog::getInvokeTarget, jobLog.getInvokeTarget())
                .apply(Validator.isNotEmpty(params.get("beginTime")),
                        "date_format(create_time,'%y%m%d') >= date_format({0},'%y%m%d')",
                        params.get("beginTime"))
                .apply(Validator.isNotEmpty(params.get("endTime")),
                        "date_format(create_time,'%y%m%d') <= date_format({0},'%y%m%d')",
                        params.get("endTime"));
        return PageUtils.buildDataInfo(page(PageUtils.buildPage(), lqw));
    }
    /**
     * èŽ·å–quartz调度器日志的计划任务
     *
     * @param jobLog è°ƒåº¦æ—¥å¿—信息
     * @return è°ƒåº¦ä»»åŠ¡æ—¥å¿—é›†åˆ
     */
    @Override
    public List<SysJobLog> selectJobLogList(SysJobLog jobLog) {
        Map<String, Object> params = jobLog.getParams();
        return list(new LambdaQueryWrapper<SysJobLog>()
                .like(StrUtil.isNotBlank(jobLog.getJobName()), SysJobLog::getJobName, jobLog.getJobName())
                .eq(StrUtil.isNotBlank(jobLog.getJobGroup()), SysJobLog::getJobGroup, jobLog.getJobGroup())
                .eq(StrUtil.isNotBlank(jobLog.getStatus()), SysJobLog::getStatus, jobLog.getStatus())
                .like(StrUtil.isNotBlank(jobLog.getInvokeTarget()), SysJobLog::getInvokeTarget, jobLog.getInvokeTarget())
                .apply(Validator.isNotEmpty(params.get("beginTime")),
                        "date_format(create_time,'%y%m%d') >= date_format({0},'%y%m%d')",
                        params.get("beginTime"))
                .apply(Validator.isNotEmpty(params.get("endTime")),
                        "date_format(create_time,'%y%m%d') <= date_format({0},'%y%m%d')",
                        params.get("endTime")));
    }
    /**
     * é€šè¿‡è°ƒåº¦ä»»åŠ¡æ—¥å¿—ID查询调度信息
     *
     * @param jobLogId è°ƒåº¦ä»»åŠ¡æ—¥å¿—ID
     * @return è°ƒåº¦ä»»åŠ¡æ—¥å¿—å¯¹è±¡ä¿¡æ¯
     */
    @Override
    public SysJobLog selectJobLogById(Long jobLogId) {
        return getById(jobLogId);
    }
    /**
     * æ–°å¢žä»»åŠ¡æ—¥å¿—
     *
     * @param jobLog è°ƒåº¦æ—¥å¿—信息
     */
    @Override
    public void addJobLog(SysJobLog jobLog) {
        baseMapper.insert(jobLog);
    }
    /**
     * æ‰¹é‡åˆ é™¤è°ƒåº¦æ—¥å¿—信息
     *
     * @param logIds éœ€è¦åˆ é™¤çš„æ•°æ®ID
     * @return ç»“æžœ
     */
    @Override
    public int deleteJobLogByIds(Long[] logIds) {
        return baseMapper.deleteBatchIds(Arrays.asList(logIds));
    }
    /**
     * åˆ é™¤ä»»åŠ¡æ—¥å¿—
     *
     * @param jobId è°ƒåº¦æ—¥å¿—ID
     */
    @Override
    public int deleteJobLogById(Long jobId) {
        return baseMapper.deleteById(jobId);
    }
    /**
     * æ¸…空任务日志
     */
    @Override
    public void cleanJobLog() {
        remove(new LambdaQueryWrapper<>());
    }
}
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java
@@ -1,246 +1,246 @@
package com.ruoyi.quartz.service.impl;
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.page.TableDataInfo;
import com.ruoyi.common.exception.job.TaskException;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.quartz.domain.SysJob;
import com.ruoyi.quartz.mapper.SysJobMapper;
import com.ruoyi.quartz.service.ISysJobService;
import com.ruoyi.quartz.util.CronUtils;
import com.ruoyi.quartz.util.ScheduleUtils;
import org.quartz.JobDataMap;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.util.List;
/**
 * å®šæ—¶ä»»åŠ¡è°ƒåº¦ä¿¡æ¯ æœåС层
 *
 * @author ruoyi
 */
@Service
public class SysJobServiceImpl extends ServiceImpl<SysJobMapper, SysJob> implements ISysJobService {
    @Autowired
    private Scheduler scheduler;
    /**
     * é¡¹ç›®å¯åŠ¨æ—¶ï¼Œåˆå§‹åŒ–å®šæ—¶å™¨ ä¸»è¦æ˜¯é˜²æ­¢æ‰‹åŠ¨ä¿®æ”¹æ•°æ®åº“å¯¼è‡´æœªåŒæ­¥åˆ°å®šæ—¶ä»»åŠ¡å¤„ç†ï¼ˆæ³¨ï¼šä¸èƒ½æ‰‹åŠ¨ä¿®æ”¹æ•°æ®åº“ID和任务组名,否则会导致脏数据)
     */
    @PostConstruct
    public void init() throws SchedulerException, TaskException {
        scheduler.clear();
        List<SysJob> jobList = list();
        for (SysJob job : jobList) {
            ScheduleUtils.createScheduleJob(scheduler, job);
        }
    }
    @Override
    public TableDataInfo<SysJob> selectPageJobList(SysJob job) {
        LambdaQueryWrapper<SysJob> lqw = new LambdaQueryWrapper<SysJob>()
                .like(StrUtil.isNotBlank(job.getJobName()), SysJob::getJobName, job.getJobName())
                .eq(StrUtil.isNotBlank(job.getJobGroup()), SysJob::getJobGroup, job.getJobGroup())
                .eq(StrUtil.isNotBlank(job.getStatus()), SysJob::getStatus, job.getStatus())
                .like(StrUtil.isNotBlank(job.getInvokeTarget()), SysJob::getInvokeTarget, job.getInvokeTarget());
        return PageUtils.buildDataInfo(page(PageUtils.buildPage(), lqw));
    }
    /**
     * èŽ·å–quartz调度器的计划任务列表
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     * @return
     */
    @Override
    public List<SysJob> selectJobList(SysJob job) {
        return list(new LambdaQueryWrapper<SysJob>()
                .like(StrUtil.isNotBlank(job.getJobName()), SysJob::getJobName, job.getJobName())
                .eq(StrUtil.isNotBlank(job.getJobGroup()), SysJob::getJobGroup, job.getJobGroup())
                .eq(StrUtil.isNotBlank(job.getStatus()), SysJob::getStatus, job.getStatus())
                .like(StrUtil.isNotBlank(job.getInvokeTarget()), SysJob::getInvokeTarget, job.getInvokeTarget()));
    }
    /**
     * é€šè¿‡è°ƒåº¦ä»»åŠ¡ID查询调度信息
     *
     * @param jobId è°ƒåº¦ä»»åŠ¡ID
     * @return è°ƒåº¦ä»»åŠ¡å¯¹è±¡ä¿¡æ¯
     */
    @Override
    public SysJob selectJobById(Long jobId) {
        return getById(jobId);
    }
    /**
     * æš‚停任务
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     */
    @Override
    @Transactional
    public int pauseJob(SysJob job) throws SchedulerException {
        Long jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
        int rows = baseMapper.updateById(job);
        if (rows > 0) {
            scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
        return rows;
    }
    /**
     * æ¢å¤ä»»åŠ¡
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     */
    @Override
    @Transactional
    public int resumeJob(SysJob job) throws SchedulerException {
        Long jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        job.setStatus(ScheduleConstants.Status.NORMAL.getValue());
        int rows = baseMapper.updateById(job);
        if (rows > 0) {
            scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
        return rows;
    }
    /**
     * åˆ é™¤ä»»åŠ¡åŽï¼Œæ‰€å¯¹åº”çš„trigger也将被删除
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     */
    @Override
    @Transactional
    public int deleteJob(SysJob job) throws SchedulerException {
        Long jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        int rows = baseMapper.deleteById(jobId);
        if (rows > 0) {
            scheduler.deleteJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
        return rows;
    }
    /**
     * æ‰¹é‡åˆ é™¤è°ƒåº¦ä¿¡æ¯
     *
     * @param jobIds éœ€è¦åˆ é™¤çš„任务ID
     * @return ç»“æžœ
     */
    @Override
    @Transactional
    public void deleteJobByIds(Long[] jobIds) throws SchedulerException {
        for (Long jobId : jobIds) {
            SysJob job = getById(jobId);
            deleteJob(job);
        }
    }
    /**
     * ä»»åŠ¡è°ƒåº¦çŠ¶æ€ä¿®æ”¹
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     */
    @Override
    @Transactional
    public int changeStatus(SysJob job) throws SchedulerException {
        int rows = 0;
        String status = job.getStatus();
        if (ScheduleConstants.Status.NORMAL.getValue().equals(status)) {
            rows = resumeJob(job);
        } else if (ScheduleConstants.Status.PAUSE.getValue().equals(status)) {
            rows = pauseJob(job);
        }
        return rows;
    }
    /**
     * ç«‹å³è¿è¡Œä»»åŠ¡
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     */
    @Override
    @Transactional
    public void run(SysJob job) throws SchedulerException {
        Long jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        SysJob properties = selectJobById(job.getJobId());
        // å‚æ•°
        JobDataMap dataMap = new JobDataMap();
        dataMap.put(ScheduleConstants.TASK_PROPERTIES, properties);
        scheduler.triggerJob(ScheduleUtils.getJobKey(jobId, jobGroup), dataMap);
    }
    /**
     * æ–°å¢žä»»åŠ¡
     *
     * @param job è°ƒåº¦ä¿¡æ¯ è°ƒåº¦ä¿¡æ¯
     */
    @Override
    @Transactional
    public int insertJob(SysJob job) throws SchedulerException, TaskException {
        job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
        int rows = baseMapper.insert(job);
        if (rows > 0) {
            ScheduleUtils.createScheduleJob(scheduler, job);
        }
        return rows;
    }
    /**
     * æ›´æ–°ä»»åŠ¡çš„æ—¶é—´è¡¨è¾¾å¼
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     */
    @Override
    @Transactional
    public int updateJob(SysJob job) throws SchedulerException, TaskException {
        SysJob properties = selectJobById(job.getJobId());
        int rows = baseMapper.updateById(job);
        if (rows > 0) {
            updateSchedulerJob(job, properties.getJobGroup());
        }
        return rows;
    }
    /**
     * æ›´æ–°ä»»åŠ¡
     *
     * @param job      ä»»åŠ¡å¯¹è±¡
     * @param jobGroup ä»»åŠ¡ç»„å
     */
    public void updateSchedulerJob(SysJob job, String jobGroup) throws SchedulerException, TaskException {
        Long jobId = job.getJobId();
        // åˆ¤æ–­æ˜¯å¦å­˜åœ¨
        JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
        if (scheduler.checkExists(jobKey)) {
            // é˜²æ­¢åˆ›å»ºæ—¶å­˜åœ¨æ•°æ®é—®é¢˜ å…ˆç§»é™¤ï¼Œç„¶åŽåœ¨æ‰§è¡Œåˆ›å»ºæ“ä½œ
            scheduler.deleteJob(jobKey);
        }
        ScheduleUtils.createScheduleJob(scheduler, job);
    }
    /**
     * æ ¡éªŒcron表达式是否有效
     *
     * @param cronExpression è¡¨è¾¾å¼
     * @return ç»“æžœ
     */
    @Override
    public boolean checkCronExpressionIsValid(String cronExpression) {
        return CronUtils.isValid(cronExpression);
    }
}
package com.ruoyi.quartz.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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;
import com.ruoyi.quartz.domain.SysJob;
import com.ruoyi.quartz.mapper.SysJobMapper;
import com.ruoyi.quartz.service.ISysJobService;
import com.ruoyi.quartz.util.CronUtils;
import com.ruoyi.quartz.util.ScheduleUtils;
import org.quartz.JobDataMap;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.util.List;
/**
 * å®šæ—¶ä»»åŠ¡è°ƒåº¦ä¿¡æ¯ æœåС层
 *
 * @author ruoyi
 */
@Service
public class SysJobServiceImpl extends ServicePlusImpl<SysJobMapper, SysJob> implements ISysJobService {
    @Autowired
    private Scheduler scheduler;
    /**
     * é¡¹ç›®å¯åŠ¨æ—¶ï¼Œåˆå§‹åŒ–å®šæ—¶å™¨ ä¸»è¦æ˜¯é˜²æ­¢æ‰‹åŠ¨ä¿®æ”¹æ•°æ®åº“å¯¼è‡´æœªåŒæ­¥åˆ°å®šæ—¶ä»»åŠ¡å¤„ç†ï¼ˆæ³¨ï¼šä¸èƒ½æ‰‹åŠ¨ä¿®æ”¹æ•°æ®åº“ID和任务组名,否则会导致脏数据)
     */
    @PostConstruct
    public void init() throws SchedulerException, TaskException {
        scheduler.clear();
        List<SysJob> jobList = list();
        for (SysJob job : jobList) {
            ScheduleUtils.createScheduleJob(scheduler, job);
        }
    }
    @Override
    public TableDataInfo<SysJob> selectPageJobList(SysJob job) {
        LambdaQueryWrapper<SysJob> lqw = new LambdaQueryWrapper<SysJob>()
                .like(StrUtil.isNotBlank(job.getJobName()), SysJob::getJobName, job.getJobName())
                .eq(StrUtil.isNotBlank(job.getJobGroup()), SysJob::getJobGroup, job.getJobGroup())
                .eq(StrUtil.isNotBlank(job.getStatus()), SysJob::getStatus, job.getStatus())
                .like(StrUtil.isNotBlank(job.getInvokeTarget()), SysJob::getInvokeTarget, job.getInvokeTarget());
        return PageUtils.buildDataInfo(page(PageUtils.buildPage(), lqw));
    }
    /**
     * èŽ·å–quartz调度器的计划任务列表
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     * @return
     */
    @Override
    public List<SysJob> selectJobList(SysJob job) {
        return list(new LambdaQueryWrapper<SysJob>()
                .like(StrUtil.isNotBlank(job.getJobName()), SysJob::getJobName, job.getJobName())
                .eq(StrUtil.isNotBlank(job.getJobGroup()), SysJob::getJobGroup, job.getJobGroup())
                .eq(StrUtil.isNotBlank(job.getStatus()), SysJob::getStatus, job.getStatus())
                .like(StrUtil.isNotBlank(job.getInvokeTarget()), SysJob::getInvokeTarget, job.getInvokeTarget()));
    }
    /**
     * é€šè¿‡è°ƒåº¦ä»»åŠ¡ID查询调度信息
     *
     * @param jobId è°ƒåº¦ä»»åŠ¡ID
     * @return è°ƒåº¦ä»»åŠ¡å¯¹è±¡ä¿¡æ¯
     */
    @Override
    public SysJob selectJobById(Long jobId) {
        return getById(jobId);
    }
    /**
     * æš‚停任务
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     */
    @Override
    @Transactional
    public int pauseJob(SysJob job) throws SchedulerException {
        Long jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
        int rows = baseMapper.updateById(job);
        if (rows > 0) {
            scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
        return rows;
    }
    /**
     * æ¢å¤ä»»åŠ¡
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     */
    @Override
    @Transactional
    public int resumeJob(SysJob job) throws SchedulerException {
        Long jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        job.setStatus(ScheduleConstants.Status.NORMAL.getValue());
        int rows = baseMapper.updateById(job);
        if (rows > 0) {
            scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
        return rows;
    }
    /**
     * åˆ é™¤ä»»åŠ¡åŽï¼Œæ‰€å¯¹åº”çš„trigger也将被删除
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     */
    @Override
    @Transactional
    public int deleteJob(SysJob job) throws SchedulerException {
        Long jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        int rows = baseMapper.deleteById(jobId);
        if (rows > 0) {
            scheduler.deleteJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
        return rows;
    }
    /**
     * æ‰¹é‡åˆ é™¤è°ƒåº¦ä¿¡æ¯
     *
     * @param jobIds éœ€è¦åˆ é™¤çš„任务ID
     * @return ç»“æžœ
     */
    @Override
    @Transactional
    public void deleteJobByIds(Long[] jobIds) throws SchedulerException {
        for (Long jobId : jobIds) {
            SysJob job = getById(jobId);
            deleteJob(job);
        }
    }
    /**
     * ä»»åŠ¡è°ƒåº¦çŠ¶æ€ä¿®æ”¹
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     */
    @Override
    @Transactional
    public int changeStatus(SysJob job) throws SchedulerException {
        int rows = 0;
        String status = job.getStatus();
        if (ScheduleConstants.Status.NORMAL.getValue().equals(status)) {
            rows = resumeJob(job);
        } else if (ScheduleConstants.Status.PAUSE.getValue().equals(status)) {
            rows = pauseJob(job);
        }
        return rows;
    }
    /**
     * ç«‹å³è¿è¡Œä»»åŠ¡
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     */
    @Override
    @Transactional
    public void run(SysJob job) throws SchedulerException {
        Long jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        SysJob properties = selectJobById(job.getJobId());
        // å‚æ•°
        JobDataMap dataMap = new JobDataMap();
        dataMap.put(ScheduleConstants.TASK_PROPERTIES, properties);
        scheduler.triggerJob(ScheduleUtils.getJobKey(jobId, jobGroup), dataMap);
    }
    /**
     * æ–°å¢žä»»åŠ¡
     *
     * @param job è°ƒåº¦ä¿¡æ¯ è°ƒåº¦ä¿¡æ¯
     */
    @Override
    @Transactional
    public int insertJob(SysJob job) throws SchedulerException, TaskException {
        job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
        int rows = baseMapper.insert(job);
        if (rows > 0) {
            ScheduleUtils.createScheduleJob(scheduler, job);
        }
        return rows;
    }
    /**
     * æ›´æ–°ä»»åŠ¡çš„æ—¶é—´è¡¨è¾¾å¼
     *
     * @param job è°ƒåº¦ä¿¡æ¯
     */
    @Override
    @Transactional
    public int updateJob(SysJob job) throws SchedulerException, TaskException {
        SysJob properties = selectJobById(job.getJobId());
        int rows = baseMapper.updateById(job);
        if (rows > 0) {
            updateSchedulerJob(job, properties.getJobGroup());
        }
        return rows;
    }
    /**
     * æ›´æ–°ä»»åŠ¡
     *
     * @param job      ä»»åŠ¡å¯¹è±¡
     * @param jobGroup ä»»åŠ¡ç»„å
     */
    public void updateSchedulerJob(SysJob job, String jobGroup) throws SchedulerException, TaskException {
        Long jobId = job.getJobId();
        // åˆ¤æ–­æ˜¯å¦å­˜åœ¨
        JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
        if (scheduler.checkExists(jobKey)) {
            // é˜²æ­¢åˆ›å»ºæ—¶å­˜åœ¨æ•°æ®é—®é¢˜ å…ˆç§»é™¤ï¼Œç„¶åŽåœ¨æ‰§è¡Œåˆ›å»ºæ“ä½œ
            scheduler.deleteJob(jobKey);
        }
        ScheduleUtils.createScheduleJob(scheduler, job);
    }
    /**
     * æ ¡éªŒcron表达式是否有效
     *
     * @param cronExpression è¡¨è¾¾å¼
     * @return ç»“æžœ
     */
    @Override
    public boolean checkCronExpressionIsValid(String cronExpression) {
        return CronUtils.isValid(cronExpression);
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java
@@ -1,6 +1,6 @@
package com.ruoyi.system.mapper;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.system.domain.SysConfig;
/**
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java
@@ -1,7 +1,7 @@
package com.ruoyi.system.mapper;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@@ -24,7 +24,7 @@
    /**
     * æ ¹æ®è§’色ID查询部门树信息
     *
     * @param roleId            è§’色ID
     * @param roleId è§’色ID
     * @param deptCheckStrictly éƒ¨é—¨æ ‘选择项是否关联显示
     * @return é€‰ä¸­éƒ¨é—¨åˆ—表
     */
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java
@@ -2,7 +2,7 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import java.util.List;
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java
@@ -1,7 +1,7 @@
package com.ruoyi.system.mapper;
import com.ruoyi.common.core.domain.entity.SysDictType;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
/**
 * å­—典表 æ•°æ®å±‚
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java
@@ -1,6 +1,6 @@
package com.ruoyi.system.mapper;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.system.domain.SysLogininfor;
/**
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java
@@ -1,7 +1,7 @@
package com.ruoyi.system.mapper;
import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import org.apache.ibatis.annotations.Param;
import java.util.List;
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java
@@ -1,6 +1,6 @@
package com.ruoyi.system.mapper;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.system.domain.SysNotice;
/**
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java
@@ -1,6 +1,6 @@
package com.ruoyi.system.mapper;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.system.domain.SysOperLog;
/**
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java
@@ -1,6 +1,6 @@
package com.ruoyi.system.mapper;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.system.domain.SysPost;
import java.util.List;
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java
@@ -1,9 +1,7 @@
package com.ruoyi.system.mapper;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.system.domain.SysRoleDept;
import java.util.List;
/**
 * è§’色与部门关联表 æ•°æ®å±‚
@@ -11,13 +9,5 @@
 * @author ruoyi
 */
public interface SysRoleDeptMapper extends BaseMapperPlus<SysRoleDept> {
    /**
     * æ‰¹é‡æ–°å¢žè§’色部门信息
     *
     * @param roleDeptList è§’色部门列表
     * @return ç»“æžœ
     */
    public int batchRoleDept(List<SysRoleDept> roleDeptList);
}
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java
@@ -2,7 +2,7 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import org.apache.ibatis.annotations.Param;
import java.util.List;
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java
@@ -1,9 +1,7 @@
package com.ruoyi.system.mapper;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.system.domain.SysRoleMenu;
import java.util.List;
/**
 * è§’色与菜单关联表 æ•°æ®å±‚
@@ -11,13 +9,5 @@
 * @author ruoyi
 */
public interface SysRoleMenuMapper extends BaseMapperPlus<SysRoleMenu> {
    /**
     * æ‰¹é‡æ–°å¢žè§’色菜单信息
     *
     * @param roleMenuList è§’色菜单列表
     * @return ç»“æžœ
     */
    public int batchRoleMenu(List<SysRoleMenu> roleMenuList);
}
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java
@@ -2,7 +2,7 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import org.apache.ibatis.annotations.Param;
import java.util.List;
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java
@@ -1,9 +1,7 @@
package com.ruoyi.system.mapper;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.system.domain.SysUserPost;
import java.util.List;
/**
 * ç”¨æˆ·ä¸Žå²—位关联表 æ•°æ®å±‚
@@ -11,13 +9,5 @@
 * @author ruoyi
 */
public interface SysUserPostMapper extends BaseMapperPlus<SysUserPost> {
    /**
     * æ‰¹é‡æ–°å¢žç”¨æˆ·å²—位信息
     *
     * @param userPostList ç”¨æˆ·è§’色列表
     * @return ç»“æžœ
     */
    public int batchUserPost(List<SysUserPost> userPostList);
}
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java
@@ -1,9 +1,7 @@
package com.ruoyi.system.mapper;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.system.domain.SysUserRole;
import java.util.List;
/**
 * ç”¨æˆ·ä¸Žè§’色关联表 æ•°æ®å±‚
@@ -11,13 +9,5 @@
 * @author ruoyi
 */
public interface SysUserRoleMapper extends BaseMapperPlus<SysUserRole> {
    /**
     * æ‰¹é‡æ–°å¢žç”¨æˆ·è§’色信息
     *
     * @param userRoleList ç”¨æˆ·è§’色列表
     * @return ç»“æžœ
     */
    public int batchUserRole(List<SysUserRole> userRoleList);
}
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.page.IServicePlus;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.system.domain.SysConfig;
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java
@@ -2,7 +2,7 @@
import com.ruoyi.common.core.domain.TreeSelect;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.page.IServicePlus;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import java.util.List;
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java
@@ -1,7 +1,7 @@
package com.ruoyi.system.service;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.page.IServicePlus;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import java.util.List;
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java
@@ -2,7 +2,7 @@
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.domain.entity.SysDictType;
import com.ruoyi.common.core.page.IServicePlus;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import java.util.List;
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.page.IServicePlus;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.system.domain.SysLogininfor;
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java
@@ -2,7 +2,7 @@
import com.ruoyi.common.core.domain.TreeSelect;
import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.common.core.page.IServicePlus;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.system.domain.vo.RouterVo;
import java.util.List;
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.page.IServicePlus;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.system.domain.SysNotice;
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.page.IServicePlus;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.system.domain.SysOperLog;
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.page.IServicePlus;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.system.domain.SysPost;
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java
@@ -1,7 +1,7 @@
package com.ruoyi.system.service;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.page.IServicePlus;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import java.util.List;
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java
@@ -1,7 +1,7 @@
package com.ruoyi.system.service;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.page.IServicePlus;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import java.util.List;
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 ServiceImpl<SysConfigMapper, SysConfig> implements ISysConfigService {
public class SysConfigServiceImpl extends ServicePlusImpl<SysConfigMapper, SysConfig> implements ISysConfigService {
    @Autowired
    private RedisCache redisCache;
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java
@@ -1,16 +1,16 @@
package com.ruoyi.system.service.impl;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Validator;
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.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;
@@ -21,7 +21,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
@@ -31,7 +30,7 @@
 * @author ruoyi
 */
@Service
public class SysDeptServiceImpl extends ServiceImpl<SysDeptMapper, SysDept> implements ISysDeptService {
public class SysDeptServiceImpl extends ServicePlusImpl<SysDeptMapper, SysDept> implements ISysDeptService {
    @Autowired
    private SysRoleMapper roleMapper;
@@ -64,14 +63,13 @@
        for (SysDept dept : depts) {
            tempList.add(dept.getDeptId());
        }
        for (Iterator<SysDept> iterator = depts.iterator(); iterator.hasNext(); ) {
            SysDept dept = (SysDept) iterator.next();
            // å¦‚果是顶级节点, éåŽ†è¯¥çˆ¶èŠ‚ç‚¹çš„æ‰€æœ‰å­èŠ‚ç‚¹
            if (!tempList.contains(dept.getParentId())) {
                recursionFn(depts, dept);
                returnList.add(dept);
            }
        }
        for (SysDept dept : depts) {
            // å¦‚果是顶级节点, éåŽ†è¯¥çˆ¶èŠ‚ç‚¹çš„æ‰€æœ‰å­èŠ‚ç‚¹
            if (!tempList.contains(dept.getParentId())) {
                recursionFn(depts, dept);
                returnList.add(dept);
            }
        }
        if (returnList.isEmpty()) {
            returnList = depts;
        }
@@ -137,7 +135,7 @@
        int result = count(new LambdaQueryWrapper<SysDept>()
                .eq(SysDept::getParentId, deptId)
                .last("limit 1"));
        return result > 0 ? true : false;
        return result > 0;
    }
    /**
@@ -150,7 +148,7 @@
    public boolean checkDeptExistUser(Long deptId) {
        int result = userMapper.selectCount(new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getDeptId, deptId));
        return result > 0 ? true : false;
        return result > 0;
    }
    /**
@@ -208,7 +206,7 @@
        int result = baseMapper.updateById(dept);
        if (UserConstants.DEPT_NORMAL.equals(dept.getStatus())) {
            // å¦‚果该部门是启用状态,则启用该部门的所有上级部门
            updateParentDeptStatus(dept);
            updateParentDeptStatusNormal(dept);
        }
        return result;
    }
@@ -218,22 +216,18 @@
     *
     * @param dept å½“前部门
     */
    private void updateParentDeptStatus(SysDept dept) {
        String updateBy = dept.getUpdateBy();
        dept = getById(dept.getDeptId());
        dept.setUpdateBy(updateBy);
        update(null,new LambdaUpdateWrapper<SysDept>()
                .set(StrUtil.isNotBlank(dept.getStatus()),
                        SysDept::getStatus,dept.getStatus())
                .set(StrUtil.isNotBlank(dept.getUpdateBy()),
                        SysDept::getUpdateBy,dept.getUpdateBy())
                .in(SysDept::getDeptId, Arrays.asList(dept.getAncestors().split(","))));
    private void updateParentDeptStatusNormal(SysDept dept) {
        String ancestors = dept.getAncestors();
        Long[] deptIds = Convert.toLongArray(ancestors);
        update(null, new LambdaUpdateWrapper<SysDept>()
                .set(SysDept::getStatus, "0")
                .in(SysDept::getDeptId, Arrays.asList(deptIds)));
    }
    /**
     * ä¿®æ”¹å­å…ƒç´ å…³ç³»
     *
     * @param deptId       è¢«ä¿®æ”¹çš„部门ID
     * @param deptId è¢«ä¿®æ”¹çš„部门ID
     * @param newAncestors æ–°çš„父ID集合
     * @param oldAncestors æ—§çš„父ID集合
     */
@@ -278,13 +272,11 @@
     */
    private List<SysDept> getChildList(List<SysDept> list, SysDept t) {
        List<SysDept> tlist = new ArrayList<SysDept>();
        Iterator<SysDept> it = list.iterator();
        while (it.hasNext()) {
            SysDept n = (SysDept) it.next();
            if (Validator.isNotNull(n.getParentId()) && n.getParentId().longValue() == t.getDeptId().longValue()) {
                tlist.add(n);
            }
        }
        for (SysDept n : list) {
            if (Validator.isNotNull(n.getParentId()) && n.getParentId().longValue() == t.getDeptId().longValue()) {
                tlist.add(n);
            }
        }
        return tlist;
    }
@@ -292,6 +284,6 @@
     * åˆ¤æ–­æ˜¯å¦æœ‰å­èŠ‚ç‚¹
     */
    private boolean hasChild(List<SysDept> list, SysDept t) {
        return getChildList(list, t).size() > 0 ? true : false;
        return getChildList(list, t).size() > 0;
    }
}
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;
@@ -20,7 +20,7 @@
 * @author ruoyi
 */
@Service
public class SysDictDataServiceImpl extends ServiceImpl<SysDictDataMapper, SysDictData> implements ISysDictDataService {
public class SysDictDataServiceImpl extends ServicePlusImpl<SysDictDataMapper, SysDictData> implements ISysDictDataService {
    @Override
    public TableDataInfo<SysDictData> selectPageDictDataList(SysDictData dictData) {
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 ServiceImpl<SysDictTypeMapper, SysDictType> implements ISysDictTypeService {
public class SysDictTypeServiceImpl extends ServicePlusImpl<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.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
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 ServiceImpl<SysLogininforMapper, SysLogininfor> implements ISysLogininforService {
public class SysLogininforServiceImpl extends ServicePlusImpl<SysLogininforMapper, SysLogininfor> implements ISysLogininforService {
    @Override
    public TableDataInfo<SysLogininfor> selectPageLogininforList(SysLogininfor logininfor) {
@@ -36,9 +36,8 @@
                        params.get("beginTime"))
                .apply(Validator.isNotEmpty(params.get("endTime")),
                        "date_format(login_time,'%y%m%d') <= date_format({0},'%y%m%d')",
                        params.get("endTime"))
                .orderByDesc(SysLogininfor::getInfoId);
        return PageUtils.buildDataInfo(page(PageUtils.buildPage(), lqw));
                        params.get("endTime"));
        return PageUtils.buildDataInfo(page(PageUtils.buildPage("info_id","desc"), lqw));
    }
    /**
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java
@@ -3,12 +3,12 @@
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.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;
@@ -25,11 +25,11 @@
/**
 * èœå• ä¸šåŠ¡å±‚å¤„ç†
 *
 *
 * @author ruoyi
 */
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements ISysMenuService {
public class SysMenuServiceImpl extends ServicePlusImpl<SysMenuMapper, SysMenu> implements ISysMenuService {
    public static final String PREMISSION_STRING = "perms[\"{0}\"]";
    @Autowired
@@ -137,7 +137,7 @@
            router.setComponent(getComponent(menu));
            router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StrUtil.equals("1", menu.getIsCache())));
            List<SysMenu> cMenus = menu.getChildren();
            if (!cMenus.isEmpty() && cMenus.size() > 0 && UserConstants.TYPE_DIR.equals(menu.getMenuType())) {
            if (!cMenus.isEmpty() && UserConstants.TYPE_DIR.equals(menu.getMenuType())) {
                router.setAlwaysShow(true);
                router.setRedirect("noRedirect");
                router.setChildren(buildMenus(cMenus));
@@ -170,14 +170,13 @@
        for (SysMenu dept : menus) {
            tempList.add(dept.getMenuId());
        }
        for (Iterator<SysMenu> iterator = menus.iterator(); iterator.hasNext(); ) {
            SysMenu menu = (SysMenu) iterator.next();
            // å¦‚果是顶级节点, éåŽ†è¯¥çˆ¶èŠ‚ç‚¹çš„æ‰€æœ‰å­èŠ‚ç‚¹
            if (!tempList.contains(menu.getParentId())) {
                recursionFn(menus, menu);
                returnList.add(menu);
            }
        }
        for (SysMenu menu : menus) {
            // å¦‚果是顶级节点, éåŽ†è¯¥çˆ¶èŠ‚ç‚¹çš„æ‰€æœ‰å­èŠ‚ç‚¹
            if (!tempList.contains(menu.getParentId())) {
                recursionFn(menus, menu);
                returnList.add(menu);
            }
        }
        if (returnList.isEmpty()) {
            returnList = menus;
        }
@@ -216,7 +215,7 @@
    @Override
    public boolean hasChildByMenuId(Long menuId) {
        int result = count(new LambdaQueryWrapper<SysMenu>().eq(SysMenu::getParentId,menuId));
        return result > 0 ? true : false;
        return result > 0;
    }
    /**
@@ -228,7 +227,7 @@
    @Override
    public boolean checkMenuExistRole(Long menuId) {
        int result = roleMenuMapper.selectCount(new LambdaQueryWrapper<SysRoleMenu>().eq(SysRoleMenu::getMenuId,menuId));
        return result > 0 ? true : false;
        return result > 0;
    }
    /**
@@ -364,14 +363,13 @@
     */
    public List<SysMenu> getChildPerms(List<SysMenu> list, int parentId) {
        List<SysMenu> returnList = new ArrayList<SysMenu>();
        for (Iterator<SysMenu> iterator = list.iterator(); iterator.hasNext(); ) {
            SysMenu t = (SysMenu) iterator.next();
            // ä¸€ã€æ ¹æ®ä¼ å…¥çš„æŸä¸ªçˆ¶èŠ‚ç‚¹ID,遍历该父节点的所有子节点
            if (t.getParentId() == parentId) {
                recursionFn(list, t);
                returnList.add(t);
            }
        }
        for (SysMenu t : list) {
            // ä¸€ã€æ ¹æ®ä¼ å…¥çš„æŸä¸ªçˆ¶èŠ‚ç‚¹ID,遍历该父节点的所有子节点
            if (t.getParentId() == parentId) {
                recursionFn(list, t);
                returnList.add(t);
            }
        }
        return returnList;
    }
@@ -397,13 +395,11 @@
     */
    private List<SysMenu> getChildList(List<SysMenu> list, SysMenu t) {
        List<SysMenu> tlist = new ArrayList<SysMenu>();
        Iterator<SysMenu> it = list.iterator();
        while (it.hasNext()) {
            SysMenu n = (SysMenu) it.next();
            if (n.getParentId().longValue() == t.getMenuId().longValue()) {
                tlist.add(n);
            }
        }
        for (SysMenu n : list) {
            if (n.getParentId().longValue() == t.getMenuId().longValue()) {
                tlist.add(n);
            }
        }
        return tlist;
    }
@@ -411,6 +407,6 @@
     * åˆ¤æ–­æ˜¯å¦æœ‰å­èŠ‚ç‚¹
     */
    private boolean hasChild(List<SysMenu> list, SysMenu t) {
        return getChildList(list, t).size() > 0 ? true : false;
        return getChildList(list, t).size() > 0;
    }
}
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.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
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 ServiceImpl<SysNoticeMapper, SysNotice> implements ISysNoticeService {
public class SysNoticeServiceImpl extends ServicePlusImpl<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.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
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 ServiceImpl<SysOperLogMapper, SysOperLog> implements ISysOperLogService {
public class SysOperLogServiceImpl extends ServicePlusImpl<SysOperLogMapper, SysOperLog> implements ISysOperLogService {
    @Override
    public TableDataInfo<SysOperLog> selectPageOperLogList(SysOperLog operLog) {
@@ -45,9 +45,8 @@
                        params.get("beginTime"))
                .apply(Validator.isNotEmpty(params.get("endTime")),
                        "date_format(oper_time,'%y%m%d') <= date_format({0},'%y%m%d')",
                        params.get("endTime"))
                .orderByDesc(SysOperLog::getOperId);
        return PageUtils.buildDataInfo(page(PageUtils.buildPage(), lqw));
                        params.get("endTime"));
        return PageUtils.buildDataInfo(page(PageUtils.buildPage("oper_id","desc"), lqw));
    }
    /**
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 ServiceImpl<SysPostMapper, SysPost> implements ISysPostService {
public class SysPostServiceImpl extends ServicePlusImpl<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 ServiceImpl<SysRoleMapper, SysRole> implements ISysRoleService {
public class SysRoleServiceImpl extends ServicePlusImpl<SysRoleMapper, SysRole> implements ISysRoleService {
    @Autowired
    private SysRoleMenuMapper roleMenuMapper;
@@ -240,7 +240,7 @@
            list.add(rm);
        }
        if (list.size() > 0) {
            rows = roleMenuMapper.batchRoleMenu(list);
            rows = roleMenuMapper.insertAll(list);
        }
        return rows;
    }
@@ -261,7 +261,7 @@
            list.add(rd);
        }
        if (list.size() > 0) {
            rows = roleDeptMapper.batchRoleDept(list);
            rows = roleDeptMapper.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 ServiceImpl<SysUserMapper, SysUser> implements ISysUserService {
public class SysUserServiceImpl extends ServicePlusImpl<SysUserMapper, SysUser> implements ISysUserService {
    @Autowired
    private SysRoleMapper roleMapper;
@@ -311,7 +311,7 @@
                list.add(ur);
            }
            if (list.size() > 0) {
                userRoleMapper.batchUserRole(list);
                userRoleMapper.insertAll(list);
            }
        }
    }
@@ -333,7 +333,7 @@
                list.add(up);
            }
            if (list.size() > 0) {
                userPostMapper.batchUserPost(list);
                userPostMapper.insertAll(list);
            }
        }
    }
ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml
@@ -9,11 +9,4 @@
        <result property="deptId" column="dept_id"/>
    </resultMap>
    <insert id="batchRoleDept">
        insert into sys_role_dept(role_id, dept_id) values
        <foreach item="item" index="index" collection="list" separator=",">
            (#{item.roleId},#{item.deptId})
        </foreach>
    </insert>
</mapper>
ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml
@@ -9,11 +9,4 @@
        <result property="menuId" column="menu_id"/>
    </resultMap>
    <insert id="batchRoleMenu">
        insert into sys_role_menu(role_id, menu_id) values
        <foreach item="item" index="index" collection="list" separator=",">
            (#{item.roleId},#{item.menuId})
        </foreach>
    </insert>
</mapper>
ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml
@@ -9,11 +9,4 @@
        <result property="postId" column="post_id"/>
    </resultMap>
    <insert id="batchUserPost">
        insert into sys_user_post(user_id, post_id) values
        <foreach item="item" index="index" collection="list" separator=",">
            (#{item.userId},#{item.postId})
        </foreach>
    </insert>
</mapper>
ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml
@@ -9,11 +9,4 @@
        <result property="roleId" column="role_id"/>
    </resultMap>
    <insert id="batchUserRole">
        insert into sys_user_role(user_id, role_id) values
        <foreach item="item" index="index" collection="list" separator=",">
            (#{item.userId},#{item.roleId})
        </foreach>
    </insert>
</mapper>
ruoyi-ui/.env.development
@@ -1,3 +1,6 @@
# é¡µé¢æ ‡é¢˜
VUE_APP_TITLE = RuoYi-Vue-Plus后台管理系统
# å¼€å‘环境配置
ENV = 'development'
ruoyi-ui/.env.production
@@ -1,3 +1,6 @@
# é¡µé¢æ ‡é¢˜
VUE_APP_TITLE = RuoYi-Vue-Plus后台管理系统
# ç”Ÿäº§çŽ¯å¢ƒé…ç½®
ENV = 'production'
ruoyi-ui/.env.staging
@@ -1,3 +1,6 @@
# é¡µé¢æ ‡é¢˜
VUE_APP_TITLE = RuoYi-Vue-Plus后台管理系统
NODE_ENV = production
# æµ‹è¯•环境配置
ruoyi-ui/package.json
@@ -41,7 +41,7 @@
    "clipboard": "2.0.6",
    "core-js": "3.8.1",
    "echarts": "4.9.0",
    "element-ui": "2.15.0",
    "element-ui": "2.15.2",
    "file-saver": "2.0.4",
    "fuse.js": "6.4.3",
    "highlight.js": "9.18.5",
@@ -55,6 +55,7 @@
    "vue": "2.6.12",
    "vue-count-to": "1.0.13",
    "vue-cropper": "0.5.5",
    "vue-meta": "^2.4.0",
    "vue-router": "3.4.9",
    "vuedraggable": "2.24.3",
    "vuex": "3.6.0"
ruoyi-ui/src/App.vue
@@ -6,6 +6,14 @@
<script>
export default  {
  name:  'App'
  name:  'App',
    metaInfo() {
        return {
            title: this.$store.state.settings.dynamicTitle && this.$store.state.settings.title,
            titleTemplate: title => {
                return title ? `${title} - ${process.env.VUE_APP_TITLE}` : process.env.VUE_APP_TITLE
            }
        }
    }
}
</script>
ruoyi-ui/src/assets/styles/ruoyi.scss
@@ -105,6 +105,15 @@
    position: absolute;
}
@media ( max-width : 768px) {
  .pagination-container .el-pagination > .el-pagination__jump {
    display: none !important;
  }
  .pagination-container .el-pagination > .el-pagination__sizes {
    display: none !important;
  }
}
.el-table .fixed-width .el-button--mini {
    padding-left: 0;
    padding-right: 0;
ruoyi-ui/src/components/DictTag/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,51 @@
<template>
  <div>
    <template v-for="(item, index) in options">
      <template v-if="values.includes(item.dictValue)">
        <span
          v-if="item.listClass == 'default' || item.listClass == ''"
          :key="item.dictValue"
          :index="index"
          :class="item.cssClass"
          >{{ item.dictLabel }}</span
        >
        <el-tag
          v-else
          :key="item.dictValue"
          :index="index"
          :type="item.listClass == 'primary' ? '' : item.listClass"
          :class="item.cssClass"
        >
          {{ item.dictLabel }}
        </el-tag>
      </template>
    </template>
  </div>
</template>
<script>
export default {
  name: "DictTag",
  props: {
    options: {
      type: Array,
      default: null,
    },
    value: [String, Array],
  },
  computed: {
    values() {
      if (this.value) {
        return Array.isArray(this.value) ? this.value : [this.value];
      } else {
        return [];
      }
    },
  },
};
</script>
<style scoped>
.el-tag + .el-tag {
  margin-left: 10px;
}
</style>
ruoyi-ui/src/components/Pagination/index.vue
@@ -6,6 +6,7 @@
      :page-size.sync="pageSize"
      :layout="layout"
      :page-sizes="pageSizes"
      :pager-count="pagerCount"
      :total="total"
      v-bind="$attrs"
      @size-change="handleSizeChange"
@@ -38,6 +39,11 @@
        return [10, 20, 30, 50]
      }
    },
    // ç§»åŠ¨ç«¯é¡µç æŒ‰é’®çš„æ•°é‡ç«¯é»˜è®¤å€¼5
    pagerCount: {
      type: Number,
      default: document.body.clientWidth < 992 ? 5 : 7
    },
    layout: {
      type: String,
      default: 'total, sizes, prev, pager, next, jumper'
ruoyi-ui/src/components/iFrame/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
<template>
  <div v-loading="loading" :style="'height:' + height">
    <iframe
      :src="src"
      frameborder="no"
      style="width: 100%; height: 100%"
      scrolling="auto"
    />
  </div>
</template>
<script>
export default {
  props: {
    src: {
      type: String,
      required: true
    },
  },
  data() {
    return {
      height: document.documentElement.clientHeight - 94.5 + "px;",
      loading: true,
      url: this.src
    };
  },
  mounted: function () {
    setTimeout(() => {
      this.loading = false;
    }, 300);
    const that = this;
    window.onresize = function temp() {
      that.height = document.documentElement.clientHeight - 94.5 + "px;";
    };
  }
};
</script>
ruoyi-ui/src/layout/components/Settings/index.vue
@@ -41,7 +41,7 @@
      <el-divider/>
      <h3 class="drawer-title">系统布局配置</h3>
      <div class="drawer-item">
        <span>开启 TopNav</span>
        <el-switch v-model="topNav" class="drawer-switch" />
@@ -60,6 +60,11 @@
      <div class="drawer-item">
        <span>显示 Logo</span>
        <el-switch v-model="sidebarLogo" class="drawer-switch" />
      </div>
      <div class="drawer-item">
        <span>动态标题</span>
        <el-switch v-model="dynamicTitle" class="drawer-switch" />
      </div>
      <el-divider/>
@@ -129,6 +134,17 @@
        })
      }
    },
    dynamicTitle: {
      get() {
        return this.$store.state.settings.dynamicTitle
      },
      set(val) {
        this.$store.dispatch('settings/changeSetting', {
          key: 'dynamicTitle',
          value: val
        })
      }
    },
  },
  methods: {
    themeChange(val) {
@@ -160,6 +176,7 @@
            "tagsView":${this.tagsView},
            "fixedHeader":${this.fixedHeader},
            "sidebarLogo":${this.sidebarLogo},
            "dynamicTitle":${this.dynamicTitle},
            "sideTheme":"${this.sideTheme}",
            "theme":"${this.theme}"
          }`
ruoyi-ui/src/layout/components/Sidebar/Logo.vue
@@ -2,11 +2,11 @@
  <div class="sidebar-logo-container" :class="{'collapse':collapse}" :style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBg : variables.menuLightBg }">
    <transition name="sidebarLogoFade">
      <router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
        <img v-if="logo" :src="logo" class="sidebar-logo">
        <img v-if="logo" :src="logo" class="sidebar-logo" />
        <h1 v-else class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.sidebarTitle : variables.sidebarLightTitle }">{{ title }} </h1>
      </router-link>
      <router-link v-else key="expand" class="sidebar-logo-link" to="/">
        <img v-if="logo" :src="logo" class="sidebar-logo">
        <img v-if="logo" :src="logo" class="sidebar-logo" />
        <h1 class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.sidebarTitle : variables.sidebarLightTitle }">{{ title }} </h1>
      </router-link>
    </transition>
ruoyi-ui/src/main.js
@@ -18,8 +18,12 @@
import { getConfigKey } from "@/api/system/config";
import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, download, handleTree } from "@/utils/ruoyi";
import Pagination from "@/components/Pagination";
// è‡ªå®šä¹‰è¡¨æ ¼å·¥å…·æ‰©å±•
// è‡ªå®šä¹‰è¡¨æ ¼å·¥å…·ç»„ä»¶
import RightToolbar from "@/components/RightToolbar"
// å­—典标签组件
import DictTag from '@/components/DictTag'
// å¤´éƒ¨æ ‡ç­¾ç»„ä»¶
import VueMeta from 'vue-meta'
// å…¨å±€æ–¹æ³•挂载
Vue.prototype.getDicts = getDicts
@@ -45,10 +49,12 @@
}
// å…¨å±€ç»„件挂载
Vue.component('DictTag', DictTag)
Vue.component('Pagination', Pagination)
Vue.component('RightToolbar', RightToolbar)
Vue.use(permission)
Vue.use(VueMeta)
/**
 * If you don't want to use mock-server
ruoyi-ui/src/permission.js
@@ -12,6 +12,7 @@
router.beforeEach((to, from, next) => {
  NProgress.start()
  if (getToken()) {
    to.meta.title && store.dispatch('settings/setTitle', to.meta.title)
    /* has token*/
    if (to.path === '/login') {
      next({ path: '/' })
ruoyi-ui/src/settings.js
@@ -1,6 +1,4 @@
module.exports = {
  title: 'RuoYi-Vue-Plus后台管理系统',
  /**
   * ä¾§è¾¹æ ä¸»é¢˜ æ·±è‰²ä¸»é¢˜theme-dark,浅色主题theme-light
   */
@@ -32,6 +30,11 @@
  sidebarLogo: true,
  /**
   * æ˜¯å¦æ˜¾ç¤ºåŠ¨æ€æ ‡é¢˜
   */
  dynamicTitle: false,
  /**
   * @type {string | array} 'production' | ['production', 'development']
   * @description Need show err logs component.
   * The default is only used in the production env
ruoyi-ui/src/store/modules/settings.js
@@ -1,17 +1,19 @@
import variables from '@/assets/styles/element-variables.scss'
import defaultSettings from '@/settings'
const { sideTheme, showSettings, topNav, tagsView, fixedHeader, sidebarLogo } = defaultSettings
const { sideTheme, showSettings, topNav, tagsView, fixedHeader, sidebarLogo, dynamicTitle } = defaultSettings
const storageSetting = JSON.parse(localStorage.getItem('layout-setting')) || ''
const state = {
  title: '',
  theme: storageSetting.theme || variables.theme,
  sideTheme: storageSetting.sideTheme || sideTheme,
  showSettings: showSettings,
  topNav:  storageSetting.topNav === undefined ? topNav : storageSetting.topNav,
  tagsView: storageSetting.tagsView === undefined ? tagsView : storageSetting.tagsView,
  fixedHeader: storageSetting.fixedHeader === undefined ? fixedHeader : storageSetting.fixedHeader,
  sidebarLogo: storageSetting.sidebarLogo === undefined ? sidebarLogo : storageSetting.sidebarLogo
  sidebarLogo: storageSetting.sidebarLogo === undefined ? sidebarLogo : storageSetting.sidebarLogo,
  dynamicTitle: storageSetting.dynamicTitle === undefined ? dynamicTitle : storageSetting.dynamicTitle
}
const mutations = {
  CHANGE_SETTING: (state, { key, value }) => {
@@ -22,8 +24,13 @@
}
const actions = {
  // ä¿®æ”¹å¸ƒå±€è®¾ç½®
  changeSetting({ commit }, data) {
    commit('CHANGE_SETTING', data)
  },
  // è®¾ç½®ç½‘页标题
  setTitle({ commit }, title) {
    state.title = title
  }
}
ruoyi-ui/src/views/demo/demo/index.vue
@@ -150,6 +150,14 @@
        <el-form-item label="值" prop="value">
          <el-input v-model="form.value" placeholder="请输入值" />
        </el-form-item>
        <el-form-item label="创建时间" prop="createTime">
          <el-date-picker clearable size="small"
                          v-model="form.createTime"
                          type="datetime"
                          value-format="yyyy-MM-dd HH:mm:ss"
                          placeholder="选择创建时间">
          </el-date-picker>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button :loading="buttonLoading" type="primary" @click="submitForm">ç¡® å®š</el-button>
ruoyi-ui/src/views/index.vue
@@ -53,7 +53,7 @@
              <li>JWT</li>
              <li>MyBatis</li>
              <li>Druid</li>
              <li>Fastjson</li>
              <li>Jackson</li>
              <li>...</li>
            </ul>
          </el-col>
ruoyi-ui/src/views/monitor/druid/index.vue
@@ -1,26 +1,15 @@
<template>
  <div v-loading="loading" :style="'height:'+ height">
    <iframe :src="src" frameborder="no" style="width: 100%;height: 100%" scrolling="auto" />
  </div>
  <i-frame :src="url" />
</template>
<script>
import iFrame from "@/components/iFrame/index";
export default {
  name: "Druid",
  components: { iFrame },
  data() {
    return {
      src: process.env.VUE_APP_BASE_API + "/druid/login.html",
      height: document.documentElement.clientHeight - 94.5 + "px;",
      loading: true
      url: process.env.VUE_APP_BASE_API + "/druid/login.html"
    };
  },
  mounted: function() {
    setTimeout(() => {
      this.loading = false;
    }, 230);
    const that = this;
    window.onresize = function temp() {
      that.height = document.documentElement.clientHeight - 94.5 + "px;";
    };
  }
};
</script>
ruoyi-ui/src/views/monitor/logininfor/index.vue
@@ -91,17 +91,17 @@
      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
    </el-row>
    <el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
    <el-table ref="tables" v-loading="loading" :data="list" @selection-change="handleSelectionChange" :default-sort="defaultSort" @sort-change="handleSortChange">
      <el-table-column type="selection" width="55" align="center" />
      <el-table-column label="访问编号" align="center" prop="infoId" />
      <el-table-column label="用户名称" align="center" prop="userName" />
      <el-table-column label="用户名称" align="center" prop="userName" :show-overflow-tooltip="true" sortable="custom" :sort-orders="['descending', 'ascending']" />
      <el-table-column label="登录地址" align="center" prop="ipaddr" width="130" :show-overflow-tooltip="true" />
      <el-table-column label="登录地点" align="center" prop="loginLocation" :show-overflow-tooltip="true" />
      <el-table-column label="浏览器" align="center" prop="browser" />
      <el-table-column label="浏览器" align="center" prop="browser" :show-overflow-tooltip="true" />
      <el-table-column label="操作系统" align="center" prop="os" />
      <el-table-column label="登录状态" align="center" prop="status" :formatter="statusFormat" />
      <el-table-column label="操作信息" align="center" prop="msg" />
      <el-table-column label="登录日期" align="center" prop="loginTime" width="180">
      <el-table-column label="登录日期" align="center" prop="loginTime" sortable="custom" :sort-orders="['descending', 'ascending']" width="180">
        <template slot-scope="scope">
          <span>{{ parseTime(scope.row.loginTime) }}</span>
        </template>
@@ -143,6 +143,8 @@
      statusOptions: [],
      // æ—¥æœŸèŒƒå›´
      dateRange: [],
      // é»˜è®¤æŽ’序
      defaultSort: {prop: 'loginTime', order: 'descending'},
      // æŸ¥è¯¢å‚æ•°
      queryParams: {
        pageNum: 1,
@@ -183,13 +185,20 @@
    resetQuery() {
      this.dateRange = [];
      this.resetForm("queryForm");
      this.$refs.tables.sort(this.defaultSort.prop, this.defaultSort.order)
      this.handleQuery();
    },
    // å¤šé€‰æ¡†é€‰ä¸­æ•°æ®
    /** å¤šé€‰æ¡†é€‰ä¸­æ•°æ® */
    handleSelectionChange(selection) {
      this.ids = selection.map(item => item.infoId)
      this.multiple = !selection.length
    },
    /** æŽ’序触发事件 */
    handleSortChange(column, prop, order) {
      this.queryParams.orderByColumn = column.prop;
      this.queryParams.isAsc = column.order;
      this.getList();
    },
    /** åˆ é™¤æŒ‰é’®æ“ä½œ */
    handleDelete(row) {
      const infoIds = row.infoId || this.ids;
ruoyi-ui/src/views/monitor/operlog/index.vue
@@ -107,17 +107,17 @@
      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
    </el-row>
    <el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
    <el-table ref="tables" v-loading="loading" :data="list" @selection-change="handleSelectionChange" :default-sort="defaultSort" @sort-change="handleSortChange">
      <el-table-column type="selection" width="55" align="center" />
      <el-table-column label="日志编号" align="center" prop="operId" />
      <el-table-column label="系统模块" align="center" prop="title" />
      <el-table-column label="操作类型" align="center" prop="businessType" :formatter="typeFormat" />
      <el-table-column label="请求方式" align="center" prop="requestMethod" />
      <el-table-column label="操作人员" align="center" prop="operName" />
      <el-table-column label="主机" align="center" prop="operIp" width="130" :show-overflow-tooltip="true" />
      <el-table-column label="操作人员" align="center" prop="operName" width="100" :show-overflow-tooltip="true" sortable="custom" :sort-orders="['descending', 'ascending']" />
      <el-table-column label="操作地址" align="center" prop="operIp" width="130" :show-overflow-tooltip="true" />
      <el-table-column label="操作地点" align="center" prop="operLocation" :show-overflow-tooltip="true" />
      <el-table-column label="操作状态" align="center" prop="status" :formatter="statusFormat" />
      <el-table-column label="操作日期" align="center" prop="operTime" width="180">
      <el-table-column label="操作日期" align="center" prop="operTime" sortable="custom" :sort-orders="['descending', 'ascending']" width="180">
        <template slot-scope="scope">
          <span>{{ parseTime(scope.row.operTime) }}</span>
        </template>
@@ -216,6 +216,8 @@
      statusOptions: [],
      // æ—¥æœŸèŒƒå›´
      dateRange: [],
      // é»˜è®¤æŽ’序
      defaultSort: {prop: 'operTime', order: 'descending'},
      // è¡¨å•参数
      form: {},
      // æŸ¥è¯¢å‚æ•°
@@ -266,13 +268,20 @@
    resetQuery() {
      this.dateRange = [];
      this.resetForm("queryForm");
      this.$refs.tables.sort(this.defaultSort.prop, this.defaultSort.order)
      this.handleQuery();
    },
    // å¤šé€‰æ¡†é€‰ä¸­æ•°æ®
    /** å¤šé€‰æ¡†é€‰ä¸­æ•°æ® */
    handleSelectionChange(selection) {
      this.ids = selection.map(item => item.operId)
      this.multiple = !selection.length
    },
    /** æŽ’序触发事件 */
    handleSortChange(column, prop, order) {
      this.queryParams.orderByColumn = column.prop;
      this.queryParams.isAsc = column.order;
      this.getList();
    },
    /** è¯¦ç»†æŒ‰é’®æ“ä½œ */
    handleView(row) {
      this.open = true;
ruoyi-ui/src/views/system/dict/data.vue
@@ -86,10 +86,19 @@
    <el-table v-loading="loading" :data="dataList" @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55" align="center" />
      <el-table-column label="字典编码" align="center" prop="dictCode" />
      <el-table-column label="字典标签" align="center" prop="dictLabel" />
      <el-table-column label="字典标签" align="center" prop="dictLabel">
        <template slot-scope="scope">
          <span v-if="scope.row.listClass == '' || scope.row.listClass == 'default'">{{scope.row.dictLabel}}</span>
          <el-tag v-else :type="scope.row.listClass == 'primary' ? '' : scope.row.listClass">{{scope.row.dictLabel}}</el-tag>
        </template>
      </el-table-column>
      <el-table-column label="字典键值" align="center" prop="dictValue" />
      <el-table-column label="字典排序" align="center" prop="dictSort" />
      <el-table-column label="状态" align="center" prop="status" :formatter="statusFormat" />
      <el-table-column label="状态" align="center" prop="status">
        <template slot-scope="scope">
          <dict-tag :options="statusOptions" :value="scope.row.status"/>
        </template>
      </el-table-column>
      <el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" />
      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
        <template slot-scope="scope">
@@ -136,8 +145,21 @@
        <el-form-item label="数据键值" prop="dictValue">
          <el-input v-model="form.dictValue" placeholder="请输入数据键值" />
        </el-form-item>
        <el-form-item label="样式属性" prop="cssClass">
          <el-input v-model="form.cssClass" placeholder="请输入样式属性" />
        </el-form-item>
        <el-form-item label="显示排序" prop="dictSort">
          <el-input-number v-model="form.dictSort" controls-position="right" :min="0" />
        </el-form-item>
        <el-form-item label="回显样式" prop="listClass">
          <el-select v-model="form.listClass">
            <el-option
              v-for="item in listClassOptions"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            ></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="状态" prop="status">
          <el-radio-group v-model="form.status">
@@ -190,6 +212,33 @@
      title: "",
      // æ˜¯å¦æ˜¾ç¤ºå¼¹å‡ºå±‚
      open: false,
      // æ•°æ®æ ‡ç­¾å›žæ˜¾æ ·å¼
      listClassOptions: [
        {
          value: "default",
          label: "默认"
        },
        {
          value: "primary",
          label: "主要"
        },
        {
          value: "success",
          label: "成功"
        },
        {
          value: "info",
          label: "信息"
        },
        {
          value: "warning",
          label: "警告"
        },
        {
          value: "danger",
          label: "危险"
        }
      ],
      // çŠ¶æ€æ•°æ®å­—å…¸
      statusOptions: [],
      // ç±»åž‹æ•°æ®å­—å…¸
@@ -250,10 +299,6 @@
        this.loading = false;
      });
    },
    // æ•°æ®çŠ¶æ€å­—å…¸ç¿»è¯‘
    statusFormat(row, column) {
      return this.selectDictLabel(this.statusOptions, row.status);
    },
    // å–消按钮
    cancel() {
      this.open = false;
@@ -265,6 +310,8 @@
        dictCode: undefined,
        dictLabel: undefined,
        dictValue: undefined,
        cssClass: undefined,
        listClass: 'default',
        dictSort: 0,
        status: "0",
        remark: undefined
ruoyi-ui/src/views/system/dict/index.vue
@@ -123,7 +123,11 @@
          </router-link>
        </template>
      </el-table-column>
      <el-table-column label="状态" align="center" prop="status" :formatter="statusFormat" />
      <el-table-column label="状态" align="center" prop="status">
        <template slot-scope="scope">
          <dict-tag :options="statusOptions" :value="scope.row.status"/>
        </template>
      </el-table-column>
      <el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" />
      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
        <template slot-scope="scope">
@@ -256,10 +260,6 @@
          this.loading = false;
        }
      );
    },
    // å­—典状态字典翻译
    statusFormat(row, column) {
      return this.selectDictLabel(this.statusOptions, row.status);
    },
    // å–消按钮
    cancel() {
ruoyi-ui/src/views/system/user/index.vue
@@ -210,7 +210,7 @@
        <el-row>
          <el-col :span="12">
            <el-form-item label="用户昵称" prop="nickName">
              <el-input v-model="form.nickName" placeholder="请输入用户昵称" />
              <el-input v-model="form.nickName" placeholder="请输入用户昵称" maxlength="30" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
@@ -234,12 +234,12 @@
        <el-row>
          <el-col :span="12">
            <el-form-item v-if="form.userId == undefined" label="用户名称" prop="userName">
              <el-input v-model="form.userName" placeholder="请输入用户名称" />
              <el-input v-model="form.userName" placeholder="请输入用户名称" maxlength="30" />
            </el-form-item>
          </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" />
              <el-input v-model="form.password" placeholder="请输入用户密码" type="password" maxlength="20" />
            </el-form-item>
          </el-col>
        </el-row>
@@ -440,7 +440,8 @@
          { required: true, message: "用户昵称不能为空", trigger: "blur" }
        ],
        password: [
          { required: true, message: "用户密码不能为空", trigger: "blur" }
          { required: true, message: "用户密码不能为空", trigger: "blur" },
          { min: 5, max: 20, message: '用户密码长度必须介于 5 å’Œ 20 ä¹‹é—´', trigger: 'blur' }
        ],
        email: [
          {
@@ -545,7 +546,7 @@
    },
    /** æœç´¢æŒ‰é’®æ“ä½œ */
    handleQuery() {
      this.queryParams.page = 1;
      this.queryParams.pageNum = 1;
      this.getList();
    },
    /** é‡ç½®æŒ‰é’®æ“ä½œ */
@@ -592,7 +593,10 @@
    handleResetPwd(row) {
      this.$prompt('请输入"' + row.userName + '"的新密码', "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消"
        cancelButtonText: "取消",
        closeOnClickModal: false,
        inputPattern: /^.{5,20}$/,
        inputErrorMessage: "用户密码长度必须介于 5 å’Œ 20 ä¹‹é—´",
      }).then(({ value }) => {
          resetUserPwd(row.userId, value).then(response => {
            this.msgSuccess("修改成功,新密码是:" + value);
ruoyi-ui/src/views/system/user/profile/userInfo.vue
@@ -1,7 +1,7 @@
<template>
  <el-form ref="form" :model="user" :rules="rules" label-width="80px">
    <el-form-item label="用户昵称" prop="nickName">
      <el-input v-model="user.nickName" />
      <el-input v-model="user.nickName" maxlength="30" />
    </el-form-item>
    <el-form-item label="手机号码" prop="phonenumber">
      <el-input v-model="user.phonenumber" maxlength="11" />
ruoyi-ui/src/views/tool/gen/genInfoForm.vue
@@ -7,7 +7,7 @@
          <el-select v-model="info.tplCategory" @change="tplSelectChange">
            <el-option label="单表(增删改查)" value="crud" />
            <el-option label="树表(增删改查)" value="tree" />
            <el-option label="主子表(增删改查)" value="sub" />
<!--            <el-option label="主子表(增删改查)" value="sub" />-->
          </el-select>
        </el-form-item>
      </el-col>
ruoyi-ui/src/views/tool/swagger/index.vue
@@ -1,26 +1,15 @@
<template>
  <div v-loading="loading" :style="'height:'+ height">
    <iframe :src="src" frameborder="no" style="width: 100%;height: 100%" scrolling="auto" />
  </div>
  <i-frame :src="url" />
</template>
<script>
import iFrame from "@/components/iFrame/index";
export default {
  name: "Swagger",
  components: { iFrame },
  data() {
    return {
      src: process.env.VUE_APP_BASE_API + "/doc.html",
      height: document.documentElement.clientHeight - 94.5 + "px;",
      loading: true
      url: process.env.VUE_APP_BASE_API + "/doc.html"
    };
  },
  mounted: function() {
    setTimeout(() => {
      this.loading = false;
    }, 230);
    const that = this;
    window.onresize = function temp() {
      that.height = document.documentElement.clientHeight - 94.5 + "px;";
    };
  }
};
</script>
ruoyi-ui/vue.config.js
@@ -1,12 +1,11 @@
'use strict'
const path = require('path')
const defaultSettings = require('./src/settings.js')
function resolve(dir) {
  return path.join(__dirname, dir)
}
const name = defaultSettings.title || 'RuoYi-Vue-Plus后台管理系统' // æ ‡é¢˜
const name = process.env.VUE_APP_TITLE || 'RuoYi-Vue-Plus后台管理系统' // ç½‘页标题
const port = process.env.port || process.env.npm_config_port || 80 // ç«¯å£
ry.sh
@@ -3,7 +3,7 @@
# ./ry.sh start å¯åЍ
# ./ry.sh stop åœæ­¢
# ./ry.sh restart é‡å¯
# ./ry.sh start çŠ¶æ€
# ./ry.sh status çŠ¶æ€
AppName=ruoyi-admin.jar
# JVM参数