From 41957619b51b20b0222a386f6c054083bd9a37fc Mon Sep 17 00:00:00 2001 From: liujie <1793218484@qq.com> Date: 星期一, 26 五月 2025 15:41:03 +0800 Subject: [PATCH] init --- ruoyi-admin/src/main/resources/application-test.yml | 237 ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java | 16 ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java | 76 ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java | 16 ruoyi-system/src/main/java/com/ruoyi/system/query/SysUserQuery.java | 26 ruoyi-applet/src/main/resources/META-INF/spring-devtools.properties | 1 ruoyi-common/src/main/java/com/ruoyi/common/core/utils/HttpUtils.java | 311 ruoyi-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java | 570 ruoyi-admin/src/main/java/com/ruoyi/web/controller/interceptor/MybatisInterceptor.java | 119 ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java | 73 ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/UUID.java | 484 ruoyi-system/src/main/resources/mapper/system/TbPermitMapper.xml | 15 ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java | 75 ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm | 590 ruoyi-generator/pom.xml | 40 ruoyi-system/src/main/java/com/ruoyi/system/model/TbAccountDetail.java | 66 ruoyi-generator/src/main/resources/vm/java/sub-domain.java.vm | 76 ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessType.java | 63 ruoyi-admin/src/main/resources/application-prod.yml | 237 ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java | 78 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbOpeningBankServiceImpl.java | 21 ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java | 121 ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml | 117 ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobLogController.java | 92 ruoyi-system/src/main/java/com/ruoyi/system/model/TbPermit.java | 56 ruoyi-system/src/main/java/com/ruoyi/system/query/SysRoleQuery.java | 17 ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java | 163 ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserException.java | 18 pom.xml | 233 ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java | 122 ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPostService.java | 99 ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/CompanyController.java | 41 ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java | 89 ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java | 61 ruoyi-system/src/main/java/com/ruoyi/system/service/TbOpeningBankService.java | 17 ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessModuleUpdateBO.java | 19 ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml | 89 ruoyi-common/src/main/java/com/ruoyi/common/enums/UserStatus.java | 30 ruoyi-common/src/main/java/com/ruoyi/common/utils/OrderNos.java | 164 ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/BaiDuApi.java | 180 ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java | 387 ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excels.java | 18 ruoyi-system/src/main/java/com/ruoyi/system/task/exceptions/TimeException.java | 37 ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java | 168 ruoyi-common/src/main/java/com/ruoyi/common/utils/WxAppletTools.java | 77 ruoyi-common/src/main/java/com/ruoyi/common/utils/IDCardUtils.java | 18 ruoyi-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java | 144 ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/TestController.java | 177 ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java | 28 ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java | 205 ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java | 166 ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java | 94 ruoyi-common/src/main/java/com/ruoyi/common/enums/BillTypeEnum.java | 48 ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java | 89 ruoyi-generator/src/main/resources/vm/sql/sql.vm | 22 ruoyi-applet/src/main/resources/mybatis-config.xml | 25 ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java | 120 ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java | 29 ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java | 343 ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableColumnServiceImpl.java | 68 ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobLogServiceImpl.java | 87 ruoyi-common/src/main/java/com/ruoyi/common/enums/ProcessCategoryEnum.java | 58 ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java | 69 ruoyi-applet/src/main/java/com/ruoyi/web/controller/common/CommonController.java | 163 ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxException.java | 55 ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java | 42 ruoyi-applet/src/main/java/com/ruoyi/web/core/config/MybatisPlusConfig.java | 51 ruoyi-system/src/main/java/com/ruoyi/system/query/CompanyListQuery.java | 45 ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/messageController.java | 101 ruoyi-common/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java | 80 ruoyi-system/src/main/java/com/ruoyi/system/vo/InviteUserListVo.java | 21 ruoyi-common/src/main/java/com/ruoyi/common/utils/MultipartFileUtil.java | 222 ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityInitializer.java | 34 ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java | 46 ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/WxLoginController.java | 148 ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysPostController.java | 123 ruoyi-applet/src/main/resources/application.yml | 4 ruoyi-generator/src/main/resources/vm/java/service.java.vm | 61 ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJob.java | 169 ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleUtils.java | 141 ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessStatus.java | 20 ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserRole.java | 46 ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileException.java | 19 ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/Watermark.java | 9 ruoyi-system/src/main/resources/mapper/system/TbBankMapper.xml | 15 ruoyi-applet/src/main/resources/logback.xml | 93 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbPermitServiceImpl.java | 21 ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileUploadException.java | 61 ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserPost.java | 46 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/AjaxResult.java | 216 ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java | 117 ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java | 130 ruoyi-common/src/main/java/com/ruoyi/common/utils/LogUtils.java | 18 ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java | 106 ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java | 174 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbBankServiceImpl.java | 21 ruoyi-system/src/main/java/com/ruoyi/system/importExcel/TCheckImportExcel.java | 20 ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java | 31 ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java | 402 ruoyi-quartz/src/main/java/com/ruoyi/quartz/config/ScheduleConfig.java | 57 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java | 178 ruoyi-admin/src/main/java/com/ruoyi/web/core/config/MybatisPlusConfig.java | 51 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbUserServiceImpl.java | 21 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUserApplet.java | 263 ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml | 105 ruoyi-system/src/main/resources/mapper/system/TbAddressMapper.xml | 22 ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TestController.java | 177 ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java | 110 ruoyi-generator/src/main/resources/vm/java/domain.java.vm | 105 ruoyi-system/src/main/java/com/ruoyi/system/vo/SysOperLogVO.java | 24 ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessTaskListBO.java | 129 ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/NumberToChineseUtils.java | 129 ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessStartBO.java | 57 ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleMenu.java | 46 ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java | 46 ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbCompanyMapper.java | 17 ruoyi-system/src/main/java/com/ruoyi/system/task/base/QuartzManager.java | 128 ruoyi-applet/pom.xml | 175 ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/BaiDuApi.java | 180 ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbPermitMapper.java | 17 ruoyi-quartz/src/main/resources/mapper/quartz/SysJobLogMapper.xml | 93 ruoyi-system/src/main/java/com/ruoyi/system/code/SubmitTemplateReg.java | 40 ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml | 102 ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java | 73 ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java | 52 ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java | 82 ruoyi-common/src/main/java/com/ruoyi/common/utils/Threads.java | 99 ruoyi-common/src/main/java/com/ruoyi/common/constant/WxConstant.java | 22 ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java | 138 ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java | 148 ruoyi-system/src/main/resources/mapper/system/TbAccountDetailMapper.xml | 15 ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java | 101 ruoyi-framework/src/main/java/com/ruoyi/framework/config/CaptchaConfig.java | 83 ruoyi-common/src/main/java/com/ruoyi/common/constant/DictConstants.java | 50 ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java | 27 ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java | 56 ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserOnlineService.java | 48 ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml | 127 ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java | 261 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbAccountDetailServiceImpl.java | 21 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java | 96 ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/ImportExcelUtil.java | 39 ruoyi-generator/src/main/resources/vm/xml/mapper.xml.vm | 135 ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java | 177 ruoyi-common/src/main/java/com/ruoyi/common/constant/AmountConstant.java | 15 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java | 279 ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml | 234 ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java | 24 ruoyi-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java | 125 ruoyi-system/src/main/resources/mapper/system/SysLogininforMapper.xml | 57 ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java | 111 ruoyi-common/src/main/java/com/ruoyi/common/xss/Xss.java | 27 ruoyi-system/src/main/resources/mapper/system/TbOpeningBankMapper.xml | 15 ruoyi-system/src/main/resources/mapper/system/TbWithdrawalMapper.xml | 16 ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java | 360 ruoyi-common/src/main/java/com/ruoyi/common/core/exception/ServiceException.java | 74 ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/UserAccountController.java | 160 ruoyi-common/src/main/java/com/ruoyi/common/exception/GlobalException.java | 58 ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml | 356 ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/UserAddressController.java | 85 ruoyi-common/src/main/java/com/ruoyi/common/core/text/CharsetKit.java | 86 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java | 11 ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessCreateBO.java | 21 ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxCache.java | 117 ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java | 373 ruoyi-common/src/main/java/com/ruoyi/common/exception/ServiceException.java | 74 ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java | 126 ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java | 165 ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java | 126 ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java | 63 ruoyi-applet/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java | 113 ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java | 39 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbCompanyServiceImpl.java | 21 ruoyi-applet/src/main/java/com/ruoyi/web/aspect/StateAspect.java | 23 ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java | 124 ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java | 454 ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java | 100 ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java | 28 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/TTenantResp.java | 69 ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resp/AccessTokenRespBody.java | 28 ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml | 34 ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java | 59 ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm | 505 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java | 79 ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessTemplatePageBO.java | 12 ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java | 129 ruoyi-system/src/main/java/com/ruoyi/system/dto/SaveUserBankDto.java | 20 ruoyi-common/src/main/java/com/ruoyi/common/exception/state/StateErrorCode.java | 21 ruoyi-common/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java | 93 ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml | 202 ruoyi-system/src/main/java/com/ruoyi/system/service/TbCompanyService.java | 17 ruoyi-common/src/main/java/com/ruoyi/common/exception/DemoModeException.java | 15 ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java | 232 ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/JobInvokeUtil.java | 182 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java | 159 ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java | 372 ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java | 67 ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java | 70 ruoyi-common/src/main/java/com/ruoyi/common/core/page/PageDomain.java | 101 ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/TaskUtil.java | 83 ruoyi-common/src/main/java/com/ruoyi/common/config/FileUploadConfig.java | 23 ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java | 38 ruoyi-generator/src/main/java/com/ruoyi/generator/config/GenConfig.java | 73 ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/QRCodeUtil.java | 124 ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java | 42 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java | 232 ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm | 474 ruoyi-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java | 40 ruoyi-admin/src/main/java/com/ruoyi/web/core/config/FileUploaderConfig.java | 67 ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/QRCodeUtil.java | 124 ruoyi-generator/src/main/resources/generator.yml | 10 ruoyi-generator/src/main/resources/vm/js/api.js.vm | 44 README.md | 2 ruoyi-admin/src/main/resources/banner.txt | 24 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseModel.java | 107 ruoyi-common/src/main/java/com/ruoyi/common/core/text/StrFormatter.java | 92 ruoyi-common/src/main/java/com/ruoyi/common/utils/Arith.java | 114 ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java | 45 ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/AbstractQuartzJob.java | 107 ruoyi-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java | 50 ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java | 50 ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java | 163 ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/WxLoginController.java | 141 ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java | 1000 + ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java | 284 ruoyi-system/src/main/java/com/ruoyi/system/query/UserMessageQuery.java | 13 ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java | 235 ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/HttpClientUtil.java | 79 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java | 252 ruoyi-system/src/main/java/com/ruoyi/system/model/TbOpeningBank.java | 53 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java | 546 ruoyi-system/src/main/java/com/ruoyi/system/service/TbPermitService.java | 17 ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbWithdrawalMapper.java | 17 ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/ImportExcelUtil.java | 39 ruoyi-system/src/main/java/com/ruoyi/system/vo/RegionVo.java | 35 ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbOpeningBankMapper.java | 17 ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resp/RespBody.java | 19 ruoyi-common/src/main/java/com/ruoyi/common/basic/PageInfo.java | 70 ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java | 614 ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java | 30 ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml | 122 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbRegionServiceImpl.java | 82 ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java | 115 ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java | 253 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/TimeRangeQueryBody.java | 45 ruoyi-system/src/main/java/com/ruoyi/system/model/TbCompany.java | 220 ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java | 131 ruoyi-common/src/main/java/com/ruoyi/common/utils/SmsUtil.java | 79 ruoyi-framework/src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java | 68 ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java | 69 ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java | 16 ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java | 53 ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/TencentCosUtil.java | 187 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java | 569 ruoyi-common/src/main/java/com/ruoyi/common/core/controller/FileController.java | 140 ruoyi-common/src/main/java/com/ruoyi/common/redis/service/RedisService.java | 273 ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbAddressMapper.java | 17 ruoyi-admin/src/main/resources/mybatis-config.xml | 25 ruoyi-common/src/main/java/com/ruoyi/common/config/MailProperties.java | 32 ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java | 214 ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java | 218 ruoyi-admin/src/main/java/com/ruoyi/RuoYiServletInitializer.java | 18 ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java | 110 ruoyi-quartz/pom.xml | 40 ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/AppletUserDecodeData.java | 52 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java | 203 ruoyi-common/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java | 167 ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java | 33 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java | 111 ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java | 83 ruoyi-generator/src/main/resources/vm/java/controller.java.vm | 115 ruoyi-applet/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java | 52 ruoyi-system/src/main/java/com/ruoyi/system/model/TbAddress.java | 108 ruoyi-common/src/main/java/com/ruoyi/common/annotation/Anonymous.java | 19 ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbBankMapper.java | 17 ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java | 56 ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/MsgUtils.java | 70 ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java | 26 ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessUpdateBO.java | 22 ruoyi-applet/src/main/resources/application-test.yml | 230 ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.java | 23 ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbRegionMapper.java | 17 ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java | 274 ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java | 84 ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java | 113 ruoyi-common/src/main/java/com/ruoyi/common/exception/user/BlackListException.java | 16 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java | 339 ruoyi-common/src/main/java/com/ruoyi/common/enums/TaskEventType.java | 146 ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobService.java | 102 ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java | 322 ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java | 521 ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java | 126 ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/Seq.java | 86 ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/model/WeixinProperties.java | 79 ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java | 1679 + ruoyi-common/src/main/java/com/ruoyi/common/constant/HttpStatus.java | 94 ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessAgreeBO.java | 36 ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxAppletTools.java | 123 ruoyi-framework/pom.xml | 64 ruoyi-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java | 52 ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxCacheTemplate.java | 34 ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java | 228 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java | 661 ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzJobExecution.java | 19 ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java | 132 ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/SHA1.java | 36 ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/MsgCodeUtil.java | 61 ruoyi-system/src/main/java/com/ruoyi/system/service/TbAccountDetailService.java | 18 ruoyi-system/src/main/java/com/ruoyi/system/model/TbUser.java | 98 ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/AppletPhoneEncrypteData.java | 19 ruoyi-system/src/main/java/com/ruoyi/system/service/TbUserService.java | 17 ruoyi-applet/src/main/resources/i18n/messages.properties | 38 ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java | 16 ruoyi-system/src/main/resources/mapper/system/TbUserMapper.xml | 7 ruoyi-system/src/main/java/com/ruoyi/system/task/jobs/StateProcessJob.java | 48 ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java | 135 ruoyi-system/src/main/java/com/ruoyi/system/model/TbRegion.java | 54 ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java | 72 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java | 389 ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java | 16 ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java | 257 ruoyi-system/src/main/java/com/ruoyi/system/vo/UserAccountVo.java | 21 ruoyi-system/src/main/java/com/ruoyi/system/vo/UserInfoVo.java | 174 ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java | 115 ruoyi-admin/src/main/java/com/ruoyi/web/core/config/DataUpdateHandlerConfig.java | 61 ruoyi-common/src/main/java/com/ruoyi/common/core/utils/Constants.java | 143 ruoyi-applet/src/main/java/com/ruoyi/web/controller/interceptor/MybatisConfiguration.java | 17 ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxUtils.java | 175 ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java | 58 ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java | 68 ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/CronUtils.java | 63 ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml | 53 ruoyi-common/src/main/java/com/ruoyi/common/utils/WebUtils.java | 170 ruoyi-system/src/main/resources/mapper/system/TbRegionMapper.xml | 15 ruoyi-common/src/main/java/com/ruoyi/common/utils/TimeConverter.java | 19 ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java | 66 ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/MyFileUtil.java | 128 ruoyi-system/src/main/java/com/ruoyi/system/importExcel/TBankFlowImportExcel.java | 51 ruoyi-generator/src/main/resources/vm/vue/v3/readme.txt | 1 ruoyi-system/src/main/java/com/ruoyi/system/bo/DeployBO.java | 15 ruoyi-system/src/main/java/com/ruoyi/system/model/TbMessage.java | 54 ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java | 101 ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordTemplateProcessor.java | 87 ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java | 157 ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java | 78 ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/MsgCodeUtil.java | 61 ruoyi-common/src/main/java/com/ruoyi/common/resp/AccessTokenRespBody.java | 28 ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java | 89 ruoyi-system/src/main/resources/mapper/system/TbCompanyMapper.xml | 49 ruoyi-applet/src/main/java/com/ruoyi/web/core/config/DataUpdateHandlerConfig.java | 61 ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java | 64 ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java | 52 ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java | 146 ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml | 159 ruoyi-applet/src/main/resources/application-prod.yml | 230 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java | 174 ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/UserController.java | 105 ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysUserController.java | 356 ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java | 255 ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java | 235 ruoyi-system/src/main/java/com/ruoyi/system/task/base/AbstractJob.java | 34 ruoyi-common/src/main/java/com/ruoyi/common/config/WxConfig.java | 37 ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableSupport.java | 56 ruoyi-common/src/main/java/com/ruoyi/common/exception/job/TaskException.java | 34 ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java | 158 ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJobLog.java | 154 ruoyi-admin/src/main/resources/mybatis/mybatis-config.xml | 20 ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java | 47 ruoyi-common/src/main/java/com/ruoyi/common/enums/HttpMethod.java | 36 ruoyi-applet/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java | 125 ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/AppletUserEncrypteData.java | 20 ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java | 168 ruoyi-common/src/main/java/com/ruoyi/common/utils/MessageUtils.java | 26 ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/IdUtils.java | 49 ruoyi-system/src/main/java/com/ruoyi/system/service/TbWithdrawalService.java | 17 ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/MsgUtils.java | 70 ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java | 267 ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java | 91 ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java | 118 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java | 94 ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm | 169 ruoyi-system/src/main/java/com/ruoyi/system/domain/SysConfig.java | 111 ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java | 55 ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java | 136 ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java | 86 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/R.java | 115 ruoyi-common/src/main/java/com/ruoyi/common/enums/OperatorType.java | 24 ruoyi-system/src/main/java/com/ruoyi/system/domain/SysNotice.java | 102 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbAddressServiceImpl.java | 21 ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java | 34 ruoyi-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java | 39 ruoyi-common/src/main/java/com/ruoyi/common/utils/VideoUtil.java | 69 ruoyi-applet/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java | 77 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BasePage.java | 50 ruoyi-common/src/main/java/com/ruoyi/common/utils/CodeGenerateUtils.java | 76 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java | 223 ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbUserMapper.java | 17 ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java | 132 ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzDisallowConcurrentExecution.java | 21 ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Base64.java | 291 ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.java | 94 ruoyi-generator/src/main/resources/vm/vue/index.vue.vm | 602 ruoyi-admin/src/main/resources/application.yml | 4 ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordPdfGenerator.java | 106 ruoyi-system/src/main/resources/mapper/system/TbMessageMapper.xml | 14 ruoyi-applet/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java | 94 ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobLogService.java | 56 ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WebUtils.java | 48 ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java | 409 ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java | 114 ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java | 60 ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessRefuseBO.java | 30 ruoyi-admin/src/main/java/com/ruoyi/web/controller/interceptor/MybatisConfiguration.java | 17 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java | 92 ruoyi-system/src/main/java/com/ruoyi/system/service/TbAddressService.java | 17 ruoyi-common/src/main/java/com/ruoyi/common/utils/TencentMailUtil.java | 203 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java | 259 ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/Server.java | 240 ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java | 291 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java | 65 ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java | 16 ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/HttpClientUtil.java | 79 ruoyi-applet/src/main/resources/banner.txt | 24 ruoyi-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java | 40 ruoyi-common/src/main/java/com/ruoyi/common/enums/SubmitStatusEnum.java | 40 ruoyi-system/src/main/java/com/ruoyi/system/task/utils/TaskUtil.java | 13 ruoyi-quartz/src/main/resources/mapper/quartz/SysJobMapper.xml | 111 ruoyi-common/src/main/java/com/ruoyi/common/redis/configure/FastJson2JsonRedisSerializer.java | 50 ruoyi-system/src/main/java/com/ruoyi/system/service/TbMessageService.java | 17 ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java | 29 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbWithdrawalServiceImpl.java | 21 ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java | 45 ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java | 60 ruoyi-system/src/main/java/com/ruoyi/system/vo/SysUserVO.java | 23 ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java | 55 ruoyi-applet/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java | 81 ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java | 19 ruoyi-system/pom.xml | 93 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java | 77 ruoyi-applet/src/main/java/com/ruoyi/RuoYiAppletApplication.java | 65 ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java | 74 ruoyi-system/src/main/java/com/ruoyi/system/query/SysOperLogQuery.java | 13 ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml | 34 ruoyi-common/src/main/java/com/ruoyi/common/enums/UpdateTypeEnum.java | 58 ruoyi-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java | 97 ruoyi-admin/src/main/resources/META-INF/spring-devtools.properties | 1 ruoyi-generator/src/main/resources/vm/java/mapper.java.vm | 91 ruoyi-system/src/main/java/com/ruoyi/system/model/TbBank.java | 58 ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java | 34 ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleDept.java | 46 ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resq/Code2SessionResqBody.java | 21 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java | 266 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java | 37 ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml | 127 ruoyi-common/src/main/java/com/ruoyi/common/config/SmsProperties.java | 38 ruoyi-system/src/main/java/com/ruoyi/system/service/TbBankService.java | 16 ruoyi-system/src/main/java/com/ruoyi/system/service/TbRegionService.java | 25 ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java | 186 ruoyi-system/src/main/java/com/ruoyi/system/export/OpticalInspectionExport.java | 41 ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java | 56 ruoyi-system/src/main/java/com/ruoyi/system/model/TbWithdrawal.java | 65 ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java | 83 ruoyi-system/src/main/java/com/ruoyi/system/query/UserAccountDetailQuery.java | 13 ruoyi-applet/src/main/resources/mybatis/mybatis-config.xml | 20 ruoyi-system/src/main/java/com/ruoyi/system/task/utils/SpringContextsUtil.java | 37 ruoyi-framework/src/main/java/com/ruoyi/framework/config/ServerConfig.java | 32 ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbAccountDetailMapper.java | 17 ruoyi-system/src/main/java/com/ruoyi/system/export/ContractExport.java | 51 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbMessageServiceImpl.java | 21 ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java | 124 ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserNotExistsException.java | 16 ruoyi-system/src/main/java/com/ruoyi/system/task/base/TimeJobType.java | 28 ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java | 121 ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobLogMapper.java | 64 ruoyi-system/src/main/java/com/ruoyi/system/domain/SysCache.java | 81 ruoyi-common/src/main/java/com/ruoyi/common/utils/NumberUtil.java | 30 ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbMessageMapper.java | 17 ruoyi-admin/src/main/resources/i18n/messages.properties | 38 ruoyi-admin/src/main/resources/logback.xml | 93 ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java | 136 ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java | 60 ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml | 252 ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java | 135 ruoyi-common/src/main/java/com/ruoyi/common/enums/DisabledEnum.java | 57 ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java | 187 ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobMapper.java | 67 ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java | 83 ruoyi-common/src/main/java/com/ruoyi/common/annotation/Log.java | 51 ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java | 98 ruoyi-applet/src/main/java/com/ruoyi/web/controller/interceptor/MybatisInterceptor.java | 121 ruoyi-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java | 132 ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml | 34 ruoyi-admin/src/main/java/com/ruoyi/web/aspect/StateAspect.java | 45 ruoyi-common/src/main/java/com/ruoyi/common/enums/LimitType.java | 20 ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java | 27 ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java | 60 ruoyi-admin/pom.xml | 190 ruoyi-common/src/main/java/com/ruoyi/common/enums/StateProcessActionEnum.java | 28 ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java | 185 ruoyi-applet/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java | 26 ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableColumnService.java | 44 ruoyi-applet/src/main/java/com/ruoyi/RuoYiServletInitializer.java | 18 ruoyi-system/src/main/java/com/ruoyi/system/dto/UserWithdrawalDto.java | 18 ruoyi-common/src/main/java/com/ruoyi/common/xss/XssValidator.java | 34 ruoyi-common/pom.xml | 211 ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java | 102 ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java | 38 ruoyi-common/src/main/java/com/ruoyi/common/exception/UtilException.java | 26 ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java | 76 ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxJsonUtils.java | 109 ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java | 83 ruoyi-common/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java | 24 ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java | 56 ruoyi-system/src/main/java/com/ruoyi/system/domain/SysPost.java | 124 ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resp/Code2SessionRespBody.java | 29 515 files changed, 54,264 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ebef626..139597f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,2 @@ -## ZhengShengXin -证省心 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..06b9445 --- /dev/null +++ b/pom.xml @@ -0,0 +1,233 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>com.ruoyi</groupId> + <artifactId>zhengshengxin</artifactId> + <version>3.8.6</version> + + <name>ruoyi</name> + <url>http://www.ruoyi.vip</url> + <description>若依管理系统</description> + + <properties> + <ruoyi.version>3.8.6</ruoyi.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.16</druid.version> + <bitwalker.version>1.21</bitwalker.version> + <swagger.version>3.0.0</swagger.version> + <kaptcha.version>2.3.3</kaptcha.version> + <pagehelper.boot.version>1.4.6</pagehelper.boot.version> + <fastjson.version>2.0.39</fastjson.version> + <oshi.version>6.4.4</oshi.version> + <commons.io.version>2.13.0</commons.io.version> + <commons.collections.version>3.2.2</commons.collections.version> + <poi.version>4.1.2</poi.version> + <velocity.version>2.3</velocity.version> + <jwt.version>0.9.1</jwt.version> + </properties> + + <!-- 依赖声明 --> + <dependencyManagement> + <dependencies> + + <!-- SpringBoot的依赖配置--> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-dependencies</artifactId> + <version>2.5.15</version> + <type>pom</type> + <scope>import</scope> + </dependency> + + <!-- 阿里数据库连接池 --> + <dependency> + <groupId>com.alibaba</groupId> + <artifactId>druid-spring-boot-starter</artifactId> + <version>${druid.version}</version> + </dependency> + + <!-- 解析客户端操作系统、浏览器等 --> + <dependency> + <groupId>eu.bitwalker</groupId> + <artifactId>UserAgentUtils</artifactId> + <version>${bitwalker.version}</version> + </dependency> + + <!-- 获取系统信息 --> + <dependency> + <groupId>com.github.oshi</groupId> + <artifactId>oshi-core</artifactId> + <version>${oshi.version}</version> + </dependency> + + <!-- Swagger3依赖 --> + <dependency> + <groupId>io.springfox</groupId> + <artifactId>springfox-boot-starter</artifactId> + <version>${swagger.version}</version> +<!-- <exclusions>--> +<!-- <exclusion>--> +<!-- <groupId>io.swagger</groupId>--> +<!-- <artifactId>swagger-models</artifactId>--> +<!-- </exclusion>--> +<!-- </exclusions>--> + </dependency> + + <!-- io常用工具类 --> + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + <version>${commons.io.version}</version> + </dependency> + + <!-- excel工具 --> + <dependency> + <groupId>org.apache.poi</groupId> + <artifactId>poi-ooxml</artifactId> + <version>${poi.version}</version> + </dependency> + + + <dependency> + <groupId>org.apache.poi</groupId> + <artifactId>poi</artifactId> + <version>${poi.version}</version> + </dependency> + + <dependency> + <groupId>org.apache.poi</groupId> + <artifactId>poi-ooxml-schemas</artifactId> + <version>${poi.version}</version> + </dependency> + + <!-- velocity代码生成使用模板 --> + <dependency> + <groupId>org.apache.velocity</groupId> + <artifactId>velocity-engine-core</artifactId> + <version>${velocity.version}</version> + </dependency> + + <!-- collections工具类 --> + <dependency> + <groupId>commons-collections</groupId> + <artifactId>commons-collections</artifactId> + <version>${commons.collections.version}</version> + </dependency> + + <!-- 阿里JSON解析器 --> + <dependency> + <groupId>com.alibaba.fastjson2</groupId> + <artifactId>fastjson2</artifactId> + <version>${fastjson.version}</version> + </dependency> + + <!-- Token生成与解析--> + <dependency> + <groupId>io.jsonwebtoken</groupId> + <artifactId>jjwt</artifactId> + <version>${jwt.version}</version> + </dependency> + + <!-- 验证码 --> + <dependency> + <groupId>pro.fessional</groupId> + <artifactId>kaptcha</artifactId> + <version>${kaptcha.version}</version> + </dependency> + + <!-- 定时任务--> + <dependency> + <groupId>com.ruoyi</groupId> + <artifactId>ruoyi-quartz</artifactId> + <version>${ruoyi.version}</version> + </dependency> + + <!-- 代码生成--> + <dependency> + <groupId>com.ruoyi</groupId> + <artifactId>ruoyi-generator</artifactId> + <version>${ruoyi.version}</version> + </dependency> + + <!-- 核心模块--> + <dependency> + <groupId>com.ruoyi</groupId> + <artifactId>ruoyi-framework</artifactId> + <version>${ruoyi.version}</version> + </dependency> + + <!-- 系统模块--> + <dependency> + <groupId>com.ruoyi</groupId> + <artifactId>ruoyi-system</artifactId> + <version>${ruoyi.version}</version> + </dependency> + + <!-- 通用工具--> + <dependency> + <groupId>com.ruoyi</groupId> + <artifactId>ruoyi-common</artifactId> + <version>${ruoyi.version}</version> + </dependency> + + </dependencies> + </dependencyManagement> + + <modules> + <module>ruoyi-admin</module> + <module>ruoyi-framework</module> + <module>ruoyi-system</module> + <module>ruoyi-quartz</module> + <module>ruoyi-generator</module> + <module>ruoyi-common</module> + <module>ruoyi-applet</module> + </modules> + <packaging>pom</packaging> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.1</version> + <configuration> + <source>${java.version}</source> + <target>${java.version}</target> + <encoding>${project.build.sourceEncoding}</encoding> + </configuration> + </plugin> + </plugins> + </build> + + <repositories> + <repository> + <id>public</id> + <name>aliyun nexus</name> + <url>https://maven.aliyun.com/repository/public</url> + <releases> + <enabled>true</enabled> + </releases> + </repository> + </repositories> + + <pluginRepositories> + <pluginRepository> + <id>public</id> + <name>aliyun nexus</name> + <url>https://maven.aliyun.com/repository/public</url> + <releases> + <enabled>true</enabled> + </releases> + <snapshots> + <enabled>false</enabled> + </snapshots> + </pluginRepository> + </pluginRepositories> + +</project> \ No newline at end of file diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml new file mode 100644 index 0000000..e821f14 --- /dev/null +++ b/ruoyi-admin/pom.xml @@ -0,0 +1,190 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>zhengshengxin</artifactId> + <groupId>com.ruoyi</groupId> + <version>3.8.6</version> + </parent> + <modelVersion>4.0.0</modelVersion> + <packaging>jar</packaging> + <artifactId>ruoyi-admin</artifactId> + + <description> + web服务入口 + </description> + + <dependencies> + <dependency> + <groupId>com.aliyun.oss</groupId> + <artifactId>aliyun-sdk-oss</artifactId> + <version>3.8.0</version> + </dependency> +<!-- <dependency>--> +<!-- <groupId>com.google.guava</groupId>--> +<!-- <artifactId>guava</artifactId>--> +<!-- <version>31.1-jre</version> <!– 请根据需要选择合适的版本 –>--> +<!-- </dependency>--> + <!-- spring-boot-devtools --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-devtools</artifactId> + <optional>true</optional> <!-- 表示依赖不会传递 --> + </dependency> + + <!-- swagger3--> + <dependency> + <groupId>io.springfox</groupId> + <artifactId>springfox-boot-starter</artifactId> + </dependency> + +<!-- <dependency>--> +<!-- <groupId>com.github.xiaoymin</groupId>--> +<!-- <artifactId>swagger-bootstrap-ui</artifactId>--> +<!-- <version>1.9.6</version>--> +<!-- </dependency>--> + + + <!-- 防止进入swagger页面报类型转换错误,排除3.0.0中的引用,手动增加1.6.2版本 --> + <dependency> + <groupId>io.swagger</groupId> + <artifactId>swagger-models</artifactId> + <version>1.6.2</version> + </dependency> + + <!-- Mysql驱动包 --> + <dependency> + <groupId>mysql</groupId> + <artifactId>mysql-connector-java</artifactId> + </dependency> + + <!-- 核心模块--> + <dependency> + <groupId>com.ruoyi</groupId> + <artifactId>ruoyi-framework</artifactId> +<!-- <exclusions>--> +<!-- <exclusion>--> +<!-- <groupId>com.github.pagehelper</groupId>--> +<!-- <artifactId>pagehelper-spring-boot-starter</artifactId>--> +<!-- </exclusion>--> +<!-- </exclusions>--> + </dependency> + + <!-- 定时任务--> + <dependency> + <groupId>com.ruoyi</groupId> + <artifactId>ruoyi-quartz</artifactId> +<!-- <exclusions>--> +<!-- <exclusion>--> +<!-- <groupId>com.github.pagehelper</groupId>--> +<!-- <artifactId>pagehelper-spring-boot-starter</artifactId>--> +<!-- </exclusion>--> +<!-- </exclusions>--> + </dependency> + + <!-- 代码生成--> + <dependency> + <groupId>com.ruoyi</groupId> + <artifactId>ruoyi-generator</artifactId> +<!-- <exclusions>--> +<!-- <exclusion>--> +<!-- <groupId>com.github.pagehelper</groupId>--> +<!-- <artifactId>pagehelper-spring-boot-starter</artifactId>--> +<!-- </exclusion>--> +<!-- </exclusions>--> + </dependency> + + <!-- zxing生成二维码 --> + <dependency> + <groupId>com.google.zxing</groupId> + <artifactId>core</artifactId> + <version>3.3.3</version> + </dependency> + + <dependency> + <groupId>com.google.zxing</groupId> + <artifactId>javase</artifactId> + <version>3.3.3</version> + </dependency> + + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-test</artifactId> + <version>5.1.3.RELEASE</version> + </dependency> +<!-- <dependency>--> +<!-- <groupId>org.apache.httpcomponents</groupId>--> +<!-- <artifactId>httpcore</artifactId>--> +<!-- <version>4.3.2</version>--> +<!-- </dependency>--> + + <dependency> + <groupId>com.squareup.okhttp3</groupId> + <artifactId>okhttp</artifactId> + <version>4.9.3</version> + </dependency> + + <dependency> + <groupId>com.alibaba</groupId> + <artifactId>fastjson</artifactId> + <version>1.2.78</version> + </dependency> + + <!-- easypoi --> + <dependency> + <groupId>cn.afterturn</groupId> + <artifactId>easypoi-base</artifactId> + <version>4.3.0</version> + </dependency> + <dependency> + <groupId>cn.afterturn</groupId> + <artifactId>easypoi-web</artifactId> + <version>4.3.0</version> + </dependency> + <dependency> + <groupId>cn.afterturn</groupId> + <artifactId>easypoi-annotation</artifactId> + <version>4.3.0</version> + </dependency> + + <!-- 阿里云短信 --> + <dependency> + <groupId>com.aliyun</groupId> + <artifactId>dysmsapi20170525</artifactId> + <version>2.0.10</version> + </dependency> + + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + <version>2.5.15</version> + <configuration> + <fork>true</fork> <!-- 如果没有该配置,devtools不会生效 --> + </configuration> + <executions> + <execution> + <goals> + <goal>repackage</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-war-plugin</artifactId> + <version>3.1.0</version> + <configuration> + <failOnMissingWebXml>false</failOnMissingWebXml> + <warName>${project.artifactId}</warName> + </configuration> + </plugin> + </plugins> + <finalName>${project.artifactId}</finalName> + </build> + +</project> \ No newline at end of file diff --git a/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java new file mode 100644 index 0000000..558fe10 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java @@ -0,0 +1,69 @@ +package com.ruoyi; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.core.env.Environment; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.web.client.RestTemplate; +import springfox.documentation.oas.annotations.EnableOpenApi; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * 启动程序 + * + * @author ruoyi + */ +@Slf4j +@EnableOpenApi +@EnableCaching +@EnableScheduling//开启定时任务 +@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }) +public class RuoYiApplication +{ + public static void main(String[] args) { + + + ConfigurableApplicationContext application = SpringApplication.run(RuoYiApplication.class, args); + try { + + Environment env = application.getEnvironment(); + log.info("\n----------------------------------------------------------\n\t" + + "应用 '{}' 运行成功! 访问连接:\n\t" + + "Swagger文档: \t\thttp://{}:{}/doc.html\n" + + "----------------------------------------------------------", + env.getProperty("spring.application.name", "后台"), + InetAddress.getLocalHost().getHostAddress(), + env.getProperty("server.port", "8081")); + }catch (Exception e){ + e.printStackTrace(); + } + } + + /** + * 当不存在此 wxRestTemplate 使用此方法的bean注入 + * + * @return + */ + @Bean + @ConditionalOnMissingBean(name = "restTemplate") + public RestTemplate wxRestTemplate() { + //复杂构造函数的使用 + SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); + // 设置超时 + requestFactory.setConnectTimeout(6000); + requestFactory.setReadTimeout(6000); + RestTemplate restTemplate = new RestTemplate(); + restTemplate.setRequestFactory(requestFactory); + return restTemplate; + } + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/RuoYiServletInitializer.java b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiServletInitializer.java new file mode 100644 index 0000000..6de67dc --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiServletInitializer.java @@ -0,0 +1,18 @@ +package com.ruoyi; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +/** + * web容器中进行部署 + * + * @author ruoyi + */ +public class RuoYiServletInitializer extends SpringBootServletInitializer +{ + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) + { + return application.sources(RuoYiApplication.class); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/aspect/StateAspect.java b/ruoyi-admin/src/main/java/com/ruoyi/web/aspect/StateAspect.java new file mode 100644 index 0000000..20e5b87 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/aspect/StateAspect.java @@ -0,0 +1,45 @@ +package com.ruoyi.web.aspect; + +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.system.service.ISysRoleService; +import com.ruoyi.system.service.ISysUserService; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + + +@Aspect +@Component + +public class StateAspect { + + @Autowired + private ISysUserService sysUserService; + + @Autowired + private ISysRoleService roleService; + @Pointcut("execution(* com.ruoyi.web.controller.manage.*.*(..)) && !execution( * com.ruoyi.web.controller.manage.LoginController.*.*(..))") + public void state(){} + + @Before("state()") + public void isfrozen(){ + System.err.println("进入切面"); + LoginUser loginUser = SecurityUtils.getLoginUser(); + SysUser sysUser = sysUserService.selectUserById(loginUser.getUserId()); + if (sysUser.getStatus().equals("1")){ + throw new ServiceException("当前账号已停用"); + } + if (roleService.selectRoleByUserId(sysUser.getUserId()).getStatus()==1){ + throw new ServiceException("当前角色已停用"); + }; + + + } + + + } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/WxLoginController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/WxLoginController.java new file mode 100644 index 0000000..aa078a8 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/WxLoginController.java @@ -0,0 +1,141 @@ +package com.ruoyi.web.controller.api; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginBody; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.redis.service.RedisService; +import com.ruoyi.common.utils.NumberUtil; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.system.model.TbUser; +import com.ruoyi.system.service.TbUserService; +import com.ruoyi.system.utils.wx.body.resp.Code2SessionRespBody; +import com.ruoyi.system.utils.wx.body.resq.Code2SessionResqBody; +import com.ruoyi.system.utils.wx.model.WeixinProperties; +import com.ruoyi.system.utils.wx.pojo.AppletUserEncrypteData; +import com.ruoyi.system.utils.wx.tools.WxAppletTools; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * <p> + * 微信小程序登录 前端控制器 + * </p> + * + * @author xiaochen + * @since 2024-08-06 + */ +@Slf4j +@RestController +@RequestMapping("/wxLogin") +@Api(tags = "登录模块") +public class WxLoginController { + + @Autowired + private WeixinProperties wxConfig; + @Autowired + private RestTemplate wxRestTemplate; + @Resource + private RedisService redisService; + + @Resource + private TokenService tokenService; + + + @Autowired + private TbUserService tbUserService; + + + + /** + * 账号密码登录 + * + * @param loginBody 登录信息 + * @return 结果 + */ + @ApiOperation(value = "手机验证码登录",notes = "手机验证码登录") + @PostMapping("/login") + public AjaxResult login(@Valid @RequestBody LoginBody loginBody) + { + AjaxResult ajax = AjaxResult.success(); + // 生成令牌 + Object cacheObject = redisService.getCacheObject("login_" + loginBody.getPhone()); + if(cacheObject==null || !cacheObject.toString().equals(loginBody.getCode())){ + return AjaxResult.error("验证码错误"); + } + TbUser user = tbUserService.getOne(new LambdaQueryWrapper<TbUser>().eq(TbUser::getPhone, loginBody.getPhone()).eq(TbUser::getIsDelete, 0)); + if(user!=null && user.getStatus()==2){ + return AjaxResult.error("登录失败,当前账号已被冻结"); + } + if(user==null){ + TbUser tbUser = new TbUser(); + tbUser.setPhone(loginBody.getPhone()); + tbUser.setStatus(1); + tbUser.setUserName(loginBody.getPhone()); + tbUserService.save(tbUser); + user = tbUser; + } + LoginUser loginUser = new LoginUser(); + loginUser.setUserId(Long.valueOf(user.getId())); + loginUser.setUser(new SysUser()); + ajax.put(Constants.TOKEN, tokenService.createToken(loginUser)); + return ajax; + } + + @ApiOperation(value = "通过code获得openid,获取用户信息",tags = {"微信小程序登录"}) + @PostMapping("/openIdByJsCode") + public R<Map<String, Object>> openIdByJsCode(@RequestBody AppletUserEncrypteData data) { + log.info("<<<<<<<<换取openid开始<<<<<<<<:{}", data.getCode()); + WxAppletTools appletTools = new WxAppletTools(wxRestTemplate, wxConfig, redisService); + Code2SessionRespBody body = appletTools.getOpenIdByJscode2session(new Code2SessionResqBody().build(data.getCode())); + String openid = body.getOpenid(); + String sessionKey = body.getSessionKey(); + return R.ok(); + } + + + + @ApiOperation(value = "发送验证码",tags = {"发送验证码"}) + @PostMapping("/sendCode") + public R<?> sendCode(String phone) { + if (StringUtils.isBlank(phone)) { + return R.fail("手机号不能为空"); + } + String code = NumberUtil.getRandomInteger(6); + redisService.setCacheObject("login_"+phone, code, 5L, TimeUnit.MINUTES); + // 发送验证码 + + + return R.ok(); + } + + + @ApiOperation(value = "获取协议",tags = {"获取协议 1用户 2隐私"}) + @PostMapping("/getAgreement") + public R<?> getAgreement(Integer type) { + + return R.ok(); + } + + + + + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java new file mode 100644 index 0000000..d2d6e8c --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java @@ -0,0 +1,94 @@ +package com.ruoyi.web.controller.common; + +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import javax.annotation.Resource; +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.FastByteArrayOutputStream; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import com.google.code.kaptcha.Producer; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.sign.Base64; +import com.ruoyi.common.utils.uuid.IdUtils; +import com.ruoyi.system.service.ISysConfigService; + +/** + * 验证码操作处理 + * + * @author ruoyi + */ +@RestController +public class CaptchaController +{ + @Resource(name = "captchaProducer") + private Producer captchaProducer; + + @Resource(name = "captchaProducerMath") + private Producer captchaProducerMath; + + @Autowired + private RedisCache redisCache; + + @Autowired + private ISysConfigService configService; + /** + * 生成验证码 + */ + @GetMapping("/captchaImage") + public AjaxResult getCode(HttpServletResponse response) throws IOException + { + AjaxResult ajax = AjaxResult.success(); + boolean captchaEnabled = configService.selectCaptchaEnabled(); + ajax.put("captchaEnabled", captchaEnabled); + if (!captchaEnabled) + { + return ajax; + } + + // 保存验证码信息 + String uuid = IdUtils.simpleUUID(); + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid; + + String capStr = null, code = null; + BufferedImage image = null; + + // 生成验证码 + String captchaType = RuoYiConfig.getCaptchaType(); + if ("math".equals(captchaType)) + { + String capText = captchaProducerMath.createText(); + capStr = capText.substring(0, capText.lastIndexOf("@")); + code = capText.substring(capText.lastIndexOf("@") + 1); + image = captchaProducerMath.createImage(capStr); + } + else if ("char".equals(captchaType)) + { + capStr = code = captchaProducer.createText(); + image = captchaProducer.createImage(capStr); + } + + redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES); + // 转换流信息写出 + FastByteArrayOutputStream os = new FastByteArrayOutputStream(); + try + { + ImageIO.write(image, "jpg", os); + } + catch (IOException e) + { + return AjaxResult.error(e.getMessage()); + } + + ajax.put("uuid", uuid); + ajax.put("img", Base64.encode(os.toByteArray())); + return ajax; + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java new file mode 100644 index 0000000..cec5006 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java @@ -0,0 +1,163 @@ +package com.ruoyi.web.controller.common; + +import java.util.ArrayList; +import java.util.List; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.file.FileUploadUtils; +import com.ruoyi.common.utils.file.FileUtils; +import com.ruoyi.framework.config.ServerConfig; + +/** + * 通用请求处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/common") +public class CommonController +{ + private static final Logger log = LoggerFactory.getLogger(CommonController.class); + + @Autowired + private ServerConfig serverConfig; + + private static final String FILE_DELIMETER = ","; + + /** + * 通用下载请求 + * + * @param fileName 文件名称 + * @param delete 是否删除 + */ + @GetMapping("/download") + public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request) + { + try + { + if (!FileUtils.checkAllowDownload(fileName)) + { + throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName)); + } + String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1); + String filePath = RuoYiConfig.getDownloadPath() + fileName; + + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + FileUtils.setAttachmentResponseHeader(response, realFileName); + FileUtils.writeBytes(filePath, response.getOutputStream()); + if (delete) + { + FileUtils.deleteFile(filePath); + } + } + catch (Exception e) + { + log.error("下载文件失败", e); + } + } + + /** + * 通用上传请求(单个) + */ + @PostMapping("/upload") + public AjaxResult uploadFile(MultipartFile file) throws Exception + { + try + { + // 上传文件路径 + String filePath = RuoYiConfig.getUploadPath(); + // 上传并返回新文件名称 + String fileName = FileUploadUtils.upload(filePath, file); + String url = serverConfig.getUrl() + fileName; + AjaxResult ajax = AjaxResult.success(); + ajax.put("url", url); + ajax.put("fileName", fileName); + ajax.put("newFileName", FileUtils.getName(fileName)); + ajax.put("originalFilename", file.getOriginalFilename()); + return ajax; + } + catch (Exception e) + { + return AjaxResult.error(e.getMessage()); + } + } + + /** + * 通用上传请求(多个) + */ + @PostMapping("/uploads") + public AjaxResult uploadFiles(List<MultipartFile> files) throws Exception + { + try + { + // 上传文件路径 + String filePath = RuoYiConfig.getUploadPath(); + List<String> urls = new ArrayList<String>(); + List<String> fileNames = new ArrayList<String>(); + List<String> newFileNames = new ArrayList<String>(); + List<String> originalFilenames = new ArrayList<String>(); + for (MultipartFile file : files) + { + // 上传并返回新文件名称 + String fileName = FileUploadUtils.upload(filePath, file); + String url = serverConfig.getUrl() + fileName; + urls.add(url); + fileNames.add(fileName); + newFileNames.add(FileUtils.getName(fileName)); + originalFilenames.add(file.getOriginalFilename()); + } + AjaxResult ajax = AjaxResult.success(); + ajax.put("urls", StringUtils.join(urls, FILE_DELIMETER)); + ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMETER)); + ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMETER)); + ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMETER)); + return ajax; + } + catch (Exception e) + { + return AjaxResult.error(e.getMessage()); + } + } + + /** + * 本地资源通用下载 + */ + @GetMapping("/download/resource") + public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response) + throws Exception + { + try + { + if (!FileUtils.checkAllowDownload(resource)) + { + throw new Exception(StringUtils.format("资源文件({})非法,不允许下载。 ", resource)); + } + // 本地资源路径 + String localPath = RuoYiConfig.getProfile(); + // 数据库资源地址 + String downloadPath = localPath + StringUtils.substringAfter(resource, Constants.RESOURCE_PREFIX); + // 下载名称 + String downloadName = StringUtils.substringAfterLast(downloadPath, "/"); + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + FileUtils.setAttachmentResponseHeader(response, downloadName); + FileUtils.writeBytes(downloadPath, response.getOutputStream()); + } + catch (Exception e) + { + log.error("下载文件失败", e); + } + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/interceptor/MybatisConfiguration.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/interceptor/MybatisConfiguration.java new file mode 100644 index 0000000..b2cbb16 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/interceptor/MybatisConfiguration.java @@ -0,0 +1,17 @@ +//package com.ruoyi.web.controller.interceptor; +// +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +// +//@Configuration +//public class MybatisConfiguration { +// +// /** +// * 注册拦截器 +// */ +// @Bean +// public MybatisInterceptor getMybatisInterceptor() { +// return new MybatisInterceptor(); +// } +// +//} \ No newline at end of file diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/interceptor/MybatisInterceptor.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/interceptor/MybatisInterceptor.java new file mode 100644 index 0000000..fcbd5dc --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/interceptor/MybatisInterceptor.java @@ -0,0 +1,119 @@ +//package com.ruoyi.web.controller.interceptor; +// +//import com.ruoyi.framework.web.service.TokenService; +//import lombok.extern.slf4j.Slf4j; +//import org.apache.ibatis.executor.Executor; +//import org.apache.ibatis.mapping.MappedStatement; +//import org.apache.ibatis.mapping.SqlCommandType; +//import org.apache.ibatis.plugin.*; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.stereotype.Component; +// +//import java.lang.reflect.Field; +//import java.time.LocalDateTime; +//import java.util.*; +// +//@Slf4j +//@Component +//@Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) }) +//public class MybatisInterceptor implements Interceptor { +// +// @Autowired +// private TokenService tokenService; +// +// @Override +// public Object intercept(Invocation invocation) throws Throwable { +// MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; +// if("com.ruoyi.system.mapper.SysLogininforMapper.insertLogininfor".equals(mappedStatement.getId())){ +// return invocation.proceed(); +// } +// // sql类型:insert、update、select、delete +// SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType(); +// Object parameter = invocation.getArgs()[1]; +// +// if (parameter == null) { +// return invocation.proceed(); +// } +// +// // 当sql为新增或更新类型时,自动填充操作人相关信息 +// if (SqlCommandType.INSERT == sqlCommandType) { +// +// Field[] fields = getAllFields(parameter); +// for (Field field : fields) { +// try { +// // 注入创建人 +// if ("createBy".equals(field.getName())) { +// // 获取当前登录用户信息 +// if(Objects.nonNull(tokenService.getLoginUser())){ +// String userName = tokenService.getLoginUser().getUser().getUserName(); +// field.setAccessible(true); +// field.set(parameter, userName); +// field.setAccessible(false); +// } +// } +// //注入创建时间 +// if ("createTime".equals(field.getName())) { +// field.setAccessible(true); +//// field.set(parameter, LocalDateTime.now()); +// field.setAccessible(false); +// } +// } catch (Exception e) { +// log.error("failed to insert data, exception = ", e); +// } +// } +// } +// if (SqlCommandType.UPDATE == sqlCommandType) { +// Field[] fields = getAllFields(parameter); +// for (Field field : fields) { +// try { +// if ("updateBy".equals(field.getName())) { +// // 获取当前登录用户信息 +// if(Objects.nonNull(tokenService.getLoginUser())){ +// String userName = tokenService.getLoginUser().getUser().getUserName(); +// field.setAccessible(true); +// field.set(parameter, userName); +// field.setAccessible(false); +// } +// } +// if ("updateTime".equals(field.getName())) { +// field.setAccessible(true); +//// field.set(parameter, new Date()); +// field.setAccessible(false); +// } +// } catch (Exception e) { +// log.error("failed to update data, exception = ", e); +// } +// } +// } +// return invocation.proceed(); +// } +// +// @Override +// public Object plugin(Object target) { +// return Plugin.wrap(target, this); +// } +// +// @Override +// public void setProperties(Properties properties) { +// // TODO Auto-generated method stub +// } +// +// /** +// * 获取类的所有属性,包括父类 +// * +// * @param object +// * @return +// */ +// private Field[] getAllFields(Object object) { +// Class<?> clazz = object.getClass(); +// List<Field> fieldList = new ArrayList<>(); +// while (clazz != null) { +// fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields()))); +// clazz = clazz.getSuperclass(); +// } +// Field[] fields = new Field[fieldList.size()]; +// fieldList.toArray(fields); +// return fields; +// } +// +//} \ No newline at end of file diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java new file mode 100644 index 0000000..69470d0 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java @@ -0,0 +1,120 @@ +package com.ruoyi.web.controller.monitor; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisCallback; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.SysCache; + +/** + * 缓存监控 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/cache") +public class CacheController +{ + @Autowired + private RedisTemplate<String, String> redisTemplate; + + private final static List<SysCache> caches = new ArrayList<SysCache>(); + { + caches.add(new SysCache(CacheConstants.LOGIN_TOKEN_KEY, "用户信息")); + caches.add(new SysCache(CacheConstants.SYS_CONFIG_KEY, "配置信息")); + caches.add(new SysCache(CacheConstants.SYS_DICT_KEY, "数据字典")); + caches.add(new SysCache(CacheConstants.CAPTCHA_CODE_KEY, "验证码")); + caches.add(new SysCache(CacheConstants.REPEAT_SUBMIT_KEY, "防重提交")); + caches.add(new SysCache(CacheConstants.RATE_LIMIT_KEY, "限流处理")); + caches.add(new SysCache(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数")); + } + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @GetMapping() + public AjaxResult getInfo() throws Exception + { + Properties info = (Properties) redisTemplate.execute((RedisCallback<Object>) connection -> connection.info()); + Properties commandStats = (Properties) redisTemplate.execute((RedisCallback<Object>) connection -> connection.info("commandstats")); + Object dbSize = redisTemplate.execute((RedisCallback<Object>) connection -> connection.dbSize()); + + Map<String, Object> result = new HashMap<>(3); + result.put("info", info); + result.put("dbSize", dbSize); + + List<Map<String, String>> pieList = new ArrayList<>(); + commandStats.stringPropertyNames().forEach(key -> { + Map<String, String> data = new HashMap<>(2); + String property = commandStats.getProperty(key); + data.put("name", StringUtils.removeStart(key, "cmdstat_")); + data.put("value", StringUtils.substringBetween(property, "calls=", ",usec")); + pieList.add(data); + }); + result.put("commandStats", pieList); + return AjaxResult.success(result); + } + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @GetMapping("/getNames") + public AjaxResult cache() + { + return AjaxResult.success(caches); + } + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @GetMapping("/getKeys/{cacheName}") + public AjaxResult getCacheKeys(@PathVariable String cacheName) + { + Set<String> cacheKeys = redisTemplate.keys(cacheName + "*"); + return AjaxResult.success(cacheKeys); + } + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @GetMapping("/getValue/{cacheName}/{cacheKey}") + public AjaxResult getCacheValue(@PathVariable String cacheName, @PathVariable String cacheKey) + { + String cacheValue = redisTemplate.opsForValue().get(cacheKey); + SysCache sysCache = new SysCache(cacheName, cacheKey, cacheValue); + return AjaxResult.success(sysCache); + } + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @DeleteMapping("/clearCacheName/{cacheName}") + public AjaxResult clearCacheName(@PathVariable String cacheName) + { + Collection<String> cacheKeys = redisTemplate.keys(cacheName + "*"); + redisTemplate.delete(cacheKeys); + return AjaxResult.success(); + } + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @DeleteMapping("/clearCacheKey/{cacheKey}") + public AjaxResult clearCacheKey(@PathVariable String cacheKey) + { + redisTemplate.delete(cacheKey); + return AjaxResult.success(); + } + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @DeleteMapping("/clearCacheAll") + public AjaxResult clearCacheAll() + { + Collection<String> cacheKeys = redisTemplate.keys("*"); + redisTemplate.delete(cacheKeys); + return AjaxResult.success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java new file mode 100644 index 0000000..cc805ad --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java @@ -0,0 +1,27 @@ +package com.ruoyi.web.controller.monitor; + +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.framework.web.domain.Server; + +/** + * 服务器监控 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/server") +public class ServerController +{ + @PreAuthorize("@ss.hasPermi('monitor:server:list')") + @GetMapping() + public AjaxResult getInfo() throws Exception + { + Server server = new Server(); + server.copyTo(); + return AjaxResult.success(server); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java new file mode 100644 index 0000000..1039b43 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java @@ -0,0 +1,82 @@ +package com.ruoyi.web.controller.monitor; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +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.utils.poi.ExcelUtil; +import com.ruoyi.framework.web.service.SysPasswordService; +import com.ruoyi.system.domain.SysLogininfor; +import com.ruoyi.system.service.ISysLogininforService; + +/** + * 系统访问记录 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/logininfor") +public class SysLogininforController extends BaseController +{ + @Autowired + private ISysLogininforService logininforService; + + @Autowired + private SysPasswordService passwordService; + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:list')") + @GetMapping("/list") + public TableDataInfo list(SysLogininfor logininfor) + { +// startPage(); + List<SysLogininfor> list = logininforService.selectLogininforList(logininfor); + return getDataTable(list); + } + +// @Log(title = "登录日志", businessType = BusinessType.EXPORT) +// @PreAuthorize("@ss.hasPermi('monitor:logininfor:export')") +// @PostMapping("/export") +// public void export(HttpServletResponse response, SysLogininfor logininfor) +// { +// List<SysLogininfor> list = logininforService.selectLogininforList(logininfor); +// ExcelUtil<SysLogininfor> util = new ExcelUtil<SysLogininfor>(SysLogininfor.class); +// util.exportExcel(response, list, "登录日志"); +// } + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')") + @Log(title = "登录日志", businessType = BusinessType.DELETE) + @DeleteMapping("/{infoIds}") + public AjaxResult remove(@PathVariable Long[] infoIds) + { + return toAjax(logininforService.deleteLogininforByIds(infoIds)); + } + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')") + @Log(title = "登录日志", businessType = BusinessType.CLEAN) + @DeleteMapping("/clean") + public AjaxResult clean() + { + logininforService.cleanLogininfor(); + return success(); + } + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:unlock')") + @Log(title = "账户解锁", businessType = BusinessType.OTHER) + @GetMapping("/unlock/{userName}") + public AjaxResult unlock(@PathVariable("userName") String userName) + { + passwordService.clearLoginRecordCache(userName); + return success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java new file mode 100644 index 0000000..4840aa3 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java @@ -0,0 +1,53 @@ +package com.ruoyi.web.controller.monitor; + +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.basic.PageInfo; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.system.query.SysOperLogQuery; +import com.ruoyi.system.service.ISysOperLogService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.List; + +/** + * 操作日志记录 + * + * @author ruoyi + */ +@Slf4j +@Api(tags = "操作日志记录") +@RestController +@RequestMapping("/monitor/operlog") +public class SysOperlogController extends BaseController +{ + @Autowired + private ISysOperLogService operLogService; + + + @Log(title = "删除操作日志", businessType = BusinessType.DELETE) + @DeleteMapping("/deleteById/{operIds}") + public AjaxResult remove(@PathVariable String operIds) + { + String[] split = operIds.split(","); + List<Long> id = new ArrayList<>(); + for (String s : split) { + id.add(Long.valueOf(s)); + } + return AjaxResult.success(operLogService.deleteOperLogByIds(id)); + } + + @Log(title = "清除操作日志", businessType = BusinessType.CLEAN) + @DeleteMapping("/clean") + public AjaxResult clean() + { + operLogService.cleanOperLog(); + return success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java new file mode 100644 index 0000000..a442863 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java @@ -0,0 +1,83 @@ +package com.ruoyi.web.controller.monitor; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.SysUserOnline; +import com.ruoyi.system.service.ISysUserOnlineService; + +/** + * 在线用户监控 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/online") +public class SysUserOnlineController extends BaseController +{ + @Autowired + private ISysUserOnlineService userOnlineService; + + @Autowired + private RedisCache redisCache; + + @PreAuthorize("@ss.hasPermi('monitor:online:list')") + @GetMapping("/list") + public TableDataInfo list(String ipaddr, String userName) + { + Collection<String> keys = redisCache.keys(CacheConstants.LOGIN_TOKEN_KEY + "*"); + List<SysUserOnline> userOnlineList = new ArrayList<SysUserOnline>(); + for (String key : keys) + { + LoginUser user = redisCache.getCacheObject(key); + if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) + { + userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user)); + } + else if (StringUtils.isNotEmpty(ipaddr)) + { + userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user)); + } + else if (StringUtils.isNotEmpty(userName) && StringUtils.isNotNull(user.getUser())) + { + userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user)); + } + else + { + userOnlineList.add(userOnlineService.loginUserToUserOnline(user)); + } + } + Collections.reverse(userOnlineList); + userOnlineList.removeAll(Collections.singleton(null)); + return getDataTable(userOnlineList); + } + + /** + * 强退用户 + */ + @PreAuthorize("@ss.hasPermi('monitor:online:forceLogout')") + @Log(title = "在线用户", businessType = BusinessType.FORCE) + @DeleteMapping("/{tokenId}") + public AjaxResult forceLogout(@PathVariable String tokenId) + { + redisCache.deleteObject(CacheConstants.LOGIN_TOKEN_KEY + tokenId); + return success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java new file mode 100644 index 0000000..5737466 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java @@ -0,0 +1,121 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +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.utils.poi.ExcelUtil; +import com.ruoyi.system.domain.SysConfig; +import com.ruoyi.system.service.ISysConfigService; + +/** + * 参数配置 信息操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/config") +public class SysConfigController extends BaseController +{ + @Autowired + private ISysConfigService configService; + +// @Log(title = "参数管理", businessType = BusinessType.EXPORT) +// @PreAuthorize("@ss.hasPermi('system:config:export')") +// @PostMapping("/export") +// public void export(HttpServletResponse response, SysConfig config) +// { +// List<SysConfig> list = configService.selectConfigList(config); +// ExcelUtil<SysConfig> util = new ExcelUtil<SysConfig>(SysConfig.class); +// util.exportExcel(response, list, "参数数据"); +// } + + /** + * 根据参数编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:config:query')") + @GetMapping(value = "/{configId}") + public AjaxResult getInfo(@PathVariable Long configId) + { + return success(configService.selectConfigById(configId)); + } + + /** + * 根据参数键名查询参数值 + */ + @GetMapping(value = "/configKey/{configKey}") + public AjaxResult getConfigKey(@PathVariable String configKey) + { + return success(configService.selectConfigByKey(configKey)); + } + + /** + * 新增参数配置 + */ + @PreAuthorize("@ss.hasPermi('system:config:add')") + @Log(title = "参数管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysConfig config) + { + if (!configService.checkConfigKeyUnique(config)) + { + return error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + config.setCreateBy(getUsername()); + return toAjax(configService.insertConfig(config)); + } + + /** + * 修改参数配置 + */ + @PreAuthorize("@ss.hasPermi('system:config:edit')") + @Log(title = "参数管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysConfig config) + { + if (!configService.checkConfigKeyUnique(config)) + { + return error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + config.setUpdateBy(getUsername()); + return toAjax(configService.updateConfig(config)); + } + + /** + * 删除参数配置 + */ + @PreAuthorize("@ss.hasPermi('system:config:remove')") + @Log(title = "参数管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{configIds}") + public AjaxResult remove(@PathVariable Long[] configIds) + { + configService.deleteConfigByIds(configIds); + return success(); + } + + /** + * 刷新参数缓存 + */ + @PreAuthorize("@ss.hasPermi('system:config:remove')") + @Log(title = "参数管理", businessType = BusinessType.CLEAN) + @DeleteMapping("/refreshCache") + public AjaxResult refreshCache() + { + configService.resetConfigCache(); + return success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java new file mode 100644 index 0000000..a74355f --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java @@ -0,0 +1,132 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.service.ISysDeptService; + +/** + * 部门信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/dept") +public class SysDeptController extends BaseController +{ + @Autowired + private ISysDeptService deptService; + + /** + * 获取部门列表 + */ + // @PreAuthorize("@ss.hasPermi('system:dept:list')") + @GetMapping("/list") + public AjaxResult list(SysDept dept) + { + List<SysDept> depts = deptService.selectDeptList(dept); + return AjaxResult.success(depts); + } + + /** + * 查询部门列表(排除节点) + */ + // @PreAuthorize("@ss.hasPermi('system:dept:list')") + @GetMapping("/list/exclude/{deptId}") + public AjaxResult excludeChild(@PathVariable(value = "deptId", required = false) Long deptId) + { + List<SysDept> depts = deptService.selectDeptList(new SysDept()); + depts.removeIf(d -> d.getDeptId().intValue() == deptId || ArrayUtils.contains(StringUtils.split(d.getAncestors(), ","), deptId + "")); + return AjaxResult.success(depts); + } + + /** + * 根据部门编号获取详细信息 + */ + // @PreAuthorize("@ss.hasPermi('system:dept:query')") + @GetMapping(value = "/{deptId}") + public AjaxResult getInfo(@PathVariable Long deptId) + { + deptService.checkDeptDataScope(deptId); + return AjaxResult.success(deptService.selectDeptById(deptId)); + } + + /** + * 新增部门 + */ + // @PreAuthorize("@ss.hasPermi('system:dept:add')") + @Log(title = "部门管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysDept dept) + { + if (!deptService.checkDeptNameUnique(dept)) + { + return error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } + dept.setCreateBy(getUsername()); + return AjaxResult.success(deptService.insertDept(dept)); + } + + /** + * 修改部门 + */ + // @PreAuthorize("@ss.hasPermi('system:dept:edit')") + @Log(title = "部门管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysDept dept) + { + Long deptId = dept.getDeptId(); + deptService.checkDeptDataScope(deptId); + if (!deptService.checkDeptNameUnique(dept)) + { + return error("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } + else if (dept.getParentId().equals(deptId)) + { + return error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己"); + } + else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus()) && deptService.selectNormalChildrenDeptById(deptId) > 0) + { + return error("该部门包含未停用的子部门!"); + } + dept.setUpdateBy(getUsername()); + return AjaxResult.success(deptService.updateDept(dept)); + } + + /** + * 删除部门 + */ + // @PreAuthorize("@ss.hasPermi('system:dept:remove')") + @Log(title = "部门管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{deptId}") + public AjaxResult remove(@PathVariable Long deptId) + { + if (deptService.hasChildByDeptId(deptId)) + { + return warn("存在下级部门,不允许删除"); + } + if (deptService.checkDeptExistUser(deptId)) + { + return warn("部门存在用户,不允许删除"); + } + deptService.checkDeptDataScope(deptId); + return AjaxResult.success(deptService.deleteDeptById(deptId)); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java new file mode 100644 index 0000000..18f47f7 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java @@ -0,0 +1,126 @@ +package com.ruoyi.web.controller.system; + +import java.util.ArrayList; +import java.util.List; +import javax.servlet.http.HttpServletResponse; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +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.domain.entity.SysDictData; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.service.ISysDictDataService; +import com.ruoyi.system.service.ISysDictTypeService; + +/** + * 数据字典信息 + * + * @author ruoyi + */ +@Api(tags = "数据字典信息") +@RestController +@RequestMapping("/system/dict/data") +public class SysDictDataController extends BaseController +{ + @Autowired + private ISysDictDataService dictDataService; + + @Autowired + private ISysDictTypeService dictTypeService; + + @PreAuthorize("@ss.hasPermi('system:dict:list')") + @GetMapping("/list") + public TableDataInfo list(SysDictData dictData) + { +// startPage(); + List<SysDictData> list = dictDataService.selectDictDataList(dictData); + return getDataTable(list); + } + +// @Log(title = "字典数据", businessType = BusinessType.EXPORT) +// @PreAuthorize("@ss.hasPermi('system:dict:export')") +// @PostMapping("/export") +// public void export(HttpServletResponse response, SysDictData dictData) +// { +// List<SysDictData> list = dictDataService.selectDictDataList(dictData); +// ExcelUtil<SysDictData> util = new ExcelUtil<SysDictData>(SysDictData.class); +// util.exportExcel(response, list, "字典数据"); +// } + + /** + * 查询字典数据详细 + */ + @PreAuthorize("@ss.hasPermi('system:dict:query')") + @GetMapping(value = "/{dictCode}") + public AjaxResult getInfo(@PathVariable Long dictCode) + { + return success(dictDataService.selectDictDataById(dictCode)); + } + + /** + * 根据字典类型查询字典数据信息 + */ + @ApiOperation(value = "根据字典类型查询字典数据信息") + @GetMapping(value = "/type/{dictType}") + public AjaxResult dictType(@PathVariable String dictType) + { + List<SysDictData> data = dictTypeService.selectDictDataByType(dictType); + if (StringUtils.isNull(data)) + { + data = new ArrayList<SysDictData>(); + } + return success(data); + } + + /** + * 新增字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:add')") + @Log(title = "字典数据", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysDictData dict) + { + dict.setCreateBy(getUsername()); + return toAjax(dictDataService.insertDictData(dict)); + } + + /** + * 修改保存字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:edit')") + @Log(title = "字典数据", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysDictData dict) + { + dict.setUpdateBy(getUsername()); + return toAjax(dictDataService.updateDictData(dict)); + } + + /** + * 删除字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:remove')") + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictCodes}") + public AjaxResult remove(@PathVariable Long[] dictCodes) + { + dictDataService.deleteDictDataByIds(dictCodes); + return success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java new file mode 100644 index 0000000..855d191 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java @@ -0,0 +1,131 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +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.domain.entity.SysDictType; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.service.ISysDictTypeService; + +/** + * 数据字典信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/dict/type") +public class SysDictTypeController extends BaseController +{ + @Autowired + private ISysDictTypeService dictTypeService; + + @PreAuthorize("@ss.hasPermi('system:dict:list')") + @GetMapping("/list") + public TableDataInfo list(SysDictType dictType) + { +// startPage(); + List<SysDictType> list = dictTypeService.selectDictTypeList(dictType); + return getDataTable(list); + } + +// @Log(title = "字典类型", businessType = BusinessType.EXPORT) +// @PreAuthorize("@ss.hasPermi('system:dict:export')") +// @PostMapping("/export") +// public void export(HttpServletResponse response, SysDictType dictType) +// { +// List<SysDictType> list = dictTypeService.selectDictTypeList(dictType); +// ExcelUtil<SysDictType> util = new ExcelUtil<SysDictType>(SysDictType.class); +// util.exportExcel(response, list, "字典类型"); +// } + + /** + * 查询字典类型详细 + */ + @PreAuthorize("@ss.hasPermi('system:dict:query')") + @GetMapping(value = "/{dictId}") + public AjaxResult getInfo(@PathVariable Long dictId) + { + return success(dictTypeService.selectDictTypeById(dictId)); + } + + /** + * 新增字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:add')") + @Log(title = "字典类型", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysDictType dict) + { + if (!dictTypeService.checkDictTypeUnique(dict)) + { + return error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dict.setCreateBy(getUsername()); + return toAjax(dictTypeService.insertDictType(dict)); + } + + /** + * 修改字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:edit')") + @Log(title = "字典类型", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysDictType dict) + { + if (!dictTypeService.checkDictTypeUnique(dict)) + { + return error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dict.setUpdateBy(getUsername()); + return toAjax(dictTypeService.updateDictType(dict)); + } + + /** + * 删除字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:remove')") + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictIds}") + public AjaxResult remove(@PathVariable Long[] dictIds) + { + dictTypeService.deleteDictTypeByIds(dictIds); + return success(); + } + + /** + * 刷新字典缓存 + */ + @PreAuthorize("@ss.hasPermi('system:dict:remove')") + @Log(title = "字典类型", businessType = BusinessType.CLEAN) + @DeleteMapping("/refreshCache") + public AjaxResult refreshCache() + { + dictTypeService.resetDictCache(); + return success(); + } + + /** + * 获取字典选择框列表 + */ + @GetMapping("/optionselect") + public AjaxResult optionselect() + { + List<SysDictType> dictTypes = dictTypeService.selectDictTypeAll(); + return success(dictTypes); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java new file mode 100644 index 0000000..13007eb --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java @@ -0,0 +1,29 @@ +package com.ruoyi.web.controller.system; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.utils.StringUtils; + +/** + * 首页 + * + * @author ruoyi + */ +@RestController +public class SysIndexController +{ + /** 系统基础配置 */ + @Autowired + private RuoYiConfig ruoyiConfig; + + /** + * 访问首页,提示语 + */ + @RequestMapping("/") + public String index() + { + return StringUtils.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion()); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java new file mode 100644 index 0000000..89c82e7 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java @@ -0,0 +1,42 @@ +package com.ruoyi.web.controller.system; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.system.service.ISysRoleService; +import com.ruoyi.web.controller.tool.MsgUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.*; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginBody; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.framework.web.service.SysLoginService; +import com.ruoyi.framework.web.service.SysPermissionService; +import com.ruoyi.system.service.ISysMenuService; + +/** + * 登录验证 + * + * @author ruoyi + */ +@Api(tags = "登录") +@RestController +public class SysLoginController +{ + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java new file mode 100644 index 0000000..ad9b0f4 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java @@ -0,0 +1,168 @@ +package com.ruoyi.web.controller.system; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.service.ISysMenuService; + +/** + * 菜单信息 + * + * @author ruoyi + */ +@Api(tags = "菜单信息") +@RestController +@RequestMapping("/system/menu") +public class SysMenuController extends BaseController +{ + @Autowired + private ISysMenuService menuService; + + @ApiOperation("菜单权限(有层级)") + @GetMapping("/levelList") + public AjaxResult levelList() + { + // 获取当前角色的菜单列表 + List<SysMenu> menus = menuService.selectList(); + if(menus.size()==0){ + return AjaxResult.success(new ArrayList<>()); + } + // 第三级 + List<SysMenu> s3 = menus.stream().filter(e -> e.getMenuType().equals("F")).collect(Collectors.toList()); + // 第二级 + List<SysMenu> s2 = menus.stream().filter(e -> e.getMenuType().equals("C")).collect(Collectors.toList()); + // 第一级 + List<SysMenu> s1 = menus.stream().filter(e -> e.getMenuType().equals("M")).collect(Collectors.toList()); + + for (SysMenu menu : s2) { + List<SysMenu> collect = s3.stream().filter(e -> e.getParentId().equals(menu.getMenuId())).collect(Collectors.toList()); + menu.setChildren(collect); + } + for (SysMenu menu : s1) { + List<SysMenu> collect = s2.stream().filter(e -> e.getParentId().equals(menu.getMenuId())).collect(Collectors.toList()); + menu.setChildren(collect); + } + + return AjaxResult.success(s1); + } + + /** + * 获取菜单列表 + */ + // @PreAuthorize("@ss.hasPermi('system:menu:list')") + @GetMapping("/list") + public AjaxResult list(SysMenu menu) + { + List<SysMenu> menus = menuService.selectMenuList(menu, getUserId()); + return AjaxResult.success(menus); + } + + /** + * 根据菜单编号获取详细信息 + */ + // @PreAuthorize("@ss.hasPermi('system:menu:query')") + @GetMapping(value = "/{menuId}") + public AjaxResult getInfo(@PathVariable Long menuId) + { + return AjaxResult.success(menuService.selectMenuById(menuId)); + } + + /** + * 获取菜单下拉树列表 + */ + @GetMapping("/treeselect") + public AjaxResult treeselect(SysMenu menu) + { + List<SysMenu> menus = menuService.selectMenuList(menu, getUserId()); + return AjaxResult.success(menuService.buildMenuTreeSelect(menus)); + } + + /** + * 加载对应角色菜单列表树 + */ + @GetMapping(value = "/roleMenuTreeselect/{roleId}") + public AjaxResult roleMenuTreeselect(@PathVariable("roleId") Long roleId) + { + List<SysMenu> menus = menuService.selectMenuList(getUserId()); + AjaxResult ajax = AjaxResult.success(); + ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId)); + ajax.put("menus", menuService.buildMenuTreeSelect(menus)); + return ajax; + } + + /** + * 新增菜单 + */ + // @PreAuthorize("@ss.hasPermi('system:menu:add')") + @Log(title = "菜单管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysMenu menu) + { + if (!menuService.checkMenuNameUnique(menu)) + { + return error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } + else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) + { + return error("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); + } + menu.setCreateBy(getUsername()); + return AjaxResult.success(menuService.insertMenu(menu)); + } + + /** + * 修改菜单 + */ + // @PreAuthorize("@ss.hasPermi('system:menu:edit')") + @Log(title = "菜单管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysMenu menu) + { + if (!menuService.checkMenuNameUnique(menu)) + { + return error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } + else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) + { + return error("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); + } + else if (menu.getMenuId().equals(menu.getParentId())) + { + return error("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己"); + } + menu.setUpdateBy(getUsername()); + return AjaxResult.success(menuService.updateMenu(menu)); + } + + /** + * 删除菜单 + */ + // @PreAuthorize("@ss.hasPermi('system:menu:remove')") + @Log(title = "菜单管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{menuId}") + public AjaxResult remove(@PathVariable("menuId") Long menuId) + { + if (menuService.hasChildByMenuId(menuId)) + { + return warn("存在子菜单,不允许删除"); + } + if (menuService.checkMenuExistRole(menuId)) + { + return warn("菜单已分配,不允许删除"); + } + return AjaxResult.success(menuService.deleteMenuById(menuId)); + } +} \ No newline at end of file diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java new file mode 100644 index 0000000..3d7352a --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java @@ -0,0 +1,91 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +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.system.domain.SysNotice; +import com.ruoyi.system.service.ISysNoticeService; + +/** + * 公告 信息操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/notice") +public class SysNoticeController extends BaseController +{ + @Autowired + private ISysNoticeService noticeService; + + /** + * 获取通知公告列表 + */ + @PreAuthorize("@ss.hasPermi('system:notice:list')") + @GetMapping("/list") + public TableDataInfo list(SysNotice notice) + { +// startPage(); + List<SysNotice> list = noticeService.selectNoticeList(notice); + return getDataTable(list); + } + + /** + * 根据通知公告编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:notice:query')") + @GetMapping(value = "/{noticeId}") + public AjaxResult getInfo(@PathVariable Long noticeId) + { + return success(noticeService.selectNoticeById(noticeId)); + } + + /** + * 新增通知公告 + */ + @PreAuthorize("@ss.hasPermi('system:notice:add')") + @Log(title = "通知公告", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysNotice notice) + { + notice.setCreateBy(getUsername()); + return toAjax(noticeService.insertNotice(notice)); + } + + /** + * 修改通知公告 + */ + @PreAuthorize("@ss.hasPermi('system:notice:edit')") + @Log(title = "通知公告", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysNotice notice) + { + notice.setUpdateBy(getUsername()); + return toAjax(noticeService.updateNotice(notice)); + } + + /** + * 删除通知公告 + */ + @PreAuthorize("@ss.hasPermi('system:notice:remove')") + @Log(title = "通知公告", businessType = BusinessType.DELETE) + @DeleteMapping("/{noticeIds}") + public AjaxResult remove(@PathVariable Long[] noticeIds) + { + return toAjax(noticeService.deleteNoticeByIds(noticeIds)); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java new file mode 100644 index 0000000..4d18083 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java @@ -0,0 +1,129 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +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.utils.poi.ExcelUtil; +import com.ruoyi.system.domain.SysPost; +import com.ruoyi.system.service.ISysPostService; + +/** + * 岗位信息操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/post") +public class SysPostController extends BaseController +{ + @Autowired + private ISysPostService postService; + + /** + * 获取岗位列表 + */ + @PreAuthorize("@ss.hasPermi('system:post:list')") + @GetMapping("/list") + public TableDataInfo list(SysPost post) + { +// startPage(); + List<SysPost> list = postService.selectPostList(post); + return getDataTable(list); + } + +// @Log(title = "岗位管理", businessType = BusinessType.EXPORT) +// @PreAuthorize("@ss.hasPermi('system:post:export')") +// @PostMapping("/export") +// public void export(HttpServletResponse response, SysPost post) +// { +// List<SysPost> list = postService.selectPostList(post); +// ExcelUtil<SysPost> util = new ExcelUtil<SysPost>(SysPost.class); +// util.exportExcel(response, list, "岗位数据"); +// } + + /** + * 根据岗位编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:post:query')") + @GetMapping(value = "/{postId}") + public AjaxResult getInfo(@PathVariable Long postId) + { + return success(postService.selectPostById(postId)); + } + + /** + * 新增岗位 + */ + @PreAuthorize("@ss.hasPermi('system:post:add')") + @Log(title = "岗位管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysPost post) + { + if (!postService.checkPostNameUnique(post)) + { + return error("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } + else if (!postService.checkPostCodeUnique(post)) + { + return error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + post.setCreateBy(getUsername()); + return toAjax(postService.insertPost(post)); + } + + /** + * 修改岗位 + */ + @PreAuthorize("@ss.hasPermi('system:post:edit')") + @Log(title = "岗位管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysPost post) + { + if (!postService.checkPostNameUnique(post)) + { + return error("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } + else if (!postService.checkPostCodeUnique(post)) + { + return error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + post.setUpdateBy(getUsername()); + return toAjax(postService.updatePost(post)); + } + + /** + * 删除岗位 + */ + @PreAuthorize("@ss.hasPermi('system:post:remove')") + @Log(title = "岗位管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{postIds}") + public AjaxResult remove(@PathVariable Long[] postIds) + { + return toAjax(postService.deletePostByIds(postIds)); + } + + /** + * 获取岗位选择框列表 + */ + @GetMapping("/optionselect") + public AjaxResult optionselect() + { + List<SysPost> posts = postService.selectPostAll(); + return success(posts); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java new file mode 100644 index 0000000..be5af6a --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java @@ -0,0 +1,136 @@ +package com.ruoyi.web.controller.system; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.file.FileUploadUtils; +import com.ruoyi.common.utils.file.MimeTypeUtils; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.system.service.ISysUserService; + +/** + * 个人信息 业务处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/user/profile") +public class SysProfileController extends BaseController +{ + @Autowired + private ISysUserService userService; + + @Autowired + private TokenService tokenService; + + /** + * 个人信息 + */ + @GetMapping + public AjaxResult profile() + { + LoginUser loginUser = getLoginUser(); + SysUser user = loginUser.getUser(); + AjaxResult ajax = AjaxResult.success(user); + ajax.put("roleGroup", userService.selectUserRoleGroup(loginUser.getUsername())); + ajax.put("postGroup", userService.selectUserPostGroup(loginUser.getUsername())); + return ajax; + } + + /** + * 修改用户 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult updateProfile(@RequestBody SysUser user) + { + LoginUser loginUser = getLoginUser(); + SysUser currentUser = loginUser.getUser(); + currentUser.setNickName(user.getNickName()); + currentUser.setEmail(user.getEmail()); + currentUser.setPhonenumber(user.getPhonenumber()); + currentUser.setSex(user.getSex()); + if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(currentUser)) + { + return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); + } + if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(currentUser)) + { + return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + if (userService.updateUserProfile(currentUser) > 0) + { + // 更新缓存用户信息 + tokenService.setLoginUser(loginUser); + return success(); + } + return error("修改个人信息异常,请联系管理员"); + } + + /** + * 重置密码 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping("/updatePwd") + public AjaxResult updatePwd(String oldPassword, String newPassword) + { + LoginUser loginUser = getLoginUser(); + String userName = loginUser.getUsername(); + String password = loginUser.getPassword(); + if (!SecurityUtils.matchesPassword(oldPassword, password)) + { + return error("修改密码失败,旧密码错误"); + } + if (SecurityUtils.matchesPassword(newPassword, password)) + { + return error("新密码不能与旧密码相同"); + } + if (userService.resetUserPwd(userName, SecurityUtils.encryptPassword(newPassword)) > 0) + { + // 更新缓存用户密码 + loginUser.getUser().setPassword(SecurityUtils.encryptPassword(newPassword)); + tokenService.setLoginUser(loginUser); + return success(); + } + return error("修改密码异常,请联系管理员"); + } + + /** + * 头像上传 + */ + @Log(title = "用户头像", businessType = BusinessType.UPDATE) + @PostMapping("/avatar") + public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws Exception + { + if (!file.isEmpty()) + { + LoginUser loginUser = getLoginUser(); + String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION); + if (userService.updateUserAvatar(loginUser.getUsername(), avatar)) + { + AjaxResult ajax = AjaxResult.success(); + ajax.put("imgUrl", avatar); + // 更新缓存用户头像 + loginUser.getUser().setAvatar(avatar); + tokenService.setLoginUser(loginUser); + return ajax; + } + } + return error("上传图片异常,请联系管理员"); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java new file mode 100644 index 0000000..fe19249 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java @@ -0,0 +1,38 @@ +package com.ruoyi.web.controller.system; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.model.RegisterBody; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.service.SysRegisterService; +import com.ruoyi.system.service.ISysConfigService; + +/** + * 注册验证 + * + * @author ruoyi + */ +@RestController +public class SysRegisterController extends BaseController +{ + @Autowired + private SysRegisterService registerService; + + @Autowired + private ISysConfigService configService; + + @PostMapping("/register") + public AjaxResult register(@RequestBody RegisterBody user) + { + if (!("true".equals(configService.selectConfigByKey("sys.account.registerUser")))) + { + return error("当前系统没有开启注册功能!"); + } + String msg = registerService.register(user); + return StringUtils.isEmpty(msg) ? success() : error(msg); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java new file mode 100644 index 0000000..5fdcb63 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java @@ -0,0 +1,253 @@ +package com.ruoyi.web.controller.system; + +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.basic.PageInfo; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysDept; +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.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.framework.web.service.SysPermissionService; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.system.domain.SysUserRole; +import com.ruoyi.system.query.SysRoleQuery; +import com.ruoyi.system.service.ISysDeptService; +import com.ruoyi.system.service.ISysMenuService; +import com.ruoyi.system.service.ISysRoleService; +import com.ruoyi.system.service.ISysUserService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 角色信息 + * + * @author ruoyi + */ +@Api(tags = "角色信息") +@RestController +@RequestMapping("/system/role") +public class SysRoleController extends BaseController +{ + @Autowired + private ISysRoleService roleService; + + @Autowired + private TokenService tokenService; + + @Autowired + private SysPermissionService permissionService; + + @Autowired + private ISysUserService userService; + + @Autowired + private ISysDeptService deptService; + @Autowired + private ISysMenuService menuService; + + @PreAuthorize("@ss.hasPermi('system:role:list')") + @ApiOperation(value = "角色列表") + @PostMapping("/list") + public AjaxResult list(@RequestBody SysRoleQuery query) + { + PageInfo<SysRole> list = roleService.selectPageList(query); + return AjaxResult.success(list); + } + + @PreAuthorize("@ss.hasPermi('system:role:list')") + @ApiOperation(value = "角色列表不分页") + @PostMapping("/listNotPage") + public AjaxResult list() + { + List<SysRole> list = roleService.selectRoleList(new SysRole()); + return AjaxResult.success(list); + } + @PreAuthorize("@ss.hasPermi('system:role:count')") + @ApiOperation(value = "角色数量统计") + @PostMapping("/roleCount") + public AjaxResult roleCount() + { + int all = roleService.selectCount(null); + int normal = roleService.selectCount(0); + int stop = roleService.selectCount(1); + + Map<String,Integer> map = new HashMap<>(); + map.put("all",all); + map.put("normal",normal); + map.put("stop",stop); + return AjaxResult.success(map); + } + +// @Log(title = "角色管理", businessType = BusinessType.EXPORT) +// // @PreAuthorize("@ss.hasPermi('system:role:export')") +// @PostMapping("/export") +// public void export(HttpServletResponse response, SysRole role) +// { +// List<SysRole> list = roleService.selectRoleList(role); +// ExcelUtil<SysRole> util = new ExcelUtil<SysRole>(SysRole.class); +// util.exportExcel(response, list, "角色数据"); +// } + + /** + * 根据角色编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:role:query')") + @GetMapping(value = "/{roleId}") + public AjaxResult getInfo(@PathVariable Long roleId) + { + roleService.checkRoleDataScope(roleId); + return AjaxResult.success(roleService.selectRoleById(roleId)); + } + + + + @ApiOperation("用户获取权限菜单") + @GetMapping("/roleInfoFromUserId") + public AjaxResult roleInfoFromUserId(@RequestParam Long userId) + { + return AjaxResult.success(roleService.roleInfoFromUserId(userId)); + } + + + /** + * 新增角色 + */ + + + /** + * 修改保存数据权限 + */ + // @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping("/dataScope") + public AjaxResult dataScope(@RequestBody SysRole role) + { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + return AjaxResult.success(roleService.authDataScope(role)); + } + + /** + * 状态修改 + */ + // @PreAuthorize("@ss.hasPermi('system:role:edit')") + @ApiOperation(value = "状态修改") + @Log(title = "角色信息-角色状态修改", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public AjaxResult changeStatus(@RequestBody SysRole role) + { + role.setUpdateBy(getUsername()); + roleService.updateStatus(role); + return AjaxResult.success(); + } + + /** + * 删除角色 + */ + // @PreAuthorize("@ss.hasPermi('system:role:remove')") + @ApiOperation(value = "删除角色") + @Log(title = "角色信息-角色删除角色", businessType = BusinessType.DELETE) + @DeleteMapping("/deleteById/{ids}") + public AjaxResult remove(@PathVariable String ids) + { + String[] split = ids.split(","); + List<Long> id = new ArrayList<>(); + for (String s : split) { + id.add(Long.valueOf(s)); + } + return AjaxResult.success(roleService.deleteRoleByIds(id)); + } + + /** + * 获取角色选择框列表 + */ + // @PreAuthorize("@ss.hasPermi('system:role:query')") + @GetMapping("/optionselect") + public AjaxResult optionselect() + { + return AjaxResult.success(roleService.selectRoleAll()); + } + + /** + * 查询已分配用户角色列表 + */ + // @PreAuthorize("@ss.hasPermi('system:role:list')") + @GetMapping("/authUser/allocatedList") + public TableDataInfo allocatedList(SysUser user) + { +// startPage(); + List<SysUser> list = userService.selectAllocatedList(user); + return getDataTable(list); + } + + /** + * 查询未分配用户角色列表 + */ + // @PreAuthorize("@ss.hasPermi('system:role:list')") + @GetMapping("/authUser/unallocatedList") + public TableDataInfo unallocatedList(SysUser user) + { +// startPage(); + List<SysUser> list = userService.selectUnallocatedList(user); + return getDataTable(list); + } + + /** + * 取消授权用户 + */ + // @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/cancel") + public AjaxResult cancelAuthUser(@RequestBody SysUserRole userRole) + { + return AjaxResult.success(roleService.deleteAuthUser(userRole)); + } + + /** + * 批量取消授权用户 + */ + // @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/cancelAll") + public AjaxResult cancelAuthUserAll(Long roleId, Long[] userIds) + { + return AjaxResult.success(roleService.deleteAuthUsers(roleId, userIds)); + } + + /** + * 批量选择用户授权 + */ + // @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/selectAll") + public AjaxResult selectAuthUserAll(Long roleId, Long[] userIds) + { + roleService.checkRoleDataScope(roleId); + return AjaxResult.success(roleService.insertAuthUsers(roleId, userIds)); + } + + /** + * 获取对应角色部门树列表 + */ + // @PreAuthorize("@ss.hasPermi('system:role:query')") + @GetMapping(value = "/deptTree/{roleId}") + public AjaxResult deptTree(@PathVariable("roleId") Long roleId) + { + AjaxResult ajax = AjaxResult.success(); + ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId)); + ajax.put("depts", deptService.selectDeptTreeList(new SysDept())); + return ajax; + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java new file mode 100644 index 0000000..3312d74 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java @@ -0,0 +1,235 @@ +package com.ruoyi.web.controller.system; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.basic.PageInfo; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +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.enums.BusinessType; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.system.query.SysUserQuery; +import com.ruoyi.system.service.*; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.CollectionUtils; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 用户信息 + * + * @author ruoyi + */ +@Api(tags = "用户信息") +@RestController +@RequestMapping("/system/user") +public class SysUserController extends BaseController +{ + @Autowired + private ISysUserService userService; + + @Autowired + private ISysRoleService roleService; + + @Autowired + private ISysDeptService deptService; + @Autowired + private TokenService tokenService; + + @ApiOperation(value = "获取用户列表-不分页") + @PostMapping("/listNotPage") + public AjaxResult listNotPage() + { + List<SysUser> list = userService.selectList(); + return AjaxResult.success(list); + } + + /** + * 获取用户黑名单列表 + */ +// @ApiOperation(value = "获取用户黑名单列表") +// @PostMapping("/blacklist") +// public AjaxResult blacklist(@RequestBody SysUserQuery query) +// { +// startPage(query.getPageNum(), query.getPageSize()); +// List<SysUserVO> list = userService.selectBlackPageList(query); +// return AjaxResult.success(getDataTable(list)); +// } + + + + + /** + * 获取用户数量统计 + */ + @ApiOperation(value = "获取用户数量统计") + @PostMapping("/getUserCount") + public AjaxResult getUserCount() + { + Map<String,Integer> map = new HashMap<>(); + + Integer userCountSum = userService.selectCount(null); + Integer normalCount = userService.selectCount(0);// 正常 + Integer stopCount = userService.selectCount(1);// 停用 + + map.put("all",userCountSum); + map.put("normal",normalCount); + map.put("stop",stopCount); + + return AjaxResult.success(map); + } + + /** + * 移除黑名单 + */ + @GetMapping("/removeBlackList") + public AjaxResult removeBlackList(@RequestParam String ids) + { + String[] split = ids.split(","); + List<Long> id = new ArrayList<>(); + for (String s : split) { + id.add(Long.valueOf(s)); + } + userService.updateUserIfBlack(id); + return AjaxResult.success(); + } + + /** + * 新增用户 + */ + // @PreAuthorize("@ss.hasPermi('system:user:add')") + @ApiOperation(value = "新增用户管理") + @Log(title = "用户信息-新增用户", businessType = BusinessType.INSERT) + @PostMapping("/add") + public AjaxResult add(@Validated @RequestBody SysUser user) + { + user.setUserName(user.getUserName()); + if (!userService.checkUserNameUnique(user)) + { + return error("新增用户'" + user.getUserName() + "'失败,登录账号已存在"); + } + else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) + { + return error("新增用户'" + user.getUserName() + "'失败,手机号码已存在"); + } + user.setCreateBy(getUsername()); + user.setPassword(SecurityUtils.encryptPassword("123456")); + userService.insertUser(user); + return AjaxResult.success(); + } + + /** + * 修改用户 + */ + // @PreAuthorize("@ss.hasPermi('system:user:edit')") + @ApiOperation(value = "修改用户管理") + @Log(title = "用户信息-修改用户", businessType = BusinessType.UPDATE) + @PostMapping("/edit") + public AjaxResult edit(@Validated @RequestBody SysUser user) + { + user.setUserName(user.getPhonenumber()); +// userService.checkUserAllowed(user); +// userService.checkUserDataScope(user.getUserId()); + if (!userService.checkUserNameUnique(user)) + { + return error("修改用户'" + user.getUserName() + "'失败,登录账号已存在"); + } + else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) + { + return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); + } + + user.setUpdateBy(getUsername()); + if(StringUtils.isNotEmpty(user.getPassword())){ + user.setPassword(SecurityUtils.encryptPassword(user.getPassword())); + } + return AjaxResult.success(userService.updateUser(user)); + } + + /** + * 删除用户 + */ + // @PreAuthorize("@ss.hasPermi('system:user:remove')") + @ApiOperation(value = "批量删除用户") + @Log(title = "用户信息-批量删除用户", businessType = BusinessType.DELETE) + @DeleteMapping("/deleteById/{ids}") + public AjaxResult remove(@PathVariable String ids) + { + String[] split = ids.split(","); + List<Long> userIds = new ArrayList<>(); + for (String s : split) { + userIds.add(Long.valueOf(s)); + } + if (userIds.contains(getUserId())) + { + return error("当前用户不能删除"); + } + return AjaxResult.success(userService.deleteUserByIds(userIds)); + } + + /** + * 重置密码 + */ + // @PreAuthorize("@ss.hasPermi('system:user:resetPwd')") + @ApiOperation(value = "重置密码") + @Log(title = "用户信息-重置密码", businessType = BusinessType.UPDATE) + @PostMapping("/resetPwd") + public AjaxResult resetPwd(@RequestBody SysUser user) + { + userService.checkUserAllowed(user); +// userService.checkUserDataScope(user.getUserId()); + user.setPassword(SecurityUtils.encryptPassword(user.getPassword())); + user.setUpdateBy(getUsername()); + return AjaxResult.success(userService.resetPwd(user)); + } + + + /** + * 根据用户编号获取授权角色 + */ + // @PreAuthorize("@ss.hasPermi('system:user:query')") + @GetMapping("/authRole/{userId}") + public AjaxResult authRole(@PathVariable("userId") Long userId) + { + AjaxResult ajax = AjaxResult.success(); + SysUser user = userService.selectUserById(userId); + List<SysRole> roles = roleService.selectRolesByUserId(userId); + ajax.put("user", user); + ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList())); + return ajax; + } + + /** + * 用户授权角色 + */ + // @PreAuthorize("@ss.hasPermi('system:user:edit')") + @Log(title = "用户管理", businessType = BusinessType.GRANT) + @PutMapping("/authRole") + public AjaxResult insertAuthRole(Long userId, Long[] roleIds) + { + userService.checkUserDataScope(userId); + userService.insertUserAuth(userId, roleIds); + return AjaxResult.success(); + } + + /** + * 获取部门树列表 + */ + // @PreAuthorize("@ss.hasPermi('system:user:list')") + @GetMapping("/deptTree") + public AjaxResult deptTree(SysDept dept) + { + return AjaxResult.success(deptService.selectDeptTreeList(dept)); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/TaskUtil.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/TaskUtil.java new file mode 100644 index 0000000..2fc75ee --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/TaskUtil.java @@ -0,0 +1,83 @@ +//package com.ruoyi.web.controller.task; +// +// +//import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +//import com.ruoyi.common.utils.SmsUtil; +//import com.ruoyi.system.mapper.TBillMapper; +//import com.ruoyi.system.model.TBill; +//import com.ruoyi.system.model.TContract; +//import com.ruoyi.system.model.TContractRentType; +//import com.ruoyi.system.service.TBillService; +//import com.ruoyi.system.service.TContractRentTypeService; +//import com.ruoyi.system.service.TContractService; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.scheduling.annotation.Scheduled; +//import org.springframework.stereotype.Component; +// +//import javax.annotation.Resource; +//import java.math.BigDecimal; +//import java.time.LocalDate; +//import java.time.LocalDateTime; +//import java.time.LocalTime; +//import java.time.ZoneId; +//import java.time.temporal.ChronoUnit; +//import java.time.temporal.TemporalAdjusters; +//import java.util.ArrayList; +//import java.util.Date; +//import java.util.List; +//import java.util.Random; +//import java.util.stream.Collectors; +// +///** +// * @author zhibing.pu +// * @date 2023/7/11 8:39 +// */ +//@Component +//public class TaskUtil { +// @Autowired +// private TContractService contractService; +// @Autowired +// private TBillMapper billMapper; +// // 用于更新违约金账单 +// // 每分钟执行一次的定时任务 +// +// @Scheduled(cron = "0 * * * * ?") +// public void dayOfProportionBill() { +// try { +// // 查询所有未缴费账单 +// List<TBill> list = billMapper.selectList(new LambdaQueryWrapper<TBill>().eq(TBill::getPayFeesStatus, 1) +// .le(TBill::getPayableFeesTime,LocalDate.now())); +// for (TBill tBill : list) { +// tBill.setPayFeesStatus("4"); +// TContract contract = contractService.getById(tBill.getContractId()); +// LocalDate payableFeesTime = tBill.getPayableFeesTime(); +// // 将LocalDate转化为LocalDateTime +// LocalDateTime payableFeesTime1 = LocalDateTime.of(payableFeesTime, LocalTime.of(0, 0, 0)); +// LocalDateTime now = LocalDateTime.now(); +// // 计算两个时间相差多少个小时 +// long hours = ChronoUnit.HOURS.between(payableFeesTime1, now); +// long l = hours / 24; +// if (l>=3){ +// // 违约金比例 +// BigDecimal proportion = contract.getProportion(); +// // 按每天 待缴费金额 * XX% 增加违约金费用 +// if (tBill.getOutstandingMoney().compareTo(new BigDecimal("0"))==0){ +// tBill.setPayFeesStatus("3"); +// billMapper.updateById(tBill); +// continue; +// } +// BigDecimal money = tBill.getOutstandingMoney().multiply(new BigDecimal(100).add(proportion)).divide(new BigDecimal(100),2, BigDecimal.ROUND_DOWN); +// tBill.setOverDays((int) l); +// tBill.setPayableFeesPenalty((tBill.getPayableFeesPenalty()!=null?tBill.getPayableFeesPenalty():BigDecimal.ZERO).add(money)); +// tBill.setOutstandingMoney(money); +// billMapper.updateById(tBill); +// +// } +// } +// } catch (Exception e) { +// e.printStackTrace(); +// } +// } +// +// +//} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/BaiDuApi.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/BaiDuApi.java new file mode 100644 index 0000000..ad7e1b8 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/BaiDuApi.java @@ -0,0 +1,180 @@ +package com.ruoyi.web.controller.tool; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.ruoyi.common.exception.ServiceException; +import lombok.extern.slf4j.Slf4j; +import okhttp3.*; + +import java.io.*; +import java.net.URLEncoder; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; + +/** + * 百度文字识别 + */ +@Slf4j +public class BaiDuApi { + + static final OkHttpClient HTTP_CLIENT = new OkHttpClient().newBuilder().build(); + + public static void main(String []args) throws IOException{ +// String url = "C:\\Users\\Admin\\Desktop\\picture\\6fd629ac-3327-4bdc-9459-fcb5295384bc.jpg"; + String url = "C:\\Users\\Admin\\Desktop\\picture\\6fd629ac-3327-4bdc-9459-fcb5295384bc.jpg"; + MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded"); + RequestBody body = RequestBody.create(mediaType, "image="+getFileContentAsBase64(url,true)); + Request request = new Request.Builder() + .url("https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic?access_token="+getAccessToken()) + .method("POST", body) + .addHeader("Content-Type", "application/x-www-form-urlencoded") + .addHeader("Accept", "application/json") + .build(); + Response response = HTTP_CLIENT.newCall(request).execute(); + +// System.err.println(response.body().string()); + + Map<String,Object> map = new HashMap<>(); + String string = response.body().string(); + String idCard = string.substring(string.lastIndexOf("单号:") + 3, string.lastIndexOf("单号:") + 27); + String time = string.substring(string.lastIndexOf("零时起") + 3, string.lastIndexOf("二十四时")-1); + time = time.replace(".","-"); + map.put("idCard",idCard); + map.put("time",time); + System.err.println(map); + } + + /** + * 资质证明证件识别 + * @param url + * @return + * @throws IOException + */ + public static Map<String,Object> qualification(String url) throws IOException { + MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded"); + RequestBody body = RequestBody.create(mediaType, "image="+getFileContentAsBase64(url,true)); + Request request = new Request.Builder() + .url("https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic?access_token="+getAccessToken()) + .method("POST", body) + .addHeader("Content-Type", "application/x-www-form-urlencoded") + .addHeader("Accept", "application/json") + .build(); + Response response = HTTP_CLIENT.newCall(request).execute(); + Map<String,Object> map = new HashMap<>(); + try{ + String string = response.body().string(); + String idCard = string.substring(string.lastIndexOf("号:") + 2, string.lastIndexOf("号:") + 21); + String time = string.substring(string.lastIndexOf("至") + 1, string.lastIndexOf("至") + 11); + time = time.replace(".","-"); + map.put("idCard",idCard); + map.put("time",time); + }catch (Exception e){ + log.error("资质证明证件识别错误!"); + throw new ServiceException("资质证明证件识别错误!"); + } + return map; + } + + /** + * 身份证识别 + * @param url + * @return + * @throws IOException + */ + public static Map<String,Object> idCard(String url) throws IOException { + MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded"); + RequestBody body = RequestBody.create(mediaType, "image="+getFileContentAsBase64(url,true)); + Request request = new Request.Builder() + .url("https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic?access_token="+getAccessToken()) + .method("POST", body) + .addHeader("Content-Type", "application/x-www-form-urlencoded") + .addHeader("Accept", "application/json") + .build(); + Response response = HTTP_CLIENT.newCall(request).execute(); + Map<String,Object> map = new HashMap<>(); + try{ + JSONObject jsonObject = JSONObject.parseObject(response.body().string()); + JSONArray jsonArray = JSONObject.parseArray(jsonObject.getString("words_result")); + JSONObject obj1 = JSONObject.parseObject(jsonArray.get(0).toString()); + String name = obj1.getString("words"); + name = name.substring(2); + + JSONObject obj2 = JSONObject.parseObject(jsonArray.get(1).toString()); + String sexStr = obj2.getString("words"); + sexStr = sexStr.substring(2, 3); + int sex = "男".equals(sexStr)?0:1; + + JSONObject obj3 = JSONObject.parseObject(jsonArray.get(3).toString()); + JSONObject obj4 = JSONObject.parseObject(jsonArray.get(4).toString()); + String address = obj3.getString("words") + obj4.getString("words"); + address = address.substring(2); + + JSONObject obj5 = JSONObject.parseObject(jsonArray.get(5).toString()); + String idCard = obj5.getString("words"); + idCard = idCard.substring(6); + + map.put("name",name); + map.put("sex",sex); + map.put("address",address); + map.put("idCard",idCard); + }catch (Exception e){ + log.error("身份证件识别错误!"); + throw new ServiceException("身份证件识别错误!"); + } + return map; + } + + /** + * 通用文字识别(标准版) + * @param url + * @return + * @throws IOException + */ + public static JSONObject pictureOcr(String url) throws IOException { + MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded"); + RequestBody body = RequestBody.create(mediaType, "image="+getFileContentAsBase64(url,true)); + Request request = new Request.Builder() + .url("https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic?access_token="+getAccessToken()) + .method("POST", body) + .addHeader("Content-Type", "application/x-www-form-urlencoded") + .addHeader("Accept", "application/json") + .build(); + Response response = HTTP_CLIENT.newCall(request).execute(); + return JSONObject.parseObject(response.body().string()); + } + + public static String getAccessToken() throws IOException { + MediaType mediaType = MediaType.parse("application/json"); + RequestBody body = RequestBody.create(mediaType, ""); + Request request = new Request.Builder() + .url("https://aip.baidubce.com/oauth/2.0/token?client_id=2RkKEqd0ltIHPvnIf3G0VpHE&client_secret=RBpPt3O64e3e4BK7pG3lP0o8I6SGgiUy&grant_type=client_credentials") + .method("POST", body) + .addHeader("Content-Type", "application/json") + .addHeader("Accept", "application/json") + .build(); + Response response = HTTP_CLIENT.newCall(request).execute(); + JSONObject jsonObject = JSONObject.parseObject(response.body().string()); + return jsonObject.getString("access_token"); + } + + /** + * 获取文件base64编码 + * + * @param path 文件路径 + * @param urlEncode 如果Content-Type是application/x-www-form-urlencoded时,传true + * @return base64编码信息,不带文件头 + * @throws IOException IO异常 + */ + static String getFileContentAsBase64(String path, boolean urlEncode) throws IOException { + byte[] b = Files.readAllBytes(Paths.get(path)); + String base64 = Base64.getEncoder().encodeToString(b); + if (urlEncode) { + base64 = URLEncoder.encode(base64, "utf-8"); + } + return base64; + } + +} \ No newline at end of file diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/HttpClientUtil.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/HttpClientUtil.java new file mode 100644 index 0000000..2da9a44 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/HttpClientUtil.java @@ -0,0 +1,79 @@ +package com.ruoyi.web.controller.tool; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; + +/** + * @author zhy + * @title: HttpClientUtil + * @projectName car_park + * @description: http连接工具类 + * @date 2019/10/2219:23 + */ +public class HttpClientUtil { + + + /** + * @param strUrl + * @return byte[] + * @throws + * @description: 获取网络图片转成字节流 + * @author zhy + * @date 2019/10/23 8:59 + */ + public static byte[] getImageFromNetByUrl(String strUrl) { + if (!isURL(strUrl)){ + return null; + } + try { + URL url = new URL(strUrl); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("GET"); + conn.setConnectTimeout(2 * 1000); + InputStream inStream = conn.getInputStream();// 通过输入流获取图片数据 + byte[] btImg = readInputStream(inStream);// 得到图片的二进制数据 + return btImg; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * 从输入流中获取字节流数据 + * + * @param inStream 输入流 + * @return + * @throws Exception + */ + public static byte[] readInputStream(InputStream inStream) throws Exception { + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[10240]; + int len = 0; + while ((len = inStream.read(buffer)) != -1) { + outStream.write(buffer, 0, len); + } + inStream.close(); + return outStream.toByteArray(); + } + + public static boolean isURL(String str) { + str = str.toLowerCase(); + String regex = "^((https|http|ftp|rtsp|mms)?://)" + + "?(([0-9a-z_!~*'().&=+$%-]+: )?[0-9a-z_!~*'().&=+$%-]+@)?" + + "(([0-9]{1,3}\\.){3}[0-9]{1,3}" + + "|" + + "([0-9a-z_!~*'()-]+\\.)*" + + "([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]\\." + + "[a-z]{2,6})" + + "(:[0-9]{1,5})?" + + "((/?)|" + + "(/[0-9a-z_!~*'().;?:@&=+$,%#-]+)+/?)$"; + return str.matches(regex); + } + + +} + \ No newline at end of file diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/ImportExcelUtil.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/ImportExcelUtil.java new file mode 100644 index 0000000..e488f03 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/ImportExcelUtil.java @@ -0,0 +1,39 @@ +package com.ruoyi.web.controller.tool; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.common.core.domain.R; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.util.List; + +/** + * 导出返回信息 + */ +@Slf4j +public class ImportExcelUtil { + + /** + * @param errorLines 错误行数 + * @param successLines 成功行数 + * @param errorMessage 错误信息 + * @return + * @throws IOException + */ + public static R<String> importReturnMsg(int errorLines, int successLines, List<String> errorMessage) throws IOException { + if (errorLines == 0) { + return R.ok("共" + successLines + "行数据全部导入成功!"); + } else { + JSONObject result = new JSONObject(5); + int totalCount = successLines + errorLines; + result.put("totalCount", totalCount); + result.put("errorCount", errorLines); + result.put("errorMessage", errorMessage); + result.put("successCount", successLines); + result.put("msg", "总上传行数:" + totalCount + ",已导入行数:" + successLines + ",错误行数:" + errorLines); + return R.ok(JSON.toJSONString(result)); + } + } + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/MsgCodeUtil.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/MsgCodeUtil.java new file mode 100644 index 0000000..073f315 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/MsgCodeUtil.java @@ -0,0 +1,61 @@ +package com.ruoyi.web.controller.tool; + +import com.alibaba.fastjson.JSONObject; +import com.ruoyi.system.code.SubmitTemplateReg; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.binary.Hex; + +import java.io.Serializable; +import java.nio.charset.StandardCharsets; + +/** + * 短信工具类 + */ +public class MsgCodeUtil implements Serializable { + + /**接口账号用户名*/ + private static final String AP_ID = ""; + /**企业名称*/ + private static final String EC_NAME = ""; + /**签名*/ + private static final String SECRET_KEY = ""; + /**签名编码*/ + private static final String SIGN = ""; + /**模板ID*/ + private static final String TEMPLATE_ID = ""; + + + /** + * 实体封装 + * @param code + * @return + */ + public static SubmitTemplateReg getSubmitTemplateReg(String code,String mobiles) { + SubmitTemplateReg submitReg =new SubmitTemplateReg(); + String[] paramss = {code}; + submitReg.setApId(AP_ID); + submitReg.setEcName(EC_NAME); + submitReg.setSecretKey(SECRET_KEY); + submitReg.setParams(JSONObject.toJSONString(paramss)); + submitReg.setMobiles(mobiles); + submitReg.setAddSerial(""); + submitReg.setSign(SIGN); + submitReg.setTemplateId(TEMPLATE_ID); + submitReg.setMac(TEMPLATE_ID); + StringBuffer stringBuffer = new StringBuffer(); + stringBuffer.append(submitReg.getEcName( ));stringBuffer.append(submitReg.getApId()); + stringBuffer.append(submitReg.getSecretKey());stringBuffer.append(submitReg.getTemplateId());stringBuffer.append(submitReg.getMobiles()); + stringBuffer.append(submitReg.getParams());stringBuffer.append(submitReg.getSign());stringBuffer.append(submitReg.getAddSerial()); + submitReg.setMac(Hex.encodeHexString(stringBuffer.toString().getBytes(StandardCharsets.UTF_8))); + String regText = JSONObject.toJSONString(submitReg); + //加密 + String encode = Base64.encodeBase64String(regText.getBytes()); + System.err.println(encode); + return submitReg; + } + + public static void main(String[] args) { + getSubmitTemplateReg("123456","18398968484"); + } + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/MsgUtils.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/MsgUtils.java new file mode 100644 index 0000000..119a5ea --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/MsgUtils.java @@ -0,0 +1,70 @@ +package com.ruoyi.web.controller.tool; + +import com.aliyun.dysmsapi20170525.models.SendSmsRequest; +import com.aliyun.dysmsapi20170525.models.SendSmsResponse; +import com.aliyun.tea.TeaException; +import com.aliyun.teaopenapi.models.Config; +import com.aliyun.teautil.models.RuntimeOptions; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class MsgUtils { + + @Value("${code.config.accessKeyId}") + private String accessKeyId; + @Value("${code.config.accessKeySecret}") + private String accessKeySecret; + @Value("${code.config.signName}") + private String signName; + @Value("${code.config.templateCode}") + private String templateCode; + @Value("${code.config.signNameTest}") + private String signNameTest; + @Value("${code.config.templateCodeTest}") + private String templateCodeTest; + + /** + * 使用AK&SK初始化账号Client + * @param accessKeyId + * @param accessKeySecret + * @return Client + * @throws Exception + */ + public static com.aliyun.dysmsapi20170525.Client createClient(String accessKeyId, String accessKeySecret) throws Exception { + Config config = new Config() + // 您的 AccessKey ID + .setAccessKeyId(accessKeyId) + // 您的 AccessKey Secret + .setAccessKeySecret(accessKeySecret); + // 访问的域名 + config.endpoint = "dysmsapi.aliyuncs.com"; + return new com.aliyun.dysmsapi20170525.Client(config); + } + + public void sendMsg(String phone,String code) throws Exception { + com.aliyun.dysmsapi20170525.Client client = MsgUtils.createClient(accessKeyId,accessKeySecret); + SendSmsRequest sendSmsRequest = new SendSmsRequest() + .setSignName(signName) + .setTemplateCode(templateCode) + .setPhoneNumbers(phone) + .setTemplateParam("{\"code\":\""+code+"\"}"); + RuntimeOptions runtime = new RuntimeOptions(); + try { + // 复制代码运行请自行打印 API 的返回值 + SendSmsResponse sendSmsResponse = client.sendSmsWithOptions(sendSmsRequest, runtime); + log.info("短信发送成功:{},{}",sendSmsResponse.getBody().getMessage(),sendSmsResponse.getStatusCode()); + } catch (TeaException error) { + // 如有需要,请打印 error + com.aliyun.teautil.Common.assertAsString(error.message); + log.info("短信发送失败:{}",error.message); + } catch (Exception _error) { + TeaException error = new TeaException(_error.getMessage(), _error); + // 如有需要,请打印 error + com.aliyun.teautil.Common.assertAsString(error.message); + log.info("短信发送失败:{}",error.message); + } + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/NumberToChineseUtils.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/NumberToChineseUtils.java new file mode 100644 index 0000000..cb8e7a8 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/NumberToChineseUtils.java @@ -0,0 +1,129 @@ +package com.ruoyi.web.controller.tool; + +public class NumberToChineseUtils { + + // 中文数字字符 + private static final char[] CHINESE_NUMBERS = {'零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'}; + // 单位字符 + private static final String[] UNITS = {"", "拾", "佰", "仟", "万", "拾", "佰", "仟", "亿"}; + + /** + * 数字转大写中文 + * + * @param num 需要转换的数字 + * @return 转换后的大写中文字符串 + */ + public static String numberToChinese(long num) { + if (num == 0) { + return "零"; + } + + StringBuilder result = new StringBuilder(); + String numStr = String.valueOf(num); + int unitIndex = 0; // 单位索引 + boolean zeroFlag = false; // 标记上一个字符是否为0 + + for (int i = numStr.length() - 1; i >= 0; i--) { + int digit = numStr.charAt(i) - '0'; + String part = toChinesePart(digit, unitIndex, zeroFlag); + result.insert(0, part); + + // 更新上一个字符是否为零的标记 + zeroFlag = digit == 0; + + // 更新单位索引 + if (unitIndex == 4 || unitIndex == 8) { + unitIndex = 1; // 万和亿后单位重置 + } else { + unitIndex++; + } + } + + // 去除结果字符串末尾的'零' + while (result.length() > 1 && result.charAt(result.length() - 1) == '零') { + result.deleteCharAt(result.length() - 1); + } + + return result.toString(); + } + + /** + * 转换数字部分并添加单位 + * + * @param digit 当前数字 + * @param unitIndex 单位索引 + * @param zeroFlag 上一个字符是否为零 + * @return 转换后的字符串 + */ + private static String toChinesePart(int digit, int unitIndex, boolean zeroFlag) { + if (digit == 0) { + return zeroFlag ? "" : "零"; + } + + String part = CHINESE_NUMBERS[digit] + UNITS[unitIndex]; + + // 连续两个'零'只保留一个 + if (part.equals("零零") || part.equals("零拾")) { + return "零"; + } + + // 去除'零拾'、'零佰'、'零仟' + if (part.startsWith("零")) { + part = part.substring(1); + } + + return part; + } + + /** + * 数字转大写中文(支持小数) + * + * @param number 需要转换的数字 + * @return 转换后的大写中文字符串 + */ + public static String numberToChinese(double number) { + if (number == 0) { + return "零"; + } + + StringBuilder result = new StringBuilder(); + String numStr = String.format("%.2f", number); // 保留两位小数 + String[] parts = numStr.split("\\."); + + // 整数部分 + if (!parts[0].equals("0")) { + result.append(numberToChinese(Long.parseLong(parts[0]))); + } + + // 小数部分 + if (parts.length > 1) { + String decimalPart = parts[1]; + if (!decimalPart.equals("00")) { + result.append("元"); + if (decimalPart.charAt(0) != '0') { + result.append(CHINESE_NUMBERS[decimalPart.charAt(0) - '0']).append("角"); + } + if (decimalPart.charAt(1) != '0') { + result.append(CHINESE_NUMBERS[decimalPart.charAt(1) - '0']).append("分"); + } + } else { + result.append("元整"); + } + } else { + result.append("元整"); + } + + return result.toString(); + } + + public static void main(String[] args) { + System.out.println(numberToChinese(123456)); // 输出: 壹拾贰万叁仟肆佰伍拾陆元整 + System.out.println(numberToChinese(10010)); // 输出: 壹万零壹拾元整 + System.out.println(numberToChinese(100000001)); // 输出: 壹亿零壹元整 + System.out.println(numberToChinese(68435)); // 输出: 陆万捌仟肆佰叁拾伍元整 + System.out.println(numberToChinese(24000)); // 输出: 捌仟伍佰肆拾叁元整 + System.out.println(numberToChinese(123.45)); // 输出: 壹佰贰拾叁元肆角伍分 + System.out.println(numberToChinese(0.67)); // 输出: 零元陆角柒分 + System.out.println(numberToChinese(100.00)); // 输出: 壹佰元整 + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/QRCodeUtil.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/QRCodeUtil.java new file mode 100644 index 0000000..9097589 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/QRCodeUtil.java @@ -0,0 +1,124 @@ +package com.ruoyi.web.controller.tool; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.EncodeHintType; +import com.google.zxing.MultiFormatWriter; +import com.google.zxing.WriterException; +import com.google.zxing.common.BitMatrix; +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; +import com.ruoyi.common.utils.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.imageio.ImageIO; +import javax.swing.filechooser.FileSystemView; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Map; + +/** + * 二维码工具 + * @Author:debug (SteadyJack) + * @Link: weixin-> debug0868 qq-> 1948831260 + * @Date: 2020/11/16 22:38 + **/ +public class QRCodeUtil { + private static final Logger log= LoggerFactory.getLogger(QRCodeUtil.class); + + //CODE_WIDTH:二维码宽度,单位像素 + private static final int CODE_WIDTH = 400; + //CODE_HEIGHT:二维码高度,单位像素 + private static final int CODE_HEIGHT = 400; + //FRONT_COLOR:二维码前景色,0x000000 表示黑色 + private static final int FRONT_COLOR = 0x000000; + //BACKGROUND_COLOR:二维码背景色,0xFFFFFF 表示白色 + //演示用 16 进制表示,和前端页面 CSS 的取色是一样的,注意前后景颜色应该对比明显,如常见的黑白 + private static final int BACKGROUND_COLOR = 0xFFFFFF; + + + public static void main(String[] args) { + createCodeToFile("5261548530",new File("C:\\Users\\Admin\\Desktop\\qrcode"),"5261548530.png"); + } + + public static void createCodeToFile(String content, File codeImgFileSaveDir, String fileName) { + try { + if (StringUtils.isBlank(content) || StringUtils.isBlank(fileName)) { + return; + } + content = content.trim(); + if (codeImgFileSaveDir==null || codeImgFileSaveDir.isFile()) { + //二维码图片存在目录为空,默认放在桌面... + codeImgFileSaveDir = FileSystemView.getFileSystemView().getHomeDirectory(); + } + if (!codeImgFileSaveDir.exists()) { + //二维码图片存在目录不存在,开始创建... + codeImgFileSaveDir.mkdirs(); + } + + //核心代码-生成二维码 + BufferedImage bufferedImage = getBufferedImage(content); + + File codeImgFile = new File(codeImgFileSaveDir, fileName); + ImageIO.write(bufferedImage, "png", codeImgFile); + + log.info("二维码图片生成成功:" + codeImgFile.getPath()); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 生成二维码并输出到输出流, 通常用于输出到网页上进行显示,输出到网页与输出到磁盘上的文件中,区别在于最后一句 ImageIO.write + * write(RenderedImage im,String formatName,File output):写到文件中 + * write(RenderedImage im,String formatName,OutputStream output):输出到输出流中 + * @param content :二维码内容 + * @param outputStream :输出流,比如 HttpServletResponse 的 getOutputStream + */ + public static void createCodeToOutputStream(String content, OutputStream outputStream) { + try { + if (StringUtils.isBlank(content)) { + return; + } + content = content.trim(); + //核心代码-生成二维码 + BufferedImage bufferedImage = getBufferedImage(content); + + //区别就是这一句,输出到输出流中,如果第三个参数是 File,则输出到文件中 + ImageIO.write(bufferedImage, "png", outputStream); + + log.info("二维码图片生成到输出流成功..."); + } catch (Exception e) { + e.printStackTrace(); + } + } + + //核心代码-生成二维码 + private static BufferedImage getBufferedImage(String content) throws WriterException { + + //com.google.zxing.EncodeHintType:编码提示类型,枚举类型 + Map<EncodeHintType, Object> hints = new HashMap(); + + //EncodeHintType.CHARACTER_SET:设置字符编码类型 + hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); + + //EncodeHintType.ERROR_CORRECTION:设置误差校正 + //ErrorCorrectionLevel:误差校正等级,L = ~7% correction、M = ~15% correction、Q = ~25% correction、H = ~30% correction + //不设置时,默认为 L 等级,等级不一样,生成的图案不同,但扫描的结果是一样的 + hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M); + + //EncodeHintType.MARGIN:设置二维码边距,单位像素,值越小,二维码距离四周越近 + hints.put(EncodeHintType.MARGIN, 1); + + MultiFormatWriter multiFormatWriter = new MultiFormatWriter(); + BitMatrix bitMatrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, CODE_WIDTH, CODE_HEIGHT, hints); + BufferedImage bufferedImage = new BufferedImage(CODE_WIDTH, CODE_HEIGHT, BufferedImage.TYPE_INT_BGR); + for (int x = 0; x < CODE_WIDTH; x++) { + for (int y = 0; y < CODE_HEIGHT; y++) { + bufferedImage.setRGB(x, y, bitMatrix.get(x, y) ? FRONT_COLOR : BACKGROUND_COLOR); + } + } + return bufferedImage; + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TestController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TestController.java new file mode 100644 index 0000000..1a14c49 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TestController.java @@ -0,0 +1,177 @@ +package com.ruoyi.web.controller.tool; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.StringUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import io.swagger.annotations.ApiOperation; + +/** + * swagger 用户测试方法 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/test/user") +public class TestController extends BaseController +{ + private final static Map<Integer, UserEntity> users = new LinkedHashMap<Integer, UserEntity>(); + { + users.put(1, new UserEntity(1, "admin", "admin123", "15888888888")); + users.put(2, new UserEntity(2, "ry", "admin123", "15666666666")); + } + + @GetMapping("/list") + public R<List<UserEntity>> userList() + { + List<UserEntity> userList = new ArrayList<UserEntity>(users.values()); + return R.ok(userList); + } + + @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class) + @GetMapping("/{userId}") + public R<UserEntity> getUser(@PathVariable Integer userId) + { + if (!users.isEmpty() && users.containsKey(userId)) + { + return R.ok(users.get(userId)); + } + else + { + return R.fail("用户不存在"); + } + } + + @ApiImplicitParams({ + @ApiImplicitParam(name = "userId", value = "用户id", dataType = "Integer", dataTypeClass = Integer.class), + @ApiImplicitParam(name = "username", value = "用户名称", dataType = "String", dataTypeClass = String.class), + @ApiImplicitParam(name = "password", value = "用户密码", dataType = "String", dataTypeClass = String.class), + @ApiImplicitParam(name = "mobile", value = "用户手机", dataType = "String", dataTypeClass = String.class) + }) + @PostMapping("/save") + public R<String> save(UserEntity user) + { + if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())) + { + return R.fail("用户ID不能为空"); + } + users.put(user.getUserId(), user); + return R.ok(); + } + + @PutMapping("/update") + public R<String> update(@RequestBody UserEntity user) + { + if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())) + { + return R.fail("用户ID不能为空"); + } + if (users.isEmpty() || !users.containsKey(user.getUserId())) + { + return R.fail("用户不存在"); + } + users.remove(user.getUserId()); + users.put(user.getUserId(), user); + return R.ok(); + } + + @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class) + @DeleteMapping("/{userId}") + public R<String> delete(@PathVariable Integer userId) + { + if (!users.isEmpty() && users.containsKey(userId)) + { + users.remove(userId); + return R.ok(); + } + else + { + return R.fail("用户不存在"); + } + } +} + +@ApiModel(value = "UserEntity", description = "用户实体") +class UserEntity +{ + @ApiModelProperty("用户ID") + private Integer userId; + + @ApiModelProperty("用户名称") + private String username; + + @ApiModelProperty("用户密码") + private String password; + + @ApiModelProperty("用户手机") + private String mobile; + + public UserEntity() + { + + } + + public UserEntity(Integer userId, String username, String password, String mobile) + { + this.userId = userId; + this.username = username; + this.password = password; + this.mobile = mobile; + } + + public Integer getUserId() + { + return userId; + } + + public void setUserId(Integer userId) + { + this.userId = userId; + } + + public String getUsername() + { + return username; + } + + public void setUsername(String username) + { + this.username = username; + } + + public String getPassword() + { + return password; + } + + public void setPassword(String password) + { + this.password = password; + } + + public String getMobile() + { + return mobile; + } + + public void setMobile(String mobile) + { + this.mobile = mobile; + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordPdfGenerator.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordPdfGenerator.java new file mode 100644 index 0000000..990e621 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordPdfGenerator.java @@ -0,0 +1,106 @@ +//package com.ruoyi.web.controller.tool; +// +//import org.apache.poi.xwpf.usermodel.*; +//import java.io.*; +//import java.time.LocalDateTime; +//import java.time.format.DateTimeFormatter; +// +//public class WordPdfGenerator { +// +// // 示例数据类 +// public static class UserData { +// private String name; +// private String id; +// private String department; +// private LocalDateTime createTime; +// +// public UserData(String name, String id, String department) { +// this.name = name; +// this.id = id; +// this.department = department; +// this.createTime = LocalDateTime.now(); +// } +// +// // getter方法省略 +// } +// +// public static void generateDocument(UserData userData, String outputPath) { +// try (XWPFDocument document = new XWPFDocument()) { +// // 创建标题 +// XWPFParagraph title = document.createParagraph(); +// title.setAlignment(ParagraphAlignment.CENTER); +// XWPFRun titleRun = title.createRun(); +// titleRun.setText("用户信息表"); +// titleRun.setBold(true); +// titleRun.setFontSize(20); +// titleRun.setFontFamily("宋体"); +// +// // 添加空行 +// document.createParagraph(); +// +// // 创建表格 +// XWPFTable table = document.createTable(5, 2); +// table.setWidth("100%"); +// +// // 设置表格数据 +// setCellText(table, 0, "姓名", userData.name); +// setCellText(table, 1, "ID", userData.id); +// setCellText(table, 2, "部门", userData.department); +// setCellText(table, 3, "创建时间", +// userData.createTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); +// +// // 保存Word文档 +// String wordFile = outputPath + ".docx"; +// try (FileOutputStream out = new FileOutputStream(wordFile)) { +// document.write(out); +// } +// +// // 转换为PDF +// convertToPdf(wordFile, outputPath + ".pdf"); +// +// } catch (Exception e) { +// e.printStackTrace(); +// } +// } +// +// private static void setCellText(XWPFTable table, int row, String label, String value) { +// XWPFTableRow tableRow = table.getRow(row); +// tableRow.getCell(0).setText(label); +// tableRow.getCell(1).setText(value); +// } +// +// private static void convertToPdf(String wordPath, String pdfPath) { +// try { +// // 使用LibreOffice进行转换 +// ProcessBuilder pb = new ProcessBuilder( +// "soffice", +// "--headless", +// "--convert-to", "pdf", +// "--outdir", new File(pdfPath).getParent(), +// wordPath +// ); +// Process process = pb.start(); +// +// // 等待转换完成 +// int exitCode = process.waitFor(); +// if (exitCode == 0) { +// System.out.println("PDF转换成功!"); +// } else { +// System.out.println("PDF转换失败!"); +// } +// +// // 删除临时Word文件 +// new File(wordPath).delete(); +// +// } catch (Exception e) { +// e.printStackTrace(); +// } +// } +// +// public static void main(String[] args) { +// // 示例使用 +// UserData userData = new UserData("张三", "EMP001", "技术部"); +// String outputPath = "E:\\"; +// generateDocument(userData, outputPath); +// } +//} \ No newline at end of file diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordTemplateProcessor.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordTemplateProcessor.java new file mode 100644 index 0000000..6b32fe3 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/WordTemplateProcessor.java @@ -0,0 +1,87 @@ +package com.ruoyi.web.controller.tool; + +import org.apache.poi.xwpf.usermodel.*; +import java.io.*; +import java.util.*; + +public class WordTemplateProcessor { + + public static void fillTemplate(String templatePath, String outputPath,Map<String, String> dataMap) { + try { + // 读取模板文件 + FileInputStream fis = new FileInputStream(templatePath); + XWPFDocument document = new XWPFDocument(fis); + + // 替换段落中的标记 + for (XWPFParagraph paragraph : document.getParagraphs()) { + replaceParagraph(paragraph, dataMap); + } + + // 替换表格中的标记 + for (XWPFTable table : document.getTables()) { + for (XWPFTableRow row : table.getRows()) { + for (XWPFTableCell cell : row.getTableCells()) { + for (XWPFParagraph paragraph : cell.getParagraphs()) { + replaceParagraph(paragraph, dataMap); + } + } + } + } + + // 保存文件 + FileOutputStream fos = new FileOutputStream(outputPath); + document.write(fos); + + // 关闭资源 + fos.close(); + fis.close(); + document.close(); + + System.out.println("模板填充完成!文件保存在: " + outputPath); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static void replaceParagraph(XWPFParagraph paragraph, Map<String, String> dataMap) { + String paragraphText = paragraph.getText(); + for (Map.Entry<String, String> entry : dataMap.entrySet()) { + if (paragraphText.contains(entry.getKey())) { + List<XWPFRun> runs = paragraph.getRuns(); + TextSegment found = paragraph.searchText(entry.getKey(), new PositionInParagraph()); + if (found != null) { + // 替换文本 + int beginRun = found.getBeginRun(); + int endRun = found.getEndRun(); + + if (beginRun >= 0 && endRun >= 0) { + // 删除原有runs + for (int runPos = beginRun; runPos <= endRun; runPos++) { + paragraph.removeRun(runPos); + } + // 创建新run + XWPFRun newRun = paragraph.insertNewRun(beginRun); + newRun.setText(entry.getValue()); + // 复制原有格式 + if (runs.size() > 0 && runs.get(0) != null) { + XWPFRun styleRun = runs.get(0); + newRun.setFontFamily(styleRun.getFontFamily()); + newRun.setFontSize(styleRun.getFontSize()); + newRun.setBold(styleRun.isBold()); + newRun.setItalic(styleRun.isItalic()); + } + } + } + } + } + } + + public static void main(String[] args) { + + String templatePath = "/path/to/template.docx"; + String outputPath = "/path/to/output.docx"; + +// fillTemplate(templatePath, outputPath, user); + } +} \ No newline at end of file diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/DataUpdateHandlerConfig.java b/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/DataUpdateHandlerConfig.java new file mode 100644 index 0000000..e5836a2 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/DataUpdateHandlerConfig.java @@ -0,0 +1,61 @@ +package com.ruoyi.web.core.config; + +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.reflection.MetaObject; +import org.springframework.context.annotation.Configuration; + +/** + * @author xiaochen + * @ClassName DataUpdateInterceptor + * @Description 数据更新操作处理 + * @date 2021-12-15 + * <p> + * 注意,之前在此处注入了 JwtTokenUtils + * <p> + * 造成spring循环依赖,项目支棱不起来 + */ +@Slf4j +@Configuration +public class DataUpdateHandlerConfig implements MetaObjectHandler { + + /** + * 新增数据执行 + * + * @param metaObject + */ + @Override + public void insertFill(MetaObject metaObject) { + // 获取登录信息 + String userName = SecurityUtils.getUsername(); + if (StringUtils.isNotBlank(userName)) { + this.setFieldValByName("createBy", userName, metaObject); + this.setFieldValByName("updateBy", userName, metaObject); + } else { + this.setFieldValByName("createBy", userName, metaObject); + this.setFieldValByName("updateBy", userName, metaObject); + } + + } + + /** + * 修改数据执行 + * + * @param metaObject + */ + @Override + public void updateFill(MetaObject metaObject) { + // 获取登录信息 + String userName = SecurityUtils.getUsername(); + if (StringUtils.isNotBlank(userName)){ + this.setFieldValByName("createBy", userName, metaObject); + this.setFieldValByName("updateBy", userName, metaObject); + } else { + this.setFieldValByName("createBy", userName, metaObject); + this.setFieldValByName("updateBy", userName, metaObject); + } + + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/FileUploaderConfig.java b/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/FileUploaderConfig.java new file mode 100644 index 0000000..d87ab0d --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/FileUploaderConfig.java @@ -0,0 +1,67 @@ +package com.ruoyi.web.core.config; + +import com.qcloud.cos.COSClient; +import com.qcloud.cos.ClientConfig; +import com.qcloud.cos.auth.BasicCOSCredentials; +import com.qcloud.cos.auth.COSCredentials; +import com.qcloud.cos.http.HttpProtocol; +import com.qcloud.cos.region.Region; +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@Data +public class FileUploaderConfig { + + /** + * COS的SecretId + */ + @Value("${cos.client.accessKey}") + private String secretId; + /** + * COS的SecretKey + */ + @Value("${cos.client.secretKey}") + private String secretKey; + /** + * 文件上传后访问路径的根路径,后面要最佳文件名字与类型 + */ + @Value("${cos.client.rootSrc}") + private String rootSrc; + /** + * 上传的存储桶的地域 + */ + @Value("${cos.client.bucketAddr}") + private String bucketAddr; + /** + * 存储桶的名字,是自己在存储空间自己创建的,我创建的名字是:qq-test-1303****** + */ + @Value("${cos.client.bucket}") + private String bucketName; + /** + * 文件存放位置 + */ + @Value("${cos.client.location}") + private String location; + + @Value("${file.url.prefix}") + private String fileUrlPrefix; + + + @Bean + public COSClient cosClient() { + // 1 初始化用户身份信息(secretId, secretKey)。 + COSCredentials cred = new BasicCOSCredentials(secretId, secretKey); + // 2.1 设置存储桶的地域(上文获得) + Region region = new Region(bucketAddr); + ClientConfig clientConfig = new ClientConfig(region); + // 2.2 使用https协议传输 + clientConfig.setHttpProtocol(HttpProtocol.https); + // 生成 cos 客户端 + return new COSClient(cred, clientConfig); + } + + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/MybatisPlusConfig.java b/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/MybatisPlusConfig.java new file mode 100644 index 0000000..fac9a9f --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/MybatisPlusConfig.java @@ -0,0 +1,51 @@ +package com.ruoyi.web.core.config; + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.core.config.GlobalConfig; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author liheng + * @ClassName MybatisPlusConfig + * @Description MybatisPlus相关配置 + * @date 2020-09-22 11:22、 + * 直接以实现类作为bean的注入(有事务管理的类) + * @EnableTransactionManagement(proxyTargetClass = true) + */ +@Configuration +public class MybatisPlusConfig { + private final DataUpdateHandlerConfig dataUpdateHandler; + + @Autowired + public MybatisPlusConfig(DataUpdateHandlerConfig dataUpdateHandler) { + this.dataUpdateHandler = dataUpdateHandler; + } + + /** + * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题 + */ + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); + return interceptor; + } + + /** + * 自动填充功能 + * + * @return + */ + @Bean + public GlobalConfig globalConfig() { + GlobalConfig globalConfig = new GlobalConfig(); + globalConfig.setMetaObjectHandler(dataUpdateHandler); + return globalConfig; + } + + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java b/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java new file mode 100644 index 0000000..b6f4214 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java @@ -0,0 +1,125 @@ +package com.ruoyi.web.core.config; + +import java.util.ArrayList; +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import com.ruoyi.common.config.RuoYiConfig; +import io.swagger.annotations.ApiOperation; +import io.swagger.models.auth.In; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.ApiKey; +import springfox.documentation.service.AuthorizationScope; +import springfox.documentation.service.Contact; +import springfox.documentation.service.SecurityReference; +import springfox.documentation.service.SecurityScheme; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spi.service.contexts.SecurityContext; +import springfox.documentation.spring.web.plugins.Docket; + +/** + * Swagger2的接口配置 + * + * @author ruoyi + */ +@Configuration +public class SwaggerConfig +{ + /** 系统基础配置 */ + @Autowired + private RuoYiConfig ruoyiConfig; + + /** 是否开启swagger */ + @Value("${swagger.enabled}") + private boolean enabled; + + /** 设置请求的统一前缀 */ + @Value("${swagger.pathMapping}") + private String pathMapping; + + /** + * 创建API + */ + @Bean + public Docket createRestApi() + { + return new Docket(DocumentationType.OAS_30) + // 是否启用Swagger + .enable(enabled) + // 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息) + .apiInfo(apiInfo()) + // 设置哪些接口暴露给Swagger展示 + .select() + // 扫描所有有注解的api,用这种方式更灵活 + .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) + // 扫描指定包中的swagger注解 + // .apis(RequestHandlerSelectors.basePackage("com.ruoyi.project.tool.swagger")) + // 扫描所有 .apis(RequestHandlerSelectors.any()) + .paths(PathSelectors.any()) + .build() + /* 设置安全模式,swagger可以设置访问token */ + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()) + .pathMapping(pathMapping); + } + + /** + * 安全模式,这里指定token通过Authorization头请求头传递 + */ + private List<SecurityScheme> securitySchemes() + { + List<SecurityScheme> apiKeyList = new ArrayList<SecurityScheme>(); + apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue())); + return apiKeyList; + } + + /** + * 安全上下文 + */ + private List<SecurityContext> securityContexts() + { + List<SecurityContext> securityContexts = new ArrayList<>(); + securityContexts.add( + SecurityContext.builder() + .securityReferences(defaultAuth()) + .operationSelector(o -> o.requestMappingPattern().matches("/.*")) + .build()); + return securityContexts; + } + + /** + * 默认的安全上引用 + */ + private List<SecurityReference> defaultAuth() + { + AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); + AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; + authorizationScopes[0] = authorizationScope; + List<SecurityReference> securityReferences = new ArrayList<>(); + securityReferences.add(new SecurityReference("Authorization", authorizationScopes)); + return securityReferences; + } + + /** + * 添加摘要信息 + */ + private ApiInfo apiInfo() + { + // 用ApiInfoBuilder进行定制 + return new ApiInfoBuilder() + // 设置标题 + .title("标题:证省心管理系统_接口文档") + // 描述 + .description("证省心接口文档") + // 作者信息 + .contact(new Contact(ruoyiConfig.getName(), null, null)) + // 版本 + .version("版本号:" + ruoyiConfig.getVersion()) + .build(); + } +} diff --git a/ruoyi-admin/src/main/resources/META-INF/spring-devtools.properties b/ruoyi-admin/src/main/resources/META-INF/spring-devtools.properties new file mode 100644 index 0000000..2b23f85 --- /dev/null +++ b/ruoyi-admin/src/main/resources/META-INF/spring-devtools.properties @@ -0,0 +1 @@ +restart.include.json=/com.alibaba.fastjson.*.jar \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/application-prod.yml b/ruoyi-admin/src/main/resources/application-prod.yml new file mode 100644 index 0000000..b00292d --- /dev/null +++ b/ruoyi-admin/src/main/resources/application-prod.yml @@ -0,0 +1,237 @@ +# 项目相关配置 +ruoyi: + # 名称 + name: RuoYi + # 版本 + version: 3.8.6 + # 版权年份 + copyrightYear: 2023 + # 实例演示开关 + demoEnabled: true + # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) + profile: D:/ruoyi/uploadPath + # 获取ip地址开关 + addressEnabled: false + # 验证码类型 math 数字计算 char 字符验证 + captchaType: math +# 开发环境配置 +server: + # 服务器的HTTP端口,默认为8080 + port: 8080 + servlet: + # 应用的访问路径 + context-path: / + tomcat: + # tomcat的URI编码 + uri-encoding: UTF-8 + # 连接数满后的排队数,默认为100 + accept-count: 1000 + threads: + # tomcat最大线程数,默认为200 + max: 800 + # Tomcat启动初始化的线程数,默认值10 + min-spare: 100 + +# 日志配置 +logging: + level: + com.ruoyi: debug + org.springframework: warn + +# 用户配置 +user: + password: + # 密码最大错误次数 + maxRetryCount: 5 + # 密码锁定时间(默认10分钟) + lockTime: 10 + +# Spring配置 +spring: + main: + allow-bean-definition-overriding: true + # 资源信息 + messages: + # 国际化资源文件路径 + basename: i18n/messages + # 文件上传 + servlet: + multipart: + # 单个文件大小 + max-file-size: 500MB + # 设置总上传的文件大小 + max-request-size: 2000MB + # 服务模块 + devtools: + restart: + # 热部署开关 + enabled: true + # redis 配置 + redis: + # 地址 + # host: 127.0.0.1 + # # 端口,默认为6379 + # port: 6379 + # # 数据库索引 + # database: 0 + # # 密码 + # password: 123456 + host: 127.0.0.1 + # 端口,默认为6379 + port: 16379 + # 数据库索引 + database: 0 + # 密码 + password: 8f5z9g52gx4bg + # 连接超时时间 + timeout: 10s + lettuce: + pool: + # 连接池中的最小空闲连接 + min-idle: 0 + # 连接池中的最大空闲连接 + max-idle: 8 + # 连接池的最大数据库连接数 + max-active: 8 + # #连接池最大阻塞等待时间(使用负值表示没有限制) + max-wait: -1ms + # 数据源配置 + datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + druid: + # 主库数据源 + master: + url: jdbc:mysql://172.27.0.13:3306/xizang?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai + username: xzgt + password: changyun!6f2gshj6h3j + # 从库数据源 + slave: + # 从数据源开关/默认关闭 + enabled: false + url: + username: + password: + # 初始连接数 + initialSize: 5 + # 最小连接池数量 + minIdle: 10 + # 最大连接池数量 + maxActive: 20 + # 配置获取连接等待超时的时间 + maxWait: 60000 + # 配置连接超时时间 + connectTimeout: 30000 + # 配置网络超时时间 + socketTimeout: 60000 + # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 60000 + # 配置一个连接在池中最小生存的时间,单位是毫秒 + minEvictableIdleTimeMillis: 300000 + # 配置一个连接在池中最大生存的时间,单位是毫秒 + maxEvictableIdleTimeMillis: 900000 + # 配置检测连接是否有效 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + webStatFilter: + enabled: true + statViewServlet: + enabled: true + # 设置白名单,不填则允许所有访问 + allow: + url-pattern: /druid/* + # 控制台管理用户名和密码 + login-username: ruoyi + login-password: 123456 + filter: + stat: + enabled: true + # 慢SQL记录 + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: true + wall: + config: + multi-statement-allow: true +# token配置 +token: + # 令牌自定义标识 + header: Authorization + # 令牌密钥 + secret: abcdefghijklmnopqrstuvwxyz + # 令牌有效期(默认30分钟) + expireTime: 120 + +mybatis-plus: + # 此处在多数据源中生效 + config-location: classpath:/mybatis-config.xml + global-config: + banner: false + db-config: + logic-not-delete-value: 0 + logic-delete-value: 1 + type-aliases-package: com.ruoyi.**.domain,com.ruoyi.**.vo,com.ruoyi.**.model + # 指定Mapper文件位置 + mapper-locations: classpath*:mapper/**/*.xml + +# Swagger配置 +swagger: + # 是否开启swagger + enabled: true + # 请求前缀 + pathMapping: / + +# 防止XSS攻击 +xss: + # 过滤开关 + enabled: true + # 排除链接(多个用逗号分隔) + excludes: /system/notice + # 匹配链接 + urlPatterns: /system/*,/monitor/*,/tool/* +# file upload +file: + upload: + location: /file/ + qrLocation: /file/qrCode/ + accessPath: /file/ + allowExt: .jpg|.png|.gif|.jpeg|.doc|.docx|.apk|.MP4|.mp4|.pdf|.PDF + url: + prefix: https://xzgt.test.591taxi.cn:${server.port}${server.servlet.context-path} +wx: + conf: + appId: wxe91f1af7638aa5dd + secretId: a787e1a462715604e0c9528b6d8960d1 +#OSS及短信配置 +code: + config: + templateCodeTest: "SMS_154950909" + signNameTest: "阿里云短信测试" + accessKeyId: LTAI5tAdba8HtT1C6UqtSxBt + accessKeySecret: 0SRb6XGkciQDPWn2rYqbJtq2qRMDY8 + signName: "四川金达通信工程" + templateCode: "SMS_293985284" +cos: + client: + accessKey: AKIDCF5EF2c0DE1e5JK8r4EGJF4mNsMgp26x + secretKey: lLl184rUyFOOE0d5KNGC3kmfNsCWk4GU + bucket: xzgttest-1305134071 + bucketAddr: ap-chengdu + rootSrc: https://xzgttest-1305134071.cos.ap-chengdu.myqcloud.com/ + location: /xizang +sms: + enable: true + appId: 1400957506 + secretid: AKIDCF5EF2c0DE1e5JK8r4EGJF4mNsMgp26x + secretkey: lLl184rUyFOOE0d5KNGC3kmfNsCWk4GU + sign: 畅云出行 +com: + taxi591: + bank: + cer-path: /usr/local/bank/TrustPay.cer + base-url: http://hello.enjoy.abchina.com + enable: true + keystore-password: gggs6666 + pfx-path: /usr/local/bank/103882597000441.pfx \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/application-test.yml b/ruoyi-admin/src/main/resources/application-test.yml new file mode 100644 index 0000000..a4337c7 --- /dev/null +++ b/ruoyi-admin/src/main/resources/application-test.yml @@ -0,0 +1,237 @@ +# 项目相关配置 +ruoyi: + # 名称 + name: RuoYi + # 版本 + version: 3.8.6 + # 版权年份 + copyrightYear: 2023 + # 实例演示开关 + demoEnabled: true + # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) + profile: D:/ruoyi/uploadPath + # 获取ip地址开关 + addressEnabled: false + # 验证码类型 math 数字计算 char 字符验证 + captchaType: math +# 开发环境配置 +server: + # 服务器的HTTP端口,默认为8080 + port: 9081 + servlet: + # 应用的访问路径 + context-path: / + tomcat: + # tomcat的URI编码 + uri-encoding: UTF-8 + # 连接数满后的排队数,默认为100 + accept-count: 1000 + threads: + # tomcat最大线程数,默认为200 + max: 800 + # Tomcat启动初始化的线程数,默认值10 + min-spare: 100 + +# 日志配置 +logging: + level: + com.ruoyi: debug + org.springframework: warn + +# 用户配置 +user: + password: + # 密码最大错误次数 + maxRetryCount: 5 + # 密码锁定时间(默认10分钟) + lockTime: 10 + +# Spring配置 +spring: + main: + allow-bean-definition-overriding: true + # 资源信息 + messages: + # 国际化资源文件路径 + basename: i18n/messages + # 文件上传 + servlet: + multipart: + # 单个文件大小 + max-file-size: 500MB + # 设置总上传的文件大小 + max-request-size: 2000MB + # 服务模块 + devtools: + restart: + # 热部署开关 + enabled: true + # redis 配置 + redis: + # 地址 + host: 127.0.0.1 +# # 端口,默认为6379 +# port: 6379 +# # 数据库索引 +# database: 0 +# # 密码 +# password: 123456 + # 端口,默认为6379 + port: 6379 + # 数据库索引 + database: 0 + # 密码 + password: 123456 + # 连接超时时间 + timeout: 10s + lettuce: + pool: + # 连接池中的最小空闲连接 + min-idle: 0 + # 连接池中的最大空闲连接 + max-idle: 8 + # 连接池的最大数据库连接数 + max-active: 8 + # #连接池最大阻塞等待时间(使用负值表示没有限制) + max-wait: -1ms +# 数据源配置 + datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + druid: + # 主库数据源 + master: + url: jdbc:mysql://192.168.110.111:3306/zhengshengxin?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai + username: root + password: 123456 + # 从库数据源 + slave: + # 从数据源开关/默认关闭 + enabled: false + url: + username: + password: + # 初始连接数 + initialSize: 5 + # 最小连接池数量 + minIdle: 10 + # 最大连接池数量 + maxActive: 20 + # 配置获取连接等待超时的时间 + maxWait: 60000 + # 配置连接超时时间 + connectTimeout: 30000 + # 配置网络超时时间 + socketTimeout: 60000 + # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 60000 + # 配置一个连接在池中最小生存的时间,单位是毫秒 + minEvictableIdleTimeMillis: 300000 + # 配置一个连接在池中最大生存的时间,单位是毫秒 + maxEvictableIdleTimeMillis: 900000 + # 配置检测连接是否有效 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + webStatFilter: + enabled: true + statViewServlet: + enabled: true + # 设置白名单,不填则允许所有访问 + allow: + url-pattern: /druid/* + # 控制台管理用户名和密码 + login-username: ruoyi + login-password: 123456 + filter: + stat: + enabled: true + # 慢SQL记录 + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: true + wall: + config: + multi-statement-allow: true +# token配置 +token: + # 令牌自定义标识 + header: Authorization + # 令牌密钥 + secret: abcdefghijklmnopqrstuvwxyz + # 令牌有效期(默认30分钟) + expireTime: 1 + +mybatis-plus: + # 此处在多数据源中生效 + config-location: classpath:/mybatis-config.xml + global-config: + banner: false + db-config: + logic-not-delete-value: 0 + logic-delete-value: 1 + type-aliases-package: com.ruoyi.**.domain,com.ruoyi.**.vo,com.ruoyi.**.model + # 指定Mapper文件位置 + mapper-locations: classpath*:mapper/**/*.xml + +# Swagger配置 +swagger: + # 是否开启swagger + enabled: true + # 请求前缀 + pathMapping: / + +# 防止XSS攻击 +xss: + # 过滤开关 + enabled: true + # 排除链接(多个用逗号分隔) + excludes: /system/notice + # 匹配链接 + urlPatterns: /system/*,/monitor/*,/tool/* +# file upload +file: + upload: + location: /file/ + qrLocation: /file/qrCode/ + accessPath: /file/ + allowExt: .jpg|.png|.gif|.jpeg|.doc|.docx|.apk|.MP4|.mp4|.pdf|.PDF + url: +# prefix: http://localhost:${server.port}${server.servlet.context-path} + prefix: https://xzgt.test.591taxi.cn:${server.port}${server.servlet.context-path} +wx: + conf: + appId: wxe91f1af7638aa5dd + secretId: a787e1a462715604e0c9528b6d8960d1 +#OSS及短信配置 +code: + config: + templateCodeTest: "SMS_154950909" + signNameTest: "阿里云短信测试" + accessKeyId: LTAI5tAdba8HtT1C6UqtSxBt + accessKeySecret: 0SRb6XGkciQDPWn2rYqbJtq2qRMDY8 + signName: "四川金达通信工程" + templateCode: "SMS_293985284" +cos: + client: + accessKey: AKIDCF5EF2c0DE1e5JK8r4EGJF4mNsMgp26x + secretKey: lLl184rUyFOOE0d5KNGC3kmfNsCWk4GU + bucket: xzgttest-1305134071 + bucketAddr: ap-chengdu + rootSrc: https://xzgttest-1305134071.cos.ap-chengdu.myqcloud.com/ + location: /xizang +sms: + enable: true + appId: 1400957506 + secretid: AKIDCF5EF2c0DE1e5JK8r4EGJF4mNsMgp26x + secretkey: lLl184rUyFOOE0d5KNGC3kmfNsCWk4GU + sign: 畅云出行 +com: + taxi591: + bank: + cer-path: D:\workspaces\工作文件\畅云\农业银行\正式\TrustPay.cer + base-url: http://hello.enjoy.abchina.com + enable: true + keystore-password: gggs6666 + pfx-path: D:\workspaces\工作文件\畅云\农业银行\正式\103882597000441.pfx \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml new file mode 100644 index 0000000..dcc106d --- /dev/null +++ b/ruoyi-admin/src/main/resources/application.yml @@ -0,0 +1,4 @@ +# 项目相关配置 +spring: + profiles: + active: test diff --git a/ruoyi-admin/src/main/resources/banner.txt b/ruoyi-admin/src/main/resources/banner.txt new file mode 100644 index 0000000..0931cb8 --- /dev/null +++ b/ruoyi-admin/src/main/resources/banner.txt @@ -0,0 +1,24 @@ +Application Version: ${ruoyi.version} +Spring Boot Version: ${spring-boot.version} +//////////////////////////////////////////////////////////////////// +// _ooOoo_ // +// o8888888o // +// 88" . "88 // +// (| ^_^ |) // +// O\ = /O // +// ____/`---'\____ // +// .' \\| |// `. // +// / \\||| : |||// \ // +// / _||||| -:- |||||- \ // +// | | \\\ - /// | | // +// | \_| ''\---/'' | | // +// \ .-\__ `-` ___/-. / // +// ___`. .' /--.--\ `. . ___ // +// ."" '< `.___\_<|>_/___.' >'"". // +// | | : `- \`.;`\ _ /`;.`/ - ` : | | // +// \ \ `-. \_ __\ /__ _/ .-` / / // +// ========`-.____`-.___\_____/___.-`____.-'======== // +// `=---=' // +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // +// 佛祖保佑 永不宕机 永无BUG // +//////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/i18n/messages.properties b/ruoyi-admin/src/main/resources/i18n/messages.properties new file mode 100644 index 0000000..93de005 --- /dev/null +++ b/ruoyi-admin/src/main/resources/i18n/messages.properties @@ -0,0 +1,38 @@ +#错误消息 +not.null=* 必须填写 +user.jcaptcha.error=验证码错误 +user.jcaptcha.expire=验证码已失效 +user.not.exists=用户不存在/密码错误 +user.password.not.match=用户不存在/密码错误 +user.password.retry.limit.count=密码输入错误{0}次 +user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟 +user.password.delete=对不起,您的账号已被删除 +user.blocked=用户已封禁,请联系管理员 +role.blocked=角色已封禁,请联系管理员 +login.blocked=很遗憾,访问IP已被列入系统黑名单 +user.logout.success=退出成功 + +length.not.valid=长度必须在{min}到{max}个字符之间 + +user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头 +user.password.not.valid=* 5-50个字符 + +user.email.not.valid=邮箱格式错误 +user.mobile.phone.number.not.valid=手机号格式错误 +user.login.success=登录成功 +user.register.success=注册成功 +user.notfound=请重新登录 +user.forcelogout=管理员强制退出,请重新登录 +user.unknown.error=未知错误,请重新登录 + +##文件上传消息 +upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB! +upload.filename.exceed.length=上传的文件名最长{0}个字符 + +##权限 +no.permission=您没有数据的权限,请联系管理员添加权限 [{0}] +no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}] +no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}] +no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}] +no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}] +no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}] diff --git a/ruoyi-admin/src/main/resources/logback.xml b/ruoyi-admin/src/main/resources/logback.xml new file mode 100644 index 0000000..a360583 --- /dev/null +++ b/ruoyi-admin/src/main/resources/logback.xml @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="UTF-8"?> +<configuration> + <!-- 日志存放路径 --> + <property name="log.path" value="/home/ruoyi/logs" /> + <!-- 日志输出格式 --> + <property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" /> + + <!-- 控制台输出 --> + <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> + <encoder> + <pattern>${log.pattern}</pattern> + </encoder> + </appender> + + <!-- 系统日志输出 --> + <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>${log.path}/sys-info.log</file> + <!-- 循环政策:基于时间创建日志文件 --> + <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> + <!-- 日志文件名格式 --> + <fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern> + <!-- 日志最大的历史 60天 --> + <maxHistory>60</maxHistory> + </rollingPolicy> + <encoder> + <pattern>${log.pattern}</pattern> + </encoder> + <filter class="ch.qos.logback.classic.filter.LevelFilter"> + <!-- 过滤的级别 --> + <level>INFO</level> + <!-- 匹配时的操作:接收(记录) --> + <onMatch>ACCEPT</onMatch> + <!-- 不匹配时的操作:拒绝(不记录) --> + <onMismatch>DENY</onMismatch> + </filter> + </appender> + + <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>${log.path}/sys-error.log</file> + <!-- 循环政策:基于时间创建日志文件 --> + <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> + <!-- 日志文件名格式 --> + <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern> + <!-- 日志最大的历史 60天 --> + <maxHistory>60</maxHistory> + </rollingPolicy> + <encoder> + <pattern>${log.pattern}</pattern> + </encoder> + <filter class="ch.qos.logback.classic.filter.LevelFilter"> + <!-- 过滤的级别 --> + <level>ERROR</level> + <!-- 匹配时的操作:接收(记录) --> + <onMatch>ACCEPT</onMatch> + <!-- 不匹配时的操作:拒绝(不记录) --> + <onMismatch>DENY</onMismatch> + </filter> + </appender> + + <!-- 用户访问日志输出 --> + <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>${log.path}/sys-user.log</file> + <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> + <!-- 按天回滚 daily --> + <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern> + <!-- 日志最大的历史 60天 --> + <maxHistory>60</maxHistory> + </rollingPolicy> + <encoder> + <pattern>${log.pattern}</pattern> + </encoder> + </appender> + + <!-- 系统模块日志级别控制 --> + <logger name="com.ruoyi" level="info" /> + <!-- Spring日志级别控制 --> + <logger name="org.springframework" level="warn" /> + + <root level="info"> + <appender-ref ref="console" /> + </root> + + <!--系统操作日志--> + <root level="info"> + <appender-ref ref="file_info" /> + <appender-ref ref="file_error" /> + </root> + + <!--系统用户操作日志--> + <logger name="sys-user" level="info"> + <appender-ref ref="sys-user"/> + </logger> +</configuration> \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/mybatis-config.xml b/ruoyi-admin/src/main/resources/mybatis-config.xml new file mode 100644 index 0000000..53c5587 --- /dev/null +++ b/ruoyi-admin/src/main/resources/mybatis-config.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> +<configuration> + + <settings> + <!-- 打印查询语句 不会写入到日志文件中--> + <setting name="logImpl" value="STDOUT_LOGGING"/> + <!--<setting name="logImpl" value="LOG4J" />--> + <!-- 控制全局缓存(二级缓存),按美团技术团队的说法,尽量别用缓存机制 emmmm.... --> + <setting name="cacheEnabled" value="true"/> + <!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认 false --> + <!-- <setting name="lazyLoadingEnabled" value="true"/> --> + <setting name="mapUnderscoreToCamelCase" value="true"/><!--是否将map下划线方式转为驼峰式命名--> + <!-- 当开启时,任何方法的调用都会加载该对象的所有属性。默认 false,可通过select标签的 fetchType来覆盖--> + <!-- <setting name="aggressiveLazyLoading" value="false"/>--> + <!-- Mybatis 创建具有延迟加载能力的对象所用到的代理工具,默认JAVASSIST --> + <!--<setting name="proxyFactory" value="CGLIB" />--> + <!-- 关于mybatis的一二级缓存 请参照:https://tech.meituan.com/2018/01/19/mybatis-cache.html --> + <!-- 一级缓存范围默认:SESSION ,此范围在复杂应用场景中可能会出现脏读数据--> + <!-- STATEMENT级别的缓存,使一级缓存,只针对当前执行的这一statement有效 --> + <!--<setting name="localCacheScope" value="STATEMENT"/>--> + <setting name="localCacheScope" value="STATEMENT"/> + </settings> + +</configuration> diff --git a/ruoyi-admin/src/main/resources/mybatis/mybatis-config.xml b/ruoyi-admin/src/main/resources/mybatis/mybatis-config.xml new file mode 100644 index 0000000..ac47c03 --- /dev/null +++ b/ruoyi-admin/src/main/resources/mybatis/mybatis-config.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE configuration +PUBLIC "-//mybatis.org//DTD Config 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-config.dtd"> +<configuration> + <!-- 全局参数 --> + <settings> + <!-- 使全局的映射器启用或禁用缓存 --> + <setting name="cacheEnabled" value="true" /> + <!-- 允许JDBC 支持自动生成主键 --> + <setting name="useGeneratedKeys" value="true" /> + <!-- 配置默认的执行器.SIMPLE就是普通执行器;REUSE执行器会重用预处理语句(prepared statements);BATCH执行器将重用语句并执行批量更新 --> + <setting name="defaultExecutorType" value="SIMPLE" /> + <!-- 指定 MyBatis 所用日志的具体实现 --> + <setting name="logImpl" value="SLF4J" /> + <!-- 使用驼峰命名法转换字段 --> + <!-- <setting name="mapUnderscoreToCamelCase" value="true"/> --> + </settings> + +</configuration> diff --git a/ruoyi-applet/pom.xml b/ruoyi-applet/pom.xml new file mode 100644 index 0000000..be7fad7 --- /dev/null +++ b/ruoyi-applet/pom.xml @@ -0,0 +1,175 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>zhengshengxin</artifactId> + <groupId>com.ruoyi</groupId> + <version>3.8.6</version> + </parent> + <modelVersion>4.0.0</modelVersion> + <packaging>jar</packaging> + <artifactId>ruoyi-applet</artifactId> + + <description> + web服务入口 + </description> + + <dependencies> + <dependency> + <groupId>com.aliyun.oss</groupId> + <artifactId>aliyun-sdk-oss</artifactId> + <version>3.8.0</version> + </dependency> + <!-- spring-boot-devtools --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-devtools</artifactId> + <optional>true</optional> <!-- 表示依赖不会传递 --> + </dependency> + + <!-- swagger3--> + <dependency> + <groupId>io.springfox</groupId> + <artifactId>springfox-boot-starter</artifactId> + </dependency> + +<!-- <dependency>--> +<!-- <groupId>com.github.xiaoymin</groupId>--> +<!-- <artifactId>swagger-bootstrap-ui</artifactId>--> +<!-- <version>1.9.6</version>--> +<!-- </dependency>--> + + + <!-- 防止进入swagger页面报类型转换错误,排除3.0.0中的引用,手动增加1.6.2版本 --> + <dependency> + <groupId>io.swagger</groupId> + <artifactId>swagger-models</artifactId> + <version>1.6.2</version> + </dependency> + + <!-- Mysql驱动包 --> + <dependency> + <groupId>mysql</groupId> + <artifactId>mysql-connector-java</artifactId> + </dependency> + + <!-- 核心模块--> + <dependency> + <groupId>com.ruoyi</groupId> + <artifactId>ruoyi-framework</artifactId> +<!-- <exclusions>--> +<!-- <exclusion>--> +<!-- <groupId>com.github.pagehelper</groupId>--> +<!-- <artifactId>pagehelper-spring-boot-starter</artifactId>--> +<!-- </exclusion>--> +<!-- </exclusions>--> + </dependency> + + <!-- 定时任务--> + <dependency> + <groupId>com.ruoyi</groupId> + <artifactId>ruoyi-quartz</artifactId> + </dependency> + + <!-- 代码生成--> + <dependency> + <groupId>com.ruoyi</groupId> + <artifactId>ruoyi-generator</artifactId> +<!-- <exclusions>--> +<!-- <exclusion>--> +<!-- <groupId>com.github.pagehelper</groupId>--> +<!-- <artifactId>pagehelper-spring-boot-starter</artifactId>--> +<!-- </exclusion>--> +<!-- </exclusions>--> + </dependency> + + <!-- zxing生成二维码 --> + <dependency> + <groupId>com.google.zxing</groupId> + <artifactId>core</artifactId> + <version>3.3.3</version> + </dependency> + + <dependency> + <groupId>com.google.zxing</groupId> + <artifactId>javase</artifactId> + <version>3.3.3</version> + </dependency> + + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-test</artifactId> + <version>5.1.3.RELEASE</version> + </dependency> + + <dependency> + <groupId>com.squareup.okhttp3</groupId> + <artifactId>okhttp</artifactId> + <version>4.9.3</version> + </dependency> + + <dependency> + <groupId>com.alibaba</groupId> + <artifactId>fastjson</artifactId> + <version>1.2.78</version> + </dependency> + + <!-- easypoi --> + <dependency> + <groupId>cn.afterturn</groupId> + <artifactId>easypoi-base</artifactId> + <version>3.0.3</version> + </dependency> + <dependency> + <groupId>cn.afterturn</groupId> + <artifactId>easypoi-web</artifactId> + <version>3.0.3</version> + </dependency> + <dependency> + <groupId>cn.afterturn</groupId> + <artifactId>easypoi-annotation</artifactId> + <version>3.0.3</version> + </dependency> + + <!-- 阿里云短信 --> + <dependency> + <groupId>com.aliyun</groupId> + <artifactId>dysmsapi20170525</artifactId> + <version>2.0.10</version> + </dependency> + + + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + <version>2.5.15</version> + <configuration> + <fork>true</fork> <!-- 如果没有该配置,devtools不会生效 --> + </configuration> + <executions> + <execution> + <goals> + <goal>repackage</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-war-plugin</artifactId> + <version>3.1.0</version> + <configuration> + <failOnMissingWebXml>false</failOnMissingWebXml> + <warName>${project.artifactId}</warName> + </configuration> + </plugin> + </plugins> + <finalName>${project.artifactId}</finalName> + </build> + +</project> \ No newline at end of file diff --git a/ruoyi-applet/src/main/java/com/ruoyi/RuoYiAppletApplication.java b/ruoyi-applet/src/main/java/com/ruoyi/RuoYiAppletApplication.java new file mode 100644 index 0000000..d7127b6 --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/RuoYiAppletApplication.java @@ -0,0 +1,65 @@ +package com.ruoyi; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.core.env.Environment; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.web.client.RestTemplate; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * 启动程序 + * + * @author ruoyi + */ +@Slf4j +@EnableScheduling//开启定时任务 +@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }) +public class RuoYiAppletApplication +{ + public static void main(String[] args) { + + + ConfigurableApplicationContext application = SpringApplication.run(RuoYiAppletApplication.class, args); + try { + + Environment env = application.getEnvironment(); + log.info("\n----------------------------------------------------------\n\t" + + "应用 '{}' 运行成功! 访问连接:\n\t" + + "Swagger文档: \t\thttp://{}:{}/doc.html\n" + + "----------------------------------------------------------", + env.getProperty("spring.application.name", "后台"), + InetAddress.getLocalHost().getHostAddress(), + env.getProperty("server.port", "8081")); + }catch (Exception e){ + e.printStackTrace(); + } + } + + /** + * 当不存在此 wxRestTemplate 使用此方法的bean注入 + * + * @return + */ + @Bean + @ConditionalOnMissingBean(name = "restTemplate") + public RestTemplate wxRestTemplate() { + //复杂构造函数的使用 + SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); + // 设置超时 + requestFactory.setConnectTimeout(6000); + requestFactory.setReadTimeout(6000); + RestTemplate restTemplate = new RestTemplate(); + restTemplate.setRequestFactory(requestFactory); + return restTemplate; + } + +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/RuoYiServletInitializer.java b/ruoyi-applet/src/main/java/com/ruoyi/RuoYiServletInitializer.java new file mode 100644 index 0000000..7c8401b --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/RuoYiServletInitializer.java @@ -0,0 +1,18 @@ +package com.ruoyi; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +/** + * web容器中进行部署 + * + * @author ruoyi + */ +public class RuoYiServletInitializer extends SpringBootServletInitializer +{ + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) + { + return application.sources(RuoYiAppletApplication.class); + } +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/aspect/StateAspect.java b/ruoyi-applet/src/main/java/com/ruoyi/web/aspect/StateAspect.java new file mode 100644 index 0000000..45281ba --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/aspect/StateAspect.java @@ -0,0 +1,23 @@ +package com.ruoyi.web.aspect; + +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.system.service.ISysRoleService; +import com.ruoyi.system.service.ISysUserService; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + + +@Aspect +@Component + +public class StateAspect { + + + + } diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/WxLoginController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/WxLoginController.java new file mode 100644 index 0000000..7080b2e --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/WxLoginController.java @@ -0,0 +1,148 @@ +package com.ruoyi.web.controller.api; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginBody; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.redis.service.RedisService; +import com.ruoyi.common.utils.NumberUtil; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.service.SysLoginService; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.system.model.TbUser; +import com.ruoyi.system.service.TbUserService; +import com.ruoyi.system.utils.wx.body.resp.Code2SessionRespBody; +import com.ruoyi.system.utils.wx.body.resq.Code2SessionResqBody; +import com.ruoyi.system.utils.wx.model.WeixinProperties; +import com.ruoyi.system.utils.wx.pojo.AppletUserEncrypteData; +import com.ruoyi.system.utils.wx.tools.WxAppletTools; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.CollectionUtils; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * <p> + * 微信小程序登录 前端控制器 + * </p> + * + * @author xiaochen + * @since 2024-08-06 + */ +@Slf4j +@RestController +@RequestMapping("/wxLogin") +@Api(tags = "登录模块") +public class WxLoginController { + + @Autowired + private WeixinProperties wxConfig; + @Autowired + private RestTemplate wxRestTemplate; + @Resource + private RedisService redisService; + + @Resource + private TokenService tokenService; + + + @Autowired + private TbUserService tbUserService; + + + + /** + * 账号密码登录 + * + * @param loginBody 登录信息 + * @return 结果 + */ + @ApiOperation(value = "手机验证码登录",notes = "手机验证码登录") + @PostMapping("/login") + public AjaxResult login(@Valid @RequestBody LoginBody loginBody) + { + AjaxResult ajax = AjaxResult.success(); + // 生成令牌 + Object cacheObject = redisService.getCacheObject("login_" + loginBody.getPhone()); + if(cacheObject==null || !cacheObject.toString().equals(loginBody.getCode())){ + return AjaxResult.error("验证码错误"); + } + TbUser user = tbUserService.getOne(new LambdaQueryWrapper<TbUser>().eq(TbUser::getPhone, loginBody.getPhone()).ne(TbUser::getStatus,3).eq(TbUser::getIsDelete, 0)); + if(user!=null && user.getStatus()==2){ + return AjaxResult.error("登录失败,当前账号已被冻结"); + } + if(user==null){ + TbUser tbUser = new TbUser(); + tbUser.setPhone(loginBody.getPhone()); + tbUser.setStatus(1); + tbUser.setUserName(loginBody.getPhone()); + tbUser.setInviteId(loginBody.getInviteId()); + tbUserService.save(tbUser); + user = tbUser; + } + LoginUser loginUser = new LoginUser(); + loginUser.setUserId(Long.valueOf(user.getId())); + loginUser.setUser(new SysUser()); + ajax.put(Constants.TOKEN, tokenService.createToken(loginUser)); + ajax.put("username",user.getUserName()); + ajax.put("avatar",user.getAvatar()); + return ajax; + } + + @ApiOperation(value = "通过code获得openid,获取用户信息",tags = {"微信小程序登录"}) + @PostMapping("/openIdByJsCode") + public R<Map<String, Object>> openIdByJsCode(@RequestBody AppletUserEncrypteData data) { + log.info("<<<<<<<<换取openid开始<<<<<<<<:{}", data.getCode()); + WxAppletTools appletTools = new WxAppletTools(wxRestTemplate, wxConfig, redisService); + Code2SessionRespBody body = appletTools.getOpenIdByJscode2session(new Code2SessionResqBody().build(data.getCode())); + String openid = body.getOpenid(); + String sessionKey = body.getSessionKey(); + return R.ok(); + } + + + + @ApiOperation(value = "发送验证码",tags = {"发送验证码"}) + @PostMapping("/sendCode") + public R<?> sendCode(String phone) { + if (StringUtils.isBlank(phone)) { + return R.fail("手机号不能为空"); + } + String code = NumberUtil.getRandomInteger(6); + redisService.setCacheObject("login_"+phone, code, 5L, TimeUnit.MINUTES); + // 发送验证码 + + + return R.ok(); + } + + + @ApiOperation(value = "获取协议",tags = {"获取协议 1用户 2隐私"}) + @PostMapping("/getAgreement") + public R<?> getAgreement(Integer type) { + + return R.ok(); + } + + + + + +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java new file mode 100644 index 0000000..d2d6e8c --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java @@ -0,0 +1,94 @@ +package com.ruoyi.web.controller.common; + +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import javax.annotation.Resource; +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.FastByteArrayOutputStream; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import com.google.code.kaptcha.Producer; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.sign.Base64; +import com.ruoyi.common.utils.uuid.IdUtils; +import com.ruoyi.system.service.ISysConfigService; + +/** + * 验证码操作处理 + * + * @author ruoyi + */ +@RestController +public class CaptchaController +{ + @Resource(name = "captchaProducer") + private Producer captchaProducer; + + @Resource(name = "captchaProducerMath") + private Producer captchaProducerMath; + + @Autowired + private RedisCache redisCache; + + @Autowired + private ISysConfigService configService; + /** + * 生成验证码 + */ + @GetMapping("/captchaImage") + public AjaxResult getCode(HttpServletResponse response) throws IOException + { + AjaxResult ajax = AjaxResult.success(); + boolean captchaEnabled = configService.selectCaptchaEnabled(); + ajax.put("captchaEnabled", captchaEnabled); + if (!captchaEnabled) + { + return ajax; + } + + // 保存验证码信息 + String uuid = IdUtils.simpleUUID(); + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid; + + String capStr = null, code = null; + BufferedImage image = null; + + // 生成验证码 + String captchaType = RuoYiConfig.getCaptchaType(); + if ("math".equals(captchaType)) + { + String capText = captchaProducerMath.createText(); + capStr = capText.substring(0, capText.lastIndexOf("@")); + code = capText.substring(capText.lastIndexOf("@") + 1); + image = captchaProducerMath.createImage(capStr); + } + else if ("char".equals(captchaType)) + { + capStr = code = captchaProducer.createText(); + image = captchaProducer.createImage(capStr); + } + + redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES); + // 转换流信息写出 + FastByteArrayOutputStream os = new FastByteArrayOutputStream(); + try + { + ImageIO.write(image, "jpg", os); + } + catch (IOException e) + { + return AjaxResult.error(e.getMessage()); + } + + ajax.put("uuid", uuid); + ajax.put("img", Base64.encode(os.toByteArray())); + return ajax; + } +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/common/CommonController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/common/CommonController.java new file mode 100644 index 0000000..cec5006 --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/common/CommonController.java @@ -0,0 +1,163 @@ +package com.ruoyi.web.controller.common; + +import java.util.ArrayList; +import java.util.List; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.file.FileUploadUtils; +import com.ruoyi.common.utils.file.FileUtils; +import com.ruoyi.framework.config.ServerConfig; + +/** + * 通用请求处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/common") +public class CommonController +{ + private static final Logger log = LoggerFactory.getLogger(CommonController.class); + + @Autowired + private ServerConfig serverConfig; + + private static final String FILE_DELIMETER = ","; + + /** + * 通用下载请求 + * + * @param fileName 文件名称 + * @param delete 是否删除 + */ + @GetMapping("/download") + public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request) + { + try + { + if (!FileUtils.checkAllowDownload(fileName)) + { + throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName)); + } + String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1); + String filePath = RuoYiConfig.getDownloadPath() + fileName; + + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + FileUtils.setAttachmentResponseHeader(response, realFileName); + FileUtils.writeBytes(filePath, response.getOutputStream()); + if (delete) + { + FileUtils.deleteFile(filePath); + } + } + catch (Exception e) + { + log.error("下载文件失败", e); + } + } + + /** + * 通用上传请求(单个) + */ + @PostMapping("/upload") + public AjaxResult uploadFile(MultipartFile file) throws Exception + { + try + { + // 上传文件路径 + String filePath = RuoYiConfig.getUploadPath(); + // 上传并返回新文件名称 + String fileName = FileUploadUtils.upload(filePath, file); + String url = serverConfig.getUrl() + fileName; + AjaxResult ajax = AjaxResult.success(); + ajax.put("url", url); + ajax.put("fileName", fileName); + ajax.put("newFileName", FileUtils.getName(fileName)); + ajax.put("originalFilename", file.getOriginalFilename()); + return ajax; + } + catch (Exception e) + { + return AjaxResult.error(e.getMessage()); + } + } + + /** + * 通用上传请求(多个) + */ + @PostMapping("/uploads") + public AjaxResult uploadFiles(List<MultipartFile> files) throws Exception + { + try + { + // 上传文件路径 + String filePath = RuoYiConfig.getUploadPath(); + List<String> urls = new ArrayList<String>(); + List<String> fileNames = new ArrayList<String>(); + List<String> newFileNames = new ArrayList<String>(); + List<String> originalFilenames = new ArrayList<String>(); + for (MultipartFile file : files) + { + // 上传并返回新文件名称 + String fileName = FileUploadUtils.upload(filePath, file); + String url = serverConfig.getUrl() + fileName; + urls.add(url); + fileNames.add(fileName); + newFileNames.add(FileUtils.getName(fileName)); + originalFilenames.add(file.getOriginalFilename()); + } + AjaxResult ajax = AjaxResult.success(); + ajax.put("urls", StringUtils.join(urls, FILE_DELIMETER)); + ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMETER)); + ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMETER)); + ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMETER)); + return ajax; + } + catch (Exception e) + { + return AjaxResult.error(e.getMessage()); + } + } + + /** + * 本地资源通用下载 + */ + @GetMapping("/download/resource") + public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response) + throws Exception + { + try + { + if (!FileUtils.checkAllowDownload(resource)) + { + throw new Exception(StringUtils.format("资源文件({})非法,不允许下载。 ", resource)); + } + // 本地资源路径 + String localPath = RuoYiConfig.getProfile(); + // 数据库资源地址 + String downloadPath = localPath + StringUtils.substringAfter(resource, Constants.RESOURCE_PREFIX); + // 下载名称 + String downloadName = StringUtils.substringAfterLast(downloadPath, "/"); + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + FileUtils.setAttachmentResponseHeader(response, downloadName); + FileUtils.writeBytes(downloadPath, response.getOutputStream()); + } + catch (Exception e) + { + log.error("下载文件失败", e); + } + } +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/interceptor/MybatisConfiguration.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/interceptor/MybatisConfiguration.java new file mode 100644 index 0000000..5390dc1 --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/interceptor/MybatisConfiguration.java @@ -0,0 +1,17 @@ +package com.ruoyi.web.controller.interceptor; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MybatisConfiguration { + + /** + * 注册拦截器 + */ + @Bean + public MybatisInterceptor getMybatisInterceptor() { + return new MybatisInterceptor(); + } + +} \ No newline at end of file diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/interceptor/MybatisInterceptor.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/interceptor/MybatisInterceptor.java new file mode 100644 index 0000000..57a4ff0 --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/interceptor/MybatisInterceptor.java @@ -0,0 +1,121 @@ +package com.ruoyi.web.controller.interceptor; + +import com.ruoyi.framework.web.service.TokenService; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.SqlCommandType; +import org.apache.ibatis.plugin.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Field; +import java.util.*; + +@Slf4j +@Component +@Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) }) +public class MybatisInterceptor implements Interceptor { + + @Autowired + private TokenService tokenService; + + @Override + public Object intercept(Invocation invocation) throws Throwable { + MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; + log.debug("{}:"+mappedStatement); + log.debug("------sqlId------" + mappedStatement.getId()); + if("com.ruoyi.system.mapper.SysLogininforMapper.insertLogininfor".equals(mappedStatement.getId())){ + return invocation.proceed(); + } + // sql类型:insert、update、select、delete + SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType(); + Object parameter = invocation.getArgs()[1]; + log.debug("------sqlCommandType------" + sqlCommandType); + + if (parameter == null) { + return invocation.proceed(); + } + + // 当sql为新增或更新类型时,自动填充操作人相关信息 + if (SqlCommandType.INSERT == sqlCommandType) { + + Field[] fields = getAllFields(parameter); + for (Field field : fields) { + try { + // 注入创建人 + if ("createBy".equals(field.getName())) { + // 获取当前登录用户信息 + if(Objects.nonNull(tokenService.getLoginUser())){ + String userName = tokenService.getLoginUser().getUser().getUserName(); + field.setAccessible(true); + field.set(parameter, userName); + field.setAccessible(false); + } + } + //注入创建时间 + if ("createTime".equals(field.getName())) { + field.setAccessible(true); +// field.set(parameter, new Date()); + field.setAccessible(false); + } + } catch (Exception e) { + log.error("failed to insert data, exception = ", e); + } + } + } + if (SqlCommandType.UPDATE == sqlCommandType) { + Field[] fields = getAllFields(parameter); + for (Field field : fields) { + try { + if ("updateBy".equals(field.getName())) { + // 获取当前登录用户信息 + if(Objects.nonNull(tokenService.getLoginUser())){ + String userName = tokenService.getLoginUser().getUser().getUserName(); + field.setAccessible(true); + field.set(parameter, userName); + field.setAccessible(false); + } + } + if ("updateTime".equals(field.getName())) { + field.setAccessible(true); +// field.set(parameter, new Date()); + field.setAccessible(false); + } + } catch (Exception e) { + log.error("failed to update data, exception = ", e); + } + } + } + return invocation.proceed(); + } + + @Override + public Object plugin(Object target) { + return Plugin.wrap(target, this); + } + + @Override + public void setProperties(Properties properties) { + // TODO Auto-generated method stub + } + + /** + * 获取类的所有属性,包括父类 + * + * @param object + * @return + */ + private Field[] getAllFields(Object object) { + Class<?> clazz = object.getClass(); + List<Field> fieldList = new ArrayList<>(); + while (clazz != null) { + fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields()))); + clazz = clazz.getSuperclass(); + } + Field[] fields = new Field[fieldList.size()]; + fieldList.toArray(fields); + return fields; + } + +} \ No newline at end of file diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java new file mode 100644 index 0000000..ef8c5dd --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java @@ -0,0 +1,113 @@ +package com.ruoyi.web.controller.monitor; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisCallback; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.SysCache; + +/** + * 缓存监控 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/cache") +public class CacheController +{ + @Autowired + private RedisTemplate<String, String> redisTemplate; + + private final static List<SysCache> caches = new ArrayList<SysCache>(); + { + caches.add(new SysCache(CacheConstants.LOGIN_TOKEN_KEY, "用户信息")); + caches.add(new SysCache(CacheConstants.SYS_CONFIG_KEY, "配置信息")); + caches.add(new SysCache(CacheConstants.SYS_DICT_KEY, "数据字典")); + caches.add(new SysCache(CacheConstants.CAPTCHA_CODE_KEY, "验证码")); + caches.add(new SysCache(CacheConstants.REPEAT_SUBMIT_KEY, "防重提交")); + caches.add(new SysCache(CacheConstants.RATE_LIMIT_KEY, "限流处理")); + caches.add(new SysCache(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数")); + } + + @GetMapping() + public AjaxResult getInfo() throws Exception + { + Properties info = (Properties) redisTemplate.execute((RedisCallback<Object>) connection -> connection.info()); + Properties commandStats = (Properties) redisTemplate.execute((RedisCallback<Object>) connection -> connection.info("commandstats")); + Object dbSize = redisTemplate.execute((RedisCallback<Object>) connection -> connection.dbSize()); + + Map<String, Object> result = new HashMap<>(3); + result.put("info", info); + result.put("dbSize", dbSize); + + List<Map<String, String>> pieList = new ArrayList<>(); + commandStats.stringPropertyNames().forEach(key -> { + Map<String, String> data = new HashMap<>(2); + String property = commandStats.getProperty(key); + data.put("name", StringUtils.removeStart(key, "cmdstat_")); + data.put("value", StringUtils.substringBetween(property, "calls=", ",usec")); + pieList.add(data); + }); + result.put("commandStats", pieList); + return AjaxResult.success(result); + } + + @GetMapping("/getNames") + public AjaxResult cache() + { + return AjaxResult.success(caches); + } + + @GetMapping("/getKeys/{cacheName}") + public AjaxResult getCacheKeys(@PathVariable String cacheName) + { + Set<String> cacheKeys = redisTemplate.keys(cacheName + "*"); + return AjaxResult.success(cacheKeys); + } + + @GetMapping("/getValue/{cacheName}/{cacheKey}") + public AjaxResult getCacheValue(@PathVariable String cacheName, @PathVariable String cacheKey) + { + String cacheValue = redisTemplate.opsForValue().get(cacheKey); + SysCache sysCache = new SysCache(cacheName, cacheKey, cacheValue); + return AjaxResult.success(sysCache); + } + + @DeleteMapping("/clearCacheName/{cacheName}") + public AjaxResult clearCacheName(@PathVariable String cacheName) + { + Collection<String> cacheKeys = redisTemplate.keys(cacheName + "*"); + redisTemplate.delete(cacheKeys); + return AjaxResult.success(); + } + + @DeleteMapping("/clearCacheKey/{cacheKey}") + public AjaxResult clearCacheKey(@PathVariable String cacheKey) + { + redisTemplate.delete(cacheKey); + return AjaxResult.success(); + } + + @DeleteMapping("/clearCacheAll") + public AjaxResult clearCacheAll() + { + Collection<String> cacheKeys = redisTemplate.keys("*"); + redisTemplate.delete(cacheKeys); + return AjaxResult.success(); + } +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java new file mode 100644 index 0000000..60f09bb --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java @@ -0,0 +1,26 @@ +package com.ruoyi.web.controller.monitor; + +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.framework.web.domain.Server; + +/** + * 服务器监控 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/server") +public class ServerController +{ + @GetMapping() + public AjaxResult getInfo() throws Exception + { + Server server = new Server(); + server.copyTo(); + return AjaxResult.success(server); + } +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java new file mode 100644 index 0000000..eb748ba --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java @@ -0,0 +1,77 @@ +package com.ruoyi.web.controller.monitor; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +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.utils.poi.ExcelUtil; +import com.ruoyi.framework.web.service.SysPasswordService; +import com.ruoyi.system.domain.SysLogininfor; +import com.ruoyi.system.service.ISysLogininforService; + +/** + * 系统访问记录 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/logininfor") +public class SysLogininforController extends BaseController +{ + @Autowired + private ISysLogininforService logininforService; + + @Autowired + private SysPasswordService passwordService; + + @GetMapping("/list") + public TableDataInfo list(SysLogininfor logininfor) + { +// startPage(); + List<SysLogininfor> list = logininforService.selectLogininforList(logininfor); + return getDataTable(list); + } + +// @Log(title = "登录日志", businessType = BusinessType.EXPORT) +// @PostMapping("/export") +// public void export(HttpServletResponse response, SysLogininfor logininfor) +// { +// List<SysLogininfor> list = logininforService.selectLogininforList(logininfor); +// ExcelUtil<SysLogininfor> util = new ExcelUtil<SysLogininfor>(SysLogininfor.class); +// util.exportExcel(response, list, "登录日志"); +// } + + @Log(title = "登录日志", businessType = BusinessType.DELETE) + @DeleteMapping("/{infoIds}") + public AjaxResult remove(@PathVariable Long[] infoIds) + { + return toAjax(logininforService.deleteLogininforByIds(infoIds)); + } + + @Log(title = "登录日志", businessType = BusinessType.CLEAN) + @DeleteMapping("/clean") + public AjaxResult clean() + { + logininforService.cleanLogininfor(); + return success(); + } + + @Log(title = "账户解锁", businessType = BusinessType.OTHER) + @GetMapping("/unlock/{userName}") + public AjaxResult unlock(@PathVariable("userName") String userName) + { + passwordService.clearLoginRecordCache(userName); + return success(); + } +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java new file mode 100644 index 0000000..0129b80 --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java @@ -0,0 +1,52 @@ +package com.ruoyi.web.controller.monitor; + +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.system.service.ISysOperLogService; +import io.swagger.annotations.Api; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.ArrayList; +import java.util.List; + +/** + * 操作日志记录 + * + * @author ruoyi + */ +@Slf4j +@Api(tags = "操作日志记录") +@RestController +@RequestMapping("/monitor/operlog") +public class SysOperlogController extends BaseController +{ + @Autowired + private ISysOperLogService operLogService; + + @Log(title = "操作日志", businessType = BusinessType.DELETE) + @DeleteMapping("/deleteById/{operIds}") + public AjaxResult remove(@PathVariable String operIds) + { + String[] split = operIds.split(","); + List<Long> id = new ArrayList<>(); + for (String s : split) { + id.add(Long.valueOf(s)); + } + return AjaxResult.success(operLogService.deleteOperLogByIds(id)); + } + + @Log(title = "操作日志", businessType = BusinessType.CLEAN) + @DeleteMapping("/clean") + public AjaxResult clean() + { + operLogService.cleanOperLog(); + return success(); + } +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java new file mode 100644 index 0000000..c13bcfc --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java @@ -0,0 +1,81 @@ +package com.ruoyi.web.controller.monitor; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.SysUserOnline; +import com.ruoyi.system.service.ISysUserOnlineService; + +/** + * 在线用户监控 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/online") +public class SysUserOnlineController extends BaseController +{ + @Autowired + private ISysUserOnlineService userOnlineService; + + @Autowired + private RedisCache redisCache; + + @GetMapping("/list") + public TableDataInfo list(String ipaddr, String userName) + { + Collection<String> keys = redisCache.keys(CacheConstants.LOGIN_TOKEN_KEY + "*"); + List<SysUserOnline> userOnlineList = new ArrayList<SysUserOnline>(); + for (String key : keys) + { + LoginUser user = redisCache.getCacheObject(key); + if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) + { + userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user)); + } + else if (StringUtils.isNotEmpty(ipaddr)) + { + userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user)); + } + else if (StringUtils.isNotEmpty(userName) && StringUtils.isNotNull(user.getUser())) + { + userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user)); + } + else + { + userOnlineList.add(userOnlineService.loginUserToUserOnline(user)); + } + } + Collections.reverse(userOnlineList); + userOnlineList.removeAll(Collections.singleton(null)); + return getDataTable(userOnlineList); + } + + /** + * 强退用户 + */ + @Log(title = "在线用户", businessType = BusinessType.FORCE) + @DeleteMapping("/{tokenId}") + public AjaxResult forceLogout(@PathVariable String tokenId) + { + redisCache.deleteObject(CacheConstants.LOGIN_TOKEN_KEY + tokenId); + return success(); + } +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/CompanyController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/CompanyController.java new file mode 100644 index 0000000..8553a75 --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/CompanyController.java @@ -0,0 +1,41 @@ +package com.ruoyi.web.controller.system; + +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.system.query.CompanyListQuery; +import com.ruoyi.system.service.TbCompanyService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; + +@Slf4j +@RestController +@RequestMapping("/user") +@Api(tags = "商城模块") +public class CompanyController { + + @Autowired + private TbCompanyService tbCompanyService; + + @Autowired + private TokenService tokenService; + + + @ApiOperation(value = "获取商城首页",tags = {"获取商城首页"}) + @GetMapping("/getCompanyList") + public R<?> getCompanyList(@Valid CompanyListQuery query) { + LoginUser loginUser = tokenService.getLoginUser(); + Long userId = loginUser.getUserId(); + return R.ok(); + } + + + +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java new file mode 100644 index 0000000..706ccf2 --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java @@ -0,0 +1,115 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +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.utils.poi.ExcelUtil; +import com.ruoyi.system.domain.SysConfig; +import com.ruoyi.system.service.ISysConfigService; + +/** + * 参数配置 信息操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/config") +public class SysConfigController extends BaseController +{ + @Autowired + private ISysConfigService configService; + +// @Log(title = "参数管理", businessType = BusinessType.EXPORT) +// @PostMapping("/export") +// public void export(HttpServletResponse response, SysConfig config) +// { +// List<SysConfig> list = configService.selectConfigList(config); +// ExcelUtil<SysConfig> util = new ExcelUtil<SysConfig>(SysConfig.class); +// util.exportExcel(response, list, "参数数据"); +// } + + /** + * 根据参数编号获取详细信息 + */ + @GetMapping(value = "/{configId}") + public AjaxResult getInfo(@PathVariable Long configId) + { + return success(configService.selectConfigById(configId)); + } + + /** + * 根据参数键名查询参数值 + */ + @GetMapping(value = "/configKey/{configKey}") + public AjaxResult getConfigKey(@PathVariable String configKey) + { + return success(configService.selectConfigByKey(configKey)); + } + + /** + * 新增参数配置 + */ + @Log(title = "参数管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysConfig config) + { + if (!configService.checkConfigKeyUnique(config)) + { + return error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + config.setCreateBy(getUsername()); + return toAjax(configService.insertConfig(config)); + } + + /** + * 修改参数配置 + */ + @Log(title = "参数管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysConfig config) + { + if (!configService.checkConfigKeyUnique(config)) + { + return error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + config.setUpdateBy(getUsername()); + return toAjax(configService.updateConfig(config)); + } + + /** + * 删除参数配置 + */ + @Log(title = "参数管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{configIds}") + public AjaxResult remove(@PathVariable Long[] configIds) + { + configService.deleteConfigByIds(configIds); + return success(); + } + + /** + * 刷新参数缓存 + */ + @Log(title = "参数管理", businessType = BusinessType.CLEAN) + @DeleteMapping("/refreshCache") + public AjaxResult refreshCache() + { + configService.resetConfigCache(); + return success(); + } +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java new file mode 100644 index 0000000..990d0f7 --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java @@ -0,0 +1,126 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.service.ISysDeptService; + +/** + * 部门信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/dept") +public class SysDeptController extends BaseController +{ + @Autowired + private ISysDeptService deptService; + + /** + * 获取部门列表 + */ + @GetMapping("/list") + public AjaxResult list(SysDept dept) + { + List<SysDept> depts = deptService.selectDeptList(dept); + return AjaxResult.success(depts); + } + + /** + * 查询部门列表(排除节点) + */ + @GetMapping("/list/exclude/{deptId}") + public AjaxResult excludeChild(@PathVariable(value = "deptId", required = false) Long deptId) + { + List<SysDept> depts = deptService.selectDeptList(new SysDept()); + depts.removeIf(d -> d.getDeptId().intValue() == deptId || ArrayUtils.contains(StringUtils.split(d.getAncestors(), ","), deptId + "")); + return AjaxResult.success(depts); + } + + /** + * 根据部门编号获取详细信息 + */ + @GetMapping(value = "/{deptId}") + public AjaxResult getInfo(@PathVariable Long deptId) + { + deptService.checkDeptDataScope(deptId); + return AjaxResult.success(deptService.selectDeptById(deptId)); + } + + /** + * 新增部门 + */ + @Log(title = "部门管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysDept dept) + { + if (!deptService.checkDeptNameUnique(dept)) + { + return error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } + dept.setCreateBy(getUsername()); + return AjaxResult.success(deptService.insertDept(dept)); + } + + /** + * 修改部门 + */ + @Log(title = "部门管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysDept dept) + { + Long deptId = dept.getDeptId(); + deptService.checkDeptDataScope(deptId); + if (!deptService.checkDeptNameUnique(dept)) + { + return error("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } + else if (dept.getParentId().equals(deptId)) + { + return error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己"); + } + else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus()) && deptService.selectNormalChildrenDeptById(deptId) > 0) + { + return error("该部门包含未停用的子部门!"); + } + dept.setUpdateBy(getUsername()); + return AjaxResult.success(deptService.updateDept(dept)); + } + + /** + * 删除部门 + */ + @Log(title = "部门管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{deptId}") + public AjaxResult remove(@PathVariable Long deptId) + { + if (deptService.hasChildByDeptId(deptId)) + { + return warn("存在下级部门,不允许删除"); + } + if (deptService.checkDeptExistUser(deptId)) + { + return warn("部门存在用户,不允许删除"); + } + deptService.checkDeptDataScope(deptId); + return AjaxResult.success(deptService.deleteDeptById(deptId)); + } +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java new file mode 100644 index 0000000..23b0c23 --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java @@ -0,0 +1,115 @@ +package com.ruoyi.web.controller.system; + +import java.util.ArrayList; +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +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.domain.entity.SysDictData; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.service.ISysDictDataService; +import com.ruoyi.system.service.ISysDictTypeService; + +/** + * 数据字典信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/dict/data") +public class SysDictDataController extends BaseController +{ + @Autowired + private ISysDictDataService dictDataService; + + @Autowired + private ISysDictTypeService dictTypeService; + + @GetMapping("/list") + public TableDataInfo list(SysDictData dictData) + { +// startPage(); + List<SysDictData> list = dictDataService.selectDictDataList(dictData); + return getDataTable(list); + } + +// @Log(title = "字典数据", businessType = BusinessType.EXPORT) +// @PostMapping("/export") +// public void export(HttpServletResponse response, SysDictData dictData) +// { +// List<SysDictData> list = dictDataService.selectDictDataList(dictData); +// ExcelUtil<SysDictData> util = new ExcelUtil<SysDictData>(SysDictData.class); +// util.exportExcel(response, list, "字典数据"); +// } + + /** + * 查询字典数据详细 + */ + @GetMapping(value = "/{dictCode}") + public AjaxResult getInfo(@PathVariable Long dictCode) + { + return success(dictDataService.selectDictDataById(dictCode)); + } + + /** + * 根据字典类型查询字典数据信息 + */ + @GetMapping(value = "/type/{dictType}") + public AjaxResult dictType(@PathVariable String dictType) + { + List<SysDictData> data = dictTypeService.selectDictDataByType(dictType); + if (StringUtils.isNull(data)) + { + data = new ArrayList<SysDictData>(); + } + return success(data); + } + + /** + * 新增字典类型 + */ + @Log(title = "字典数据", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysDictData dict) + { + dict.setCreateBy(getUsername()); + return toAjax(dictDataService.insertDictData(dict)); + } + + /** + * 修改保存字典类型 + */ + @Log(title = "字典数据", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysDictData dict) + { + dict.setUpdateBy(getUsername()); + return toAjax(dictDataService.updateDictData(dict)); + } + + /** + * 删除字典类型 + */ + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictCodes}") + public AjaxResult remove(@PathVariable Long[] dictCodes) + { + dictDataService.deleteDictDataByIds(dictCodes); + return success(); + } +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java new file mode 100644 index 0000000..1c5ab6b --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java @@ -0,0 +1,124 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +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.domain.entity.SysDictType; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.service.ISysDictTypeService; + +/** + * 数据字典信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/dict/type") +public class SysDictTypeController extends BaseController +{ + @Autowired + private ISysDictTypeService dictTypeService; + + @GetMapping("/list") + public TableDataInfo list(SysDictType dictType) + { +// startPage(); + List<SysDictType> list = dictTypeService.selectDictTypeList(dictType); + return getDataTable(list); + } + +// @Log(title = "字典类型", businessType = BusinessType.EXPORT) +// @PostMapping("/export") +// public void export(HttpServletResponse response, SysDictType dictType) +// { +// List<SysDictType> list = dictTypeService.selectDictTypeList(dictType); +// ExcelUtil<SysDictType> util = new ExcelUtil<SysDictType>(SysDictType.class); +// util.exportExcel(response, list, "字典类型"); +// } + + /** + * 查询字典类型详细 + */ + @GetMapping(value = "/{dictId}") + public AjaxResult getInfo(@PathVariable Long dictId) + { + return success(dictTypeService.selectDictTypeById(dictId)); + } + + /** + * 新增字典类型 + */ + @Log(title = "字典类型", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysDictType dict) + { + if (!dictTypeService.checkDictTypeUnique(dict)) + { + return error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dict.setCreateBy(getUsername()); + return toAjax(dictTypeService.insertDictType(dict)); + } + + /** + * 修改字典类型 + */ + @Log(title = "字典类型", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysDictType dict) + { + if (!dictTypeService.checkDictTypeUnique(dict)) + { + return error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dict.setUpdateBy(getUsername()); + return toAjax(dictTypeService.updateDictType(dict)); + } + + /** + * 删除字典类型 + */ + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictIds}") + public AjaxResult remove(@PathVariable Long[] dictIds) + { + dictTypeService.deleteDictTypeByIds(dictIds); + return success(); + } + + /** + * 刷新字典缓存 + */ + @Log(title = "字典类型", businessType = BusinessType.CLEAN) + @DeleteMapping("/refreshCache") + public AjaxResult refreshCache() + { + dictTypeService.resetDictCache(); + return success(); + } + + /** + * 获取字典选择框列表 + */ + @GetMapping("/optionselect") + public AjaxResult optionselect() + { + List<SysDictType> dictTypes = dictTypeService.selectDictTypeAll(); + return success(dictTypes); + } +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java new file mode 100644 index 0000000..13007eb --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java @@ -0,0 +1,29 @@ +package com.ruoyi.web.controller.system; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.utils.StringUtils; + +/** + * 首页 + * + * @author ruoyi + */ +@RestController +public class SysIndexController +{ + /** 系统基础配置 */ + @Autowired + private RuoYiConfig ruoyiConfig; + + /** + * 访问首页,提示语 + */ + @RequestMapping("/") + public String index() + { + return StringUtils.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion()); + } +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java new file mode 100644 index 0000000..2b5e89c --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java @@ -0,0 +1,163 @@ +package com.ruoyi.web.controller.system; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.service.ISysMenuService; + +/** + * 菜单信息 + * + * @author ruoyi + */ +@Api(tags = "菜单信息") +@RestController +@RequestMapping("/system/menu") +public class SysMenuController extends BaseController +{ + @Autowired + private ISysMenuService menuService; + + @ApiOperation("菜单权限(有层级)") + @GetMapping("/levelList") + public AjaxResult levelList() + { + // 获取当前角色的菜单列表 + List<SysMenu> menus = menuService.selectList(); + if(menus.size()==0){ + return AjaxResult.success(new ArrayList<>()); + } + // 第三级 + List<SysMenu> s3 = menus.stream().filter(e -> e.getMenuType().equals("F")).collect(Collectors.toList()); + // 第二级 + List<SysMenu> s2 = menus.stream().filter(e -> e.getMenuType().equals("C")).collect(Collectors.toList()); + // 第一级 + List<SysMenu> s1 = menus.stream().filter(e -> e.getMenuType().equals("M")).collect(Collectors.toList()); + + for (SysMenu menu : s2) { + List<SysMenu> collect = s3.stream().filter(e -> e.getParentId().equals(menu.getMenuId())).collect(Collectors.toList()); + menu.setChildren(collect); + } + for (SysMenu menu : s1) { + List<SysMenu> collect = s2.stream().filter(e -> e.getParentId().equals(menu.getMenuId())).collect(Collectors.toList()); + menu.setChildren(collect); + } + + return AjaxResult.success(s1); + } + + /** + * 获取菜单列表 + */ + @GetMapping("/list") + public AjaxResult list(SysMenu menu) + { + List<SysMenu> menus = menuService.selectMenuList(menu, getUserId()); + return AjaxResult.success(menus); + } + + /** + * 根据菜单编号获取详细信息 + */ + @GetMapping(value = "/{menuId}") + public AjaxResult getInfo(@PathVariable Long menuId) + { + return AjaxResult.success(menuService.selectMenuById(menuId)); + } + + /** + * 获取菜单下拉树列表 + */ + @GetMapping("/treeselect") + public AjaxResult treeselect(SysMenu menu) + { + List<SysMenu> menus = menuService.selectMenuList(menu, getUserId()); + return AjaxResult.success(menuService.buildMenuTreeSelect(menus)); + } + + /** + * 加载对应角色菜单列表树 + */ + @GetMapping(value = "/roleMenuTreeselect/{roleId}") + public AjaxResult roleMenuTreeselect(@PathVariable("roleId") Long roleId) + { + List<SysMenu> menus = menuService.selectMenuList(getUserId()); + AjaxResult ajax = AjaxResult.success(); + ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId)); + ajax.put("menus", menuService.buildMenuTreeSelect(menus)); + return ajax; + } + + /** + * 新增菜单 + */ + @Log(title = "菜单管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysMenu menu) + { + if (!menuService.checkMenuNameUnique(menu)) + { + return error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } + else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) + { + return error("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); + } + menu.setCreateBy(getUsername()); + return AjaxResult.success(menuService.insertMenu(menu)); + } + + /** + * 修改菜单 + */ + @Log(title = "菜单管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysMenu menu) + { + if (!menuService.checkMenuNameUnique(menu)) + { + return error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } + else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) + { + return error("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); + } + else if (menu.getMenuId().equals(menu.getParentId())) + { + return error("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己"); + } + menu.setUpdateBy(getUsername()); + return AjaxResult.success(menuService.updateMenu(menu)); + } + + /** + * 删除菜单 + */ + @Log(title = "菜单管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{menuId}") + public AjaxResult remove(@PathVariable("menuId") Long menuId) + { + if (menuService.hasChildByMenuId(menuId)) + { + return warn("存在子菜单,不允许删除"); + } + if (menuService.checkMenuExistRole(menuId)) + { + return warn("菜单已分配,不允许删除"); + } + return AjaxResult.success(menuService.deleteMenuById(menuId)); + } +} \ No newline at end of file diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java new file mode 100644 index 0000000..92a1d6b --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java @@ -0,0 +1,86 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +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.system.domain.SysNotice; +import com.ruoyi.system.service.ISysNoticeService; + +/** + * 公告 信息操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/notice") +public class SysNoticeController extends BaseController +{ + @Autowired + private ISysNoticeService noticeService; + + /** + * 获取通知公告列表 + */ + @GetMapping("/list") + public TableDataInfo list(SysNotice notice) + { +// startPage(); + List<SysNotice> list = noticeService.selectNoticeList(notice); + return getDataTable(list); + } + + /** + * 根据通知公告编号获取详细信息 + */ + @GetMapping(value = "/{noticeId}") + public AjaxResult getInfo(@PathVariable Long noticeId) + { + return success(noticeService.selectNoticeById(noticeId)); + } + + /** + * 新增通知公告 + */ + @Log(title = "通知公告", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysNotice notice) + { + notice.setCreateBy(getUsername()); + return toAjax(noticeService.insertNotice(notice)); + } + + /** + * 修改通知公告 + */ + @Log(title = "通知公告", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysNotice notice) + { + notice.setUpdateBy(getUsername()); + return toAjax(noticeService.updateNotice(notice)); + } + + /** + * 删除通知公告 + */ + @Log(title = "通知公告", businessType = BusinessType.DELETE) + @DeleteMapping("/{noticeIds}") + public AjaxResult remove(@PathVariable Long[] noticeIds) + { + return toAjax(noticeService.deleteNoticeByIds(noticeIds)); + } +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysPostController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysPostController.java new file mode 100644 index 0000000..a7a3e13 --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysPostController.java @@ -0,0 +1,123 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +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.utils.poi.ExcelUtil; +import com.ruoyi.system.domain.SysPost; +import com.ruoyi.system.service.ISysPostService; + +/** + * 岗位信息操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/post") +public class SysPostController extends BaseController +{ + @Autowired + private ISysPostService postService; + + /** + * 获取岗位列表 + */ + @GetMapping("/list") + public TableDataInfo list(SysPost post) + { +// startPage(); + List<SysPost> list = postService.selectPostList(post); + return getDataTable(list); + } + +// @Log(title = "岗位管理", businessType = BusinessType.EXPORT) +// @PostMapping("/export") +// public void export(HttpServletResponse response, SysPost post) +// { +// List<SysPost> list = postService.selectPostList(post); +// ExcelUtil<SysPost> util = new ExcelUtil<SysPost>(SysPost.class); +// util.exportExcel(response, list, "岗位数据"); +// } + + /** + * 根据岗位编号获取详细信息 + */ + @GetMapping(value = "/{postId}") + public AjaxResult getInfo(@PathVariable Long postId) + { + return success(postService.selectPostById(postId)); + } + + /** + * 新增岗位 + */ + @Log(title = "岗位管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysPost post) + { + if (!postService.checkPostNameUnique(post)) + { + return error("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } + else if (!postService.checkPostCodeUnique(post)) + { + return error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + post.setCreateBy(getUsername()); + return toAjax(postService.insertPost(post)); + } + + /** + * 修改岗位 + */ + @Log(title = "岗位管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysPost post) + { + if (!postService.checkPostNameUnique(post)) + { + return error("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } + else if (!postService.checkPostCodeUnique(post)) + { + return error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + post.setUpdateBy(getUsername()); + return toAjax(postService.updatePost(post)); + } + + /** + * 删除岗位 + */ + @Log(title = "岗位管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{postIds}") + public AjaxResult remove(@PathVariable Long[] postIds) + { + return toAjax(postService.deletePostByIds(postIds)); + } + + /** + * 获取岗位选择框列表 + */ + @GetMapping("/optionselect") + public AjaxResult optionselect() + { + List<SysPost> posts = postService.selectPostAll(); + return success(posts); + } +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java new file mode 100644 index 0000000..be5af6a --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java @@ -0,0 +1,136 @@ +package com.ruoyi.web.controller.system; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.file.FileUploadUtils; +import com.ruoyi.common.utils.file.MimeTypeUtils; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.system.service.ISysUserService; + +/** + * 个人信息 业务处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/user/profile") +public class SysProfileController extends BaseController +{ + @Autowired + private ISysUserService userService; + + @Autowired + private TokenService tokenService; + + /** + * 个人信息 + */ + @GetMapping + public AjaxResult profile() + { + LoginUser loginUser = getLoginUser(); + SysUser user = loginUser.getUser(); + AjaxResult ajax = AjaxResult.success(user); + ajax.put("roleGroup", userService.selectUserRoleGroup(loginUser.getUsername())); + ajax.put("postGroup", userService.selectUserPostGroup(loginUser.getUsername())); + return ajax; + } + + /** + * 修改用户 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult updateProfile(@RequestBody SysUser user) + { + LoginUser loginUser = getLoginUser(); + SysUser currentUser = loginUser.getUser(); + currentUser.setNickName(user.getNickName()); + currentUser.setEmail(user.getEmail()); + currentUser.setPhonenumber(user.getPhonenumber()); + currentUser.setSex(user.getSex()); + if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(currentUser)) + { + return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); + } + if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(currentUser)) + { + return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + if (userService.updateUserProfile(currentUser) > 0) + { + // 更新缓存用户信息 + tokenService.setLoginUser(loginUser); + return success(); + } + return error("修改个人信息异常,请联系管理员"); + } + + /** + * 重置密码 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping("/updatePwd") + public AjaxResult updatePwd(String oldPassword, String newPassword) + { + LoginUser loginUser = getLoginUser(); + String userName = loginUser.getUsername(); + String password = loginUser.getPassword(); + if (!SecurityUtils.matchesPassword(oldPassword, password)) + { + return error("修改密码失败,旧密码错误"); + } + if (SecurityUtils.matchesPassword(newPassword, password)) + { + return error("新密码不能与旧密码相同"); + } + if (userService.resetUserPwd(userName, SecurityUtils.encryptPassword(newPassword)) > 0) + { + // 更新缓存用户密码 + loginUser.getUser().setPassword(SecurityUtils.encryptPassword(newPassword)); + tokenService.setLoginUser(loginUser); + return success(); + } + return error("修改密码异常,请联系管理员"); + } + + /** + * 头像上传 + */ + @Log(title = "用户头像", businessType = BusinessType.UPDATE) + @PostMapping("/avatar") + public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws Exception + { + if (!file.isEmpty()) + { + LoginUser loginUser = getLoginUser(); + String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION); + if (userService.updateUserAvatar(loginUser.getUsername(), avatar)) + { + AjaxResult ajax = AjaxResult.success(); + ajax.put("imgUrl", avatar); + // 更新缓存用户头像 + loginUser.getUser().setAvatar(avatar); + tokenService.setLoginUser(loginUser); + return ajax; + } + } + return error("上传图片异常,请联系管理员"); + } +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java new file mode 100644 index 0000000..fe19249 --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java @@ -0,0 +1,38 @@ +package com.ruoyi.web.controller.system; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.model.RegisterBody; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.service.SysRegisterService; +import com.ruoyi.system.service.ISysConfigService; + +/** + * 注册验证 + * + * @author ruoyi + */ +@RestController +public class SysRegisterController extends BaseController +{ + @Autowired + private SysRegisterService registerService; + + @Autowired + private ISysConfigService configService; + + @PostMapping("/register") + public AjaxResult register(@RequestBody RegisterBody user) + { + if (!("true".equals(configService.selectConfigByKey("sys.account.registerUser")))) + { + return error("当前系统没有开启注册功能!"); + } + String msg = registerService.register(user); + return StringUtils.isEmpty(msg) ? success() : error(msg); + } +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java new file mode 100644 index 0000000..99f5bee --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java @@ -0,0 +1,235 @@ +package com.ruoyi.web.controller.system; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import com.ruoyi.common.basic.PageInfo; +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.system.query.SysRoleQuery; +import com.ruoyi.system.service.ISysMenuService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +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.domain.entity.SysDept; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.service.SysPermissionService; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.system.domain.SysUserRole; +import com.ruoyi.system.service.ISysDeptService; +import com.ruoyi.system.service.ISysRoleService; +import com.ruoyi.system.service.ISysUserService; + +/** + * 角色信息 + * + * @author ruoyi + */ +@Api(tags = "角色信息") +@RestController +@RequestMapping("/system/role") +public class SysRoleController extends BaseController +{ + @Autowired + private ISysRoleService roleService; + + @Autowired + private TokenService tokenService; + + @Autowired + private SysPermissionService permissionService; + + @Autowired + private ISysUserService userService; + + @Autowired + private ISysDeptService deptService; + @Autowired + private ISysMenuService menuService; + + @ApiOperation(value = "角色列表") + @PostMapping("/list") + public AjaxResult list(@RequestBody SysRoleQuery query) + { + PageInfo<SysRole> list = roleService.selectPageList(query); + return AjaxResult.success(list); + } + + @ApiOperation(value = "角色列表不分页") + @PostMapping("/listNotPage") + public AjaxResult list() + { + List<SysRole> list = roleService.selectRoleList(new SysRole()); + return AjaxResult.success(list); + } + + @ApiOperation(value = "角色数量统计") + @PostMapping("/roleCount") + public AjaxResult roleCount() + { + int all = roleService.selectCount(null); + int normal = roleService.selectCount(0); + int stop = roleService.selectCount(1); + + Map<String,Integer> map = new HashMap<>(); + map.put("all",all); + map.put("normal",normal); + map.put("stop",stop); + return AjaxResult.success(map); + } + +// @Log(title = "角色管理", businessType = BusinessType.EXPORT) +// @PostMapping("/export") +// public void export(HttpServletResponse response, SysRole role) +// { +// List<SysRole> list = roleService.selectRoleList(role); +// ExcelUtil<SysRole> util = new ExcelUtil<SysRole>(SysRole.class); +// util.exportExcel(response, list, "角色数据"); +// } + + /** + * 根据角色编号获取详细信息 + */ + @GetMapping(value = "/{roleId}") + public AjaxResult getInfo(@PathVariable Long roleId) + { + roleService.checkRoleDataScope(roleId); + return AjaxResult.success(roleService.selectRoleById(roleId)); + } + + + @ApiOperation("用户获取权限菜单") + @GetMapping("/roleInfoFromUserId") + public AjaxResult roleInfoFromUserId(@RequestParam Long userId) + { + return AjaxResult.success(roleService.roleInfoFromUserId(userId)); + } + + + /** + * 修改保存数据权限 + */ + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping("/dataScope") + public AjaxResult dataScope(@RequestBody SysRole role) + { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + return AjaxResult.success(roleService.authDataScope(role)); + } + + /** + * 状态修改 + */ + @ApiOperation(value = "状态修改") + @Log(title = "角色信息-角色状态修改", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public AjaxResult changeStatus(@RequestBody SysRole role) + { + role.setUpdateBy(getUsername()); + roleService.updateStatus(role); + return AjaxResult.success(); + } + + /** + * 删除角色 + */ + @ApiOperation(value = "删除角色") + @Log(title = "角色信息-角色删除角色", businessType = BusinessType.DELETE) + @DeleteMapping("/deleteById/{ids}") + public AjaxResult remove(@PathVariable String ids) + { + String[] split = ids.split(","); + List<Long> id = new ArrayList<>(); + for (String s : split) { + id.add(Long.valueOf(s)); + } + return AjaxResult.success(roleService.deleteRoleByIds(id)); + } + + /** + * 获取角色选择框列表 + */ + @GetMapping("/optionselect") + public AjaxResult optionselect() + { + return AjaxResult.success(roleService.selectRoleAll()); + } + + /** + * 查询已分配用户角色列表 + */ + @GetMapping("/authUser/allocatedList") + public TableDataInfo allocatedList(SysUser user) + { +// startPage(); + List<SysUser> list = userService.selectAllocatedList(user); + return getDataTable(list); + } + + /** + * 查询未分配用户角色列表 + */ + @GetMapping("/authUser/unallocatedList") + public TableDataInfo unallocatedList(SysUser user) + { +// startPage(); + List<SysUser> list = userService.selectUnallocatedList(user); + return getDataTable(list); + } + + /** + * 取消授权用户 + */ + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/cancel") + public AjaxResult cancelAuthUser(@RequestBody SysUserRole userRole) + { + return AjaxResult.success(roleService.deleteAuthUser(userRole)); + } + + /** + * 批量取消授权用户 + */ + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/cancelAll") + public AjaxResult cancelAuthUserAll(Long roleId, Long[] userIds) + { + return AjaxResult.success(roleService.deleteAuthUsers(roleId, userIds)); + } + + /** + * 批量选择用户授权 + */ + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/selectAll") + public AjaxResult selectAuthUserAll(Long roleId, Long[] userIds) + { + roleService.checkRoleDataScope(roleId); + return AjaxResult.success(roleService.insertAuthUsers(roleId, userIds)); + } + + /** + * 获取对应角色部门树列表 + */ + @GetMapping(value = "/deptTree/{roleId}") + public AjaxResult deptTree(@PathVariable("roleId") Long roleId) + { + AjaxResult ajax = AjaxResult.success(); + ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId)); + ajax.put("depts", deptService.selectDeptTreeList(new SysDept())); + return ajax; + } +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysUserController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysUserController.java new file mode 100644 index 0000000..e323bfe --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysUserController.java @@ -0,0 +1,356 @@ +package com.ruoyi.web.controller.system; + +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.basic.PageInfo; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +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.enums.BusinessType; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.system.query.SysUserQuery; +import com.ruoyi.system.service.ISysDeptService; +import com.ruoyi.system.service.ISysRoleService; +import com.ruoyi.system.service.ISysUserService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 用户信息 + * + * @author ruoyi + */ +@Api(tags = "用户信息") +@RestController +@RequestMapping("/system/user") +public class SysUserController extends BaseController +{ + @Autowired + private ISysUserService userService; + + @Autowired + private ISysRoleService roleService; + + @Autowired + private ISysDeptService deptService; + @Autowired + private TokenService tokenService; + + + @ApiOperation(value = "获取用户列表-不分页") + @PostMapping("/listNotPage") + public AjaxResult listNotPage() + { + List<SysUser> list = userService.selectList(); + return AjaxResult.success(list); + } + + /** + * 获取用户黑名单列表 + */ +// @ApiOperation(value = "获取用户黑名单列表") +// @PostMapping("/blacklist") +// public AjaxResult blacklist(@RequestBody SysUserQuery query) +// { +// startPage(query.getPageNum(), query.getPageSize()); +// List<SysUserVO> list = userService.selectBlackPageList(query); +// return AjaxResult.success(getDataTable(list)); +// } + + /** + * 人员借用列表 + */ +// @ApiOperation(value = "人员借用列表") +// @GetMapping("/userBorrowList") +// public AjaxResult userBorrowList(@RequestParam(required = false) String name, +// @RequestParam(required = false) Integer type) +// { +// +// UserAddListVO userAddListVO = new UserAddListVO(); +// +// Long companyId = tokenService.getLoginUser().getUser().getCompanyId(); +// +// List<TCompany> companyList = new ArrayList<>(); +// List<TDept> deptList = new ArrayList<>(); +// List<SysUser> userList = new ArrayList<>(); +// // 查询公司 +// if(Objects.nonNull(type) && type == 1){ +// companyList = companyService.userAddListByCompanyName(name); +// } +// // 查询部门 +// if(Objects.nonNull(type) && type == 2){ +// deptList = tDeptService.userAddListByDeptName(name); +// } +// // 查询用户 +// if(Objects.nonNull(type) && type == 3){ +// userList = userService.selectListByNamePhone(name); +// } +// +// if(Objects.isNull(type)){ +// companyList = companyService.userAddListByCompanyName(name); +// deptList = tDeptService.userAddListByDeptName(name); +// userList = userService.selectListByNamePhone(name); +// } +// +// List<Long> companyIds = companyList.stream().map(TCompany::getId).collect(Collectors.toList()); +// List<Long> deptCompanyIds = deptList.stream().map(TDept::getCompanyId).collect(Collectors.toList()); +// List<Long> userCompanyIds = userList.stream().map(SysUser::getCompanyId).collect(Collectors.toList()); +// companyIds.addAll(deptCompanyIds); +// companyIds.addAll(userCompanyIds); +// +// companyIds = companyIds.stream().distinct().collect(Collectors.toList()); +// +// if(CollectionUtils.isEmpty(companyIds)){ +// return AjaxResult.success(userAddListVO); +// } +// SysUser user1 = tokenService.getLoginUser().getUser(); +// if(!user1.isAdmin()){ +// companyIds = companyIds.stream().filter(e->!e.equals(companyId)).collect(Collectors.toList()); +// } +// +// // 查询符合要求的公司 +// List<UserLevelVO> parent = companyService.userAddListByCompanyIds(companyIds); +// +// List<TDept> depts = tDeptService.selectList(); +// +// List<SysUser> sysUsers = userService.selectList(); +// +// for (UserLevelVO userLevelVO : parent) { +// +// // 找到公司下的部门 +// List<TDept> tDepts = depts.stream().filter(e -> userLevelVO.getKey().equals(e.getCompanyId())).collect(Collectors.toList()); +// List<UserLevelVO> children = new ArrayList<>(); +// // 封装部门 +// for (TDept dept : tDepts) { +// userLevelVO.setChildren(children); +// UserLevelVO userLevelVO1 = new UserLevelVO(); +// userLevelVO1.setKey(dept.getId()); +// userLevelVO1.setTitle(dept.getDeptName()); +// // 找到部门下的人员 +// List<SysUser> users; +// if(StringUtils.isNotEmpty(name) && type == 3){ +// users = sysUsers.stream().filter(e -> userLevelVO1.getKey().equals(e.getDeptId()) +// && ((StringUtils.isNotEmpty(e.getNickName()) && e.getNickName().contains(name))) +// || (StringUtils.isNotEmpty(e.getPhonenumber()) && e.getPhonenumber().contains(name))).collect(Collectors.toList()); +// }else { +// users = sysUsers.stream().filter(e -> userLevelVO1.getKey().equals(e.getDeptId())).collect(Collectors.toList()); +// } +// List<UserLevelVO> children1 = new ArrayList<>(); +// // 封装人员 +// for (SysUser user : users) { +// UserLevelVO userLevelVO2 = new UserLevelVO(); +// userLevelVO2.setKey(user.getUserId()); +// userLevelVO2.setTitle(user.getNickName()); +// userLevelVO2.setAvatar(user.getAvatar()); +// userLevelVO2.setFlag(true); +// children1.add(userLevelVO2); +// } +// userLevelVO1.setChildren(children1); +// +// children.add(userLevelVO1); +// } +// userLevelVO.setChildren(children); +// } +// userAddListVO.setUserLevelVOS(parent); +// userAddListVO.setUserList(sysUsers); +// return AjaxResult.success(userAddListVO); +// } + + + + /** + * 获取用户数量统计 + */ + @ApiOperation(value = "获取用户数量统计") + @PostMapping("/getUserCount") + public AjaxResult getUserCount() + { + Map<String,Integer> map = new HashMap<>(); + + Integer userCountSum = userService.selectCount(null); + Integer normalCount = userService.selectCount(0);// 正常 + Integer stopCount = userService.selectCount(1);// 停用 + + map.put("all",userCountSum); + map.put("normal",normalCount); + map.put("stop",stopCount); + + return AjaxResult.success(map); + } + + /** + * 移除黑名单 + */ + @GetMapping("/removeBlackList") + public AjaxResult removeBlackList(@RequestParam String ids) + { + String[] split = ids.split(","); + List<Long> id = new ArrayList<>(); + for (String s : split) { + id.add(Long.valueOf(s)); + } + userService.updateUserIfBlack(id); + return AjaxResult.success(); + } + + +// @Log(title = "用户管理", businessType = BusinessType.EXPORT) +// @PostMapping("/export") +// public void export(HttpServletResponse response, SysUser user) +// { +// List<SysUser> list = userService.selectUserList(user); +// ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class); +// util.exportExcel(response, list, "用户数据"); +// } + +// @Log(title = "用户管理", businessType = BusinessType.IMPORT) +// @PostMapping("/importData") +// public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception +// { +// ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class); +// List<SysUser> userList = util.importExcel(file.getInputStream()); +// String operName = getUsername(); +// String message = userService.importUser(userList, updateSupport, operName); +// return AjaxResult.success(message); +// } + +// @PostMapping("/importTemplate") +// public void importTemplate(HttpServletResponse response) +// { +// ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class); +// util.importTemplateExcel(response, "用户数据"); +// } + + + /** + * 新增用户 + */ + @ApiOperation(value = "新增用户管理") + @Log(title = "用户信息-新增用户", businessType = BusinessType.INSERT) + @PostMapping("/add") + public AjaxResult add(@Validated @RequestBody SysUser user) + { + user.setUserName(user.getPhonenumber()); + if (!userService.checkUserNameUnique(user)) + { + return error("新增用户'" + user.getUserName() + "'失败,登录账号已存在"); + } + else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) + { + return error("新增用户'" + user.getUserName() + "'失败,手机号码已存在"); + } + user.setCreateBy(getUsername()); + user.setPassword(SecurityUtils.encryptPassword(user.getPassword())); + userService.insertUser(user); + return AjaxResult.success(); + } + + /** + * 修改用户 + */ + @ApiOperation(value = "修改用户管理") + @Log(title = "用户信息-修改用户", businessType = BusinessType.UPDATE) + @PostMapping("/edit") + public AjaxResult edit(@Validated @RequestBody SysUser user) + { + user.setUserName(user.getPhonenumber()); +// userService.checkUserAllowed(user); +// userService.checkUserDataScope(user.getUserId()); + if (!userService.checkUserNameUnique(user)) + { + return error("修改用户'" + user.getUserName() + "'失败,登录账号已存在"); + } + else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) + { + return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); + } + + user.setUpdateBy(getUsername()); + user.setPassword(SecurityUtils.encryptPassword(user.getPassword())); + return AjaxResult.success(userService.updateUser(user)); + } + + /** + * 删除用户 + */ + @ApiOperation(value = "批量删除用户") + @Log(title = "用户信息-批量删除用户", businessType = BusinessType.DELETE) + @DeleteMapping("/deleteById/{ids}") + public AjaxResult remove(@PathVariable String ids) + { + String[] split = ids.split(","); + List<Long> userIds = new ArrayList<>(); + for (String s : split) { + userIds.add(Long.valueOf(s)); + } + if (userIds.contains(getUserId())) + { + return error("当前用户不能删除"); + } + return AjaxResult.success(userService.deleteUserByIds(userIds)); + } + + /** + * 重置密码 + */ + @ApiOperation(value = "重置密码") + @Log(title = "用户信息-重置密码", businessType = BusinessType.UPDATE) + @PostMapping("/resetPwd") + public AjaxResult resetPwd(@RequestBody SysUser user) + { + userService.checkUserAllowed(user); +// userService.checkUserDataScope(user.getUserId()); + user.setPassword(SecurityUtils.encryptPassword(user.getPassword())); + user.setUpdateBy(getUsername()); + return AjaxResult.success(userService.resetPwd(user)); + } + + + /** + * 根据用户编号获取授权角色 + */ + @GetMapping("/authRole/{userId}") + public AjaxResult authRole(@PathVariable("userId") Long userId) + { + AjaxResult ajax = AjaxResult.success(); + SysUser user = userService.selectUserById(userId); + List<SysRole> roles = roleService.selectRolesByUserId(userId); + ajax.put("user", user); + ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList())); + return ajax; + } + + /** + * 用户授权角色 + */ + @Log(title = "用户管理", businessType = BusinessType.GRANT) + @PutMapping("/authRole") + public AjaxResult insertAuthRole(Long userId, Long[] roleIds) + { + userService.checkUserDataScope(userId); + userService.insertUserAuth(userId, roleIds); + return AjaxResult.success(); + } + + /** + * 获取部门树列表 + */ + @GetMapping("/deptTree") + public AjaxResult deptTree(SysDept dept) + { + return AjaxResult.success(deptService.selectDeptTreeList(dept)); + } +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/UserAccountController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/UserAccountController.java new file mode 100644 index 0000000..c1a039b --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/UserAccountController.java @@ -0,0 +1,160 @@ +package com.ruoyi.web.controller.system; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.system.dto.SaveUserBankDto; +import com.ruoyi.system.dto.UserWithdrawalDto; +import com.ruoyi.system.model.*; +import com.ruoyi.system.query.UserAccountDetailQuery; +import com.ruoyi.system.service.*; +import com.ruoyi.system.vo.RegionVo; +import com.ruoyi.system.vo.UserAccountVo; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.parameters.P; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.math.BigDecimal; +import java.util.List; + +@Slf4j +@RestController +@RequestMapping("/user-account") +@Api(tags = "用户钱包模块") +public class UserAccountController { + @Autowired + private TbUserService userService; + + @Autowired + private TokenService tokenService; + + @Autowired + private TbBankService bankService; + + @Autowired + private TbAccountDetailService accountDetailService; + + @Autowired + private TbWithdrawalService withdrawalService; + + + + // TODO 需要订单来查看其他金额 + @ApiOperation(value = "获取用户钱包信息",tags = {"获取用户钱包信息"}) + @GetMapping("/getUserAccount") + public R<UserAccountVo> getUserAccount() { + UserAccountVo userAccountVo = new UserAccountVo(); + LoginUser loginUser = tokenService.getLoginUser(); + TbUser user = userService.getById(loginUser.getUserId()); + userAccountVo.setBalance(user.getBalance()); + + + + + return R.ok(userAccountVo); + } + + + + + + @ApiOperation(value = "获取用户钱包信息-收支明细",tags = {"获取用户钱包信息-收支明细"}) + @GetMapping("/getUserAccountDetail") + public R<Page<TbAccountDetail>> getUserAccountDetail(UserAccountDetailQuery query) { + LoginUser loginUser = tokenService.getLoginUser(); + LambdaQueryWrapper<TbAccountDetail> wrapper = new LambdaQueryWrapper<>(); + if(StringUtils.isNotEmpty(query.getStartTime()) && StringUtils.isNotEmpty(query.getEndTime())){ + wrapper.between(TbAccountDetail::getCreateTime,query.getStartTime(),query.getEndTime()); + } + wrapper.eq(TbAccountDetail::getUserId,loginUser.getUserId()); + wrapper.orderByDesc(TbAccountDetail::getCreateTime); + Page<TbAccountDetail> page = accountDetailService.page(new Page<>(query.getPageNum(),query.getPageSize()),wrapper); + return R.ok(page); + } + + + @ApiOperation(value = "申请提现",tags = {"获取用户钱包信息-申请提现"}) + @PostMapping("/withdrawal") + @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) + public R<?> withdrawal(@RequestBody @Valid UserWithdrawalDto dto) { + if(dto.getAmount()<=0){ + return R.fail("提现金额不能小于0"); + } + LoginUser loginUser = tokenService.getLoginUser(); + TbUser user = userService.getById(loginUser.getUserId()); + // 判断是否有卡 + List<TbBank> list = bankService.list(new LambdaQueryWrapper<TbBank>().eq(TbBank::getUserId, loginUser.getUserId()).eq(TbBank::getIsDelete, 0)); + if(list.isEmpty()){ + return R.fail("请先绑定银行卡"); + } + if(user.getBalance().doubleValue()<dto.getAmount()){ + return R.fail("余额不足"); + } + TbBank tbBank = list.get(0); + TbWithdrawal withdrawal = new TbWithdrawal(); + withdrawal.setUserId(loginUser.getUserId().toString()); + withdrawal.setMoney(new BigDecimal(dto.getAmount())); + withdrawal.setCardNo(tbBank.getCardNo()); + withdrawal.setUsername(tbBank.getUsername()); + withdrawal.setStatus(0); + user.setBalance(user.getBalance().subtract(new BigDecimal(dto.getAmount()))); + userService.updateById(user); + withdrawalService.save(withdrawal); + + // 存储记录 + TbAccountDetail accountDetail = new TbAccountDetail(); + accountDetail.setUserId(loginUser.getUserId().toString()); + accountDetail.setType(2); + accountDetail.setCategory(1); + accountDetail.setStatus(1); + accountDetail.setMoney(new BigDecimal(dto.getAmount())); + accountDetailService.save(accountDetail); + return R.ok(); + } + + + + @ApiOperation(value = "获取用户绑定的卡",tags = {"获取用户绑定的卡"}) + @GetMapping("/getUserBank") + public R<TbBank> getUserBank() { + LoginUser loginUser = tokenService.getLoginUser(); + TbBank tbBank = bankService.getOne(new LambdaQueryWrapper<TbBank>().eq(TbBank::getUserId, loginUser.getUserId()).eq(TbBank::getIsDelete, 0)); + return R.ok(tbBank); + } + + + @ApiOperation(value = "保存银行卡",tags = {"保存银行卡" }) + @PostMapping("/saveUserBank") + public R<?> getUserBank(@Valid @RequestBody SaveUserBankDto dto) { + LoginUser loginUser = tokenService.getLoginUser(); + TbBank tbBank = bankService.getOne(new LambdaQueryWrapper<TbBank>().eq(TbBank::getUserId, loginUser.getUserId()).eq(TbBank::getIsDelete, 0)); + if(tbBank==null){ + tbBank = new TbBank(); + tbBank.setUserId(loginUser.getUserId().toString()); + tbBank.setCardNo(dto.getCardNo()); + tbBank.setUsername(dto.getUsername()); + tbBank.setIsDelete(0); + bankService.save(tbBank); + }else { + tbBank.setCardNo(dto.getCardNo()); + tbBank.setUsername(dto.getUsername()); + bankService.updateById(tbBank); + } + return R.ok(); + } + + + + + +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/UserAddressController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/UserAddressController.java new file mode 100644 index 0000000..c6ed5a0 --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/UserAddressController.java @@ -0,0 +1,85 @@ +package com.ruoyi.web.controller.system; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.system.model.TbAddress; +import com.ruoyi.system.model.TbUser; +import com.ruoyi.system.service.TbAddressService; +import com.ruoyi.system.service.TbRegionService; +import com.ruoyi.system.service.TbUserService; +import com.ruoyi.system.vo.RegionVo; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.ss.formula.functions.T; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.List; + +@Slf4j +@RestController +@RequestMapping("/user-address") +@Api(tags = "用户地址模块") +public class UserAddressController { + + @Autowired + private TbRegionService regionService; + @Autowired + private TbAddressService addressService; + + @Autowired + private TokenService tokenService; + + + + + @ApiOperation(value = "获取用户地址",tags = {"获取用户地址"}) + @GetMapping("/getAddressList") + public R<?> getAddressList() { + LoginUser loginUser = tokenService.getLoginUser(); + Long userId = loginUser.getUserId(); + List<TbAddress> list = addressService.list(new LambdaQueryWrapper<TbAddress>().eq(TbAddress::getUserId, userId).eq(TbAddress::getIsDelete, 0).orderByDesc(TbAddress::getCreateTime)); + return R.ok(list); + } + + + + @ApiOperation(value = "添加修改用户地址",tags = {"添加修改用户地址"}) + @PostMapping("/updateUserAddress") + public R<?> updateUserAddress(@RequestBody @Valid TbAddress tbAddress) { + LoginUser loginUser = tokenService.getLoginUser(); + Long userId = loginUser.getUserId(); + tbAddress.setUserId(userId.toString()); + addressService.saveOrUpdate(tbAddress); + return R.ok(); + } + + + + @ApiOperation(value = "账号注销",tags = {"账号注销"}) + @DeleteMapping("/deleteAddress/{id}") + public R<?> deleteAddress(@PathVariable("id")String id) { + TbAddress address = addressService.getById(id); + address.setIsDelete(1); + addressService.updateById(address); + return R.ok(); + } + + + + @GetMapping(value = "/listRegion") + @ApiOperation(value = "获取省市区三级联动列表") + public R<List<RegionVo>> listCity() { + List<RegionVo> regionVoList = regionService.listCityVo(); + return R.ok(regionVoList); + } + + + +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/UserController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/UserController.java new file mode 100644 index 0000000..16079e1 --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/UserController.java @@ -0,0 +1,105 @@ +package com.ruoyi.web.controller.system; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.common.core.domain.BasePage; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.NumberUtil; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.system.model.TbUser; +import com.ruoyi.system.service.TbUserService; +import com.ruoyi.system.vo.InviteUserListVo; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +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.concurrent.TimeUnit; + +@Slf4j +@RestController +@RequestMapping("/user") +@Api(tags = "用户模块") +public class UserController { + + @Autowired + private TbUserService userService; + + @Autowired + private TokenService tokenService; + + @ApiOperation(value = "修改用户信息",tags = {"修改用户信息"}) + @PostMapping("/updateUserInfo") + public R<?> updateUserInfo(String avatar,String username) { + LoginUser loginUser = tokenService.getLoginUser(); + if (StringUtils.isBlank(avatar)) { + return R.fail("头像不能为空"); + } + if (StringUtils.isBlank(username)) { + return R.fail("姓名不能为空"); + } + Long userId = loginUser.getUserId(); + + TbUser user = userService.getById(userId); + user.setUserName(username); + user.setAvatar(avatar); + userService.updateById(user); + return R.ok(); + } + + + @ApiOperation(value = "获取最新用户信息",tags = {"获取最新用户信息"}) + @GetMapping("/getUserInfo") + public R<?> getUserInfo() { + LoginUser loginUser = tokenService.getLoginUser(); + Long userId = loginUser.getUserId(); + TbUser user = userService.getById(userId); + return R.ok(user); + } + + @ApiOperation(value = "账号注销",tags = {"账号注销"}) + @GetMapping("/accountCancellation") + public R<?> accountCancellation() { + LoginUser loginUser = tokenService.getLoginUser(); + Long userId = loginUser.getUserId(); + TbUser user = userService.getById(userId); + user.setStatus(3); + userService.updateById(user); + return R.ok(user); + } + + + + @ApiOperation(value = "分享有礼列表",tags = {"分享有礼列表"}) + @GetMapping("/getInviteUserList") + public R<Page<InviteUserListVo>> getInviteUserList(BasePage page) { + LoginUser loginUser = tokenService.getLoginUser(); + Long userId = loginUser.getUserId(); + Page<TbUser> page1 = userService.page(new Page<>(page.getPageNum(), page.getPageSize()), new LambdaQueryWrapper<TbUser>().eq(TbUser::getInviteId, userId).orderByDesc(TbUser::getCreateTime)); + Page<InviteUserListVo> inviteUserListVoPage = new Page<>(); + if(page1.getRecords().isEmpty()){ + return R.ok(inviteUserListVoPage); + } + ArrayList<InviteUserListVo> inviteUserListVos = new ArrayList<>(); + for (TbUser record : page1.getRecords()) { + InviteUserListVo inviteUserListVo = new InviteUserListVo(); + inviteUserListVo.setUsername(record.getUserName()); + inviteUserListVo.setCreateTime(record.getCreateTime()); + inviteUserListVo.setInviteNum(record.getInviteNum()); + inviteUserListVos.add(inviteUserListVo); + } + BeanUtils.copyProperties(page1,inviteUserListVoPage); + inviteUserListVoPage.setRecords(inviteUserListVos); + return R.ok(inviteUserListVoPage); + } + + +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/messageController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/messageController.java new file mode 100644 index 0000000..e736e12 --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/messageController.java @@ -0,0 +1,101 @@ +package com.ruoyi.web.controller.system; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.common.core.domain.BasePage; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.system.model.TbMessage; +import com.ruoyi.system.model.TbUser; +import com.ruoyi.system.query.UserMessageQuery; +import com.ruoyi.system.service.TbMessageService; +import com.ruoyi.system.service.TbUserService; +import com.ruoyi.system.vo.InviteUserListVo; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; + +@Slf4j +@RestController +@RequestMapping("/message") +@Api(tags = "消息模块") +public class messageController { + + @Autowired + private TbUserService userService; + + @Autowired + private TbMessageService messageService; + + @Autowired + private TokenService tokenService; + + @ApiOperation(value = "获取用户消息",tags = {"获取用户消息"}) + @GetMapping("/getUserMessage") + public R<Page<TbMessage>> getUserMessage(UserMessageQuery query) { + LoginUser loginUser = tokenService.getLoginUser(); + Long userId = loginUser.getUserId(); + Page<TbMessage> page = messageService.page(new Page<>(query.getPageNum(), query.getPageSize()), new LambdaQueryWrapper<TbMessage>().eq(TbMessage::getUserId, userId)); + return R.ok(page); + } + + + @ApiOperation(value = "消息已读",tags = {"消息已读"}) + @PostMapping("/readMessage") + public R<?> readMessage(String id) { + LoginUser loginUser = tokenService.getLoginUser(); + Long userId = loginUser.getUserId(); + TbMessage message = messageService.getById(id); + if(!message.getUserId().equals(userId.toString())){ + return R.fail("无权限"); + } + message.setIsRead(1); + messageService.updateById(message); + return R.ok(); + } + + @ApiOperation(value = "账号注销",tags = {"账号注销"}) + @GetMapping("/accountCancellation") + public R<?> accountCancellation() { + LoginUser loginUser = tokenService.getLoginUser(); + Long userId = loginUser.getUserId(); + TbUser user = userService.getById(userId); + user.setStatus(3); + userService.updateById(user); + return R.ok(user); + } + + + + @ApiOperation(value = "分享有礼列表",tags = {"分享有礼列表"}) + @GetMapping("/getInviteUserList") + public R<Page<InviteUserListVo>> getInviteUserList(BasePage page) { + LoginUser loginUser = tokenService.getLoginUser(); + Long userId = loginUser.getUserId(); + Page<TbUser> page1 = userService.page(new Page<>(page.getPageNum(), page.getPageSize()), new LambdaQueryWrapper<TbUser>().eq(TbUser::getInviteId, userId).orderByDesc(TbUser::getCreateTime)); + Page<InviteUserListVo> inviteUserListVoPage = new Page<>(); + if(page1.getRecords().isEmpty()){ + return R.ok(inviteUserListVoPage); + } + ArrayList<InviteUserListVo> inviteUserListVos = new ArrayList<>(); + for (TbUser record : page1.getRecords()) { + InviteUserListVo inviteUserListVo = new InviteUserListVo(); + inviteUserListVo.setUsername(record.getUserName()); + inviteUserListVo.setCreateTime(record.getCreateTime()); + inviteUserListVo.setInviteNum(record.getInviteNum()); + inviteUserListVos.add(inviteUserListVo); + } + BeanUtils.copyProperties(page1,inviteUserListVoPage); + inviteUserListVoPage.setRecords(inviteUserListVos); + return R.ok(inviteUserListVoPage); + } + + +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/BaiDuApi.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/BaiDuApi.java new file mode 100644 index 0000000..ad7e1b8 --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/BaiDuApi.java @@ -0,0 +1,180 @@ +package com.ruoyi.web.controller.tool; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.ruoyi.common.exception.ServiceException; +import lombok.extern.slf4j.Slf4j; +import okhttp3.*; + +import java.io.*; +import java.net.URLEncoder; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; + +/** + * 百度文字识别 + */ +@Slf4j +public class BaiDuApi { + + static final OkHttpClient HTTP_CLIENT = new OkHttpClient().newBuilder().build(); + + public static void main(String []args) throws IOException{ +// String url = "C:\\Users\\Admin\\Desktop\\picture\\6fd629ac-3327-4bdc-9459-fcb5295384bc.jpg"; + String url = "C:\\Users\\Admin\\Desktop\\picture\\6fd629ac-3327-4bdc-9459-fcb5295384bc.jpg"; + MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded"); + RequestBody body = RequestBody.create(mediaType, "image="+getFileContentAsBase64(url,true)); + Request request = new Request.Builder() + .url("https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic?access_token="+getAccessToken()) + .method("POST", body) + .addHeader("Content-Type", "application/x-www-form-urlencoded") + .addHeader("Accept", "application/json") + .build(); + Response response = HTTP_CLIENT.newCall(request).execute(); + +// System.err.println(response.body().string()); + + Map<String,Object> map = new HashMap<>(); + String string = response.body().string(); + String idCard = string.substring(string.lastIndexOf("单号:") + 3, string.lastIndexOf("单号:") + 27); + String time = string.substring(string.lastIndexOf("零时起") + 3, string.lastIndexOf("二十四时")-1); + time = time.replace(".","-"); + map.put("idCard",idCard); + map.put("time",time); + System.err.println(map); + } + + /** + * 资质证明证件识别 + * @param url + * @return + * @throws IOException + */ + public static Map<String,Object> qualification(String url) throws IOException { + MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded"); + RequestBody body = RequestBody.create(mediaType, "image="+getFileContentAsBase64(url,true)); + Request request = new Request.Builder() + .url("https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic?access_token="+getAccessToken()) + .method("POST", body) + .addHeader("Content-Type", "application/x-www-form-urlencoded") + .addHeader("Accept", "application/json") + .build(); + Response response = HTTP_CLIENT.newCall(request).execute(); + Map<String,Object> map = new HashMap<>(); + try{ + String string = response.body().string(); + String idCard = string.substring(string.lastIndexOf("号:") + 2, string.lastIndexOf("号:") + 21); + String time = string.substring(string.lastIndexOf("至") + 1, string.lastIndexOf("至") + 11); + time = time.replace(".","-"); + map.put("idCard",idCard); + map.put("time",time); + }catch (Exception e){ + log.error("资质证明证件识别错误!"); + throw new ServiceException("资质证明证件识别错误!"); + } + return map; + } + + /** + * 身份证识别 + * @param url + * @return + * @throws IOException + */ + public static Map<String,Object> idCard(String url) throws IOException { + MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded"); + RequestBody body = RequestBody.create(mediaType, "image="+getFileContentAsBase64(url,true)); + Request request = new Request.Builder() + .url("https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic?access_token="+getAccessToken()) + .method("POST", body) + .addHeader("Content-Type", "application/x-www-form-urlencoded") + .addHeader("Accept", "application/json") + .build(); + Response response = HTTP_CLIENT.newCall(request).execute(); + Map<String,Object> map = new HashMap<>(); + try{ + JSONObject jsonObject = JSONObject.parseObject(response.body().string()); + JSONArray jsonArray = JSONObject.parseArray(jsonObject.getString("words_result")); + JSONObject obj1 = JSONObject.parseObject(jsonArray.get(0).toString()); + String name = obj1.getString("words"); + name = name.substring(2); + + JSONObject obj2 = JSONObject.parseObject(jsonArray.get(1).toString()); + String sexStr = obj2.getString("words"); + sexStr = sexStr.substring(2, 3); + int sex = "男".equals(sexStr)?0:1; + + JSONObject obj3 = JSONObject.parseObject(jsonArray.get(3).toString()); + JSONObject obj4 = JSONObject.parseObject(jsonArray.get(4).toString()); + String address = obj3.getString("words") + obj4.getString("words"); + address = address.substring(2); + + JSONObject obj5 = JSONObject.parseObject(jsonArray.get(5).toString()); + String idCard = obj5.getString("words"); + idCard = idCard.substring(6); + + map.put("name",name); + map.put("sex",sex); + map.put("address",address); + map.put("idCard",idCard); + }catch (Exception e){ + log.error("身份证件识别错误!"); + throw new ServiceException("身份证件识别错误!"); + } + return map; + } + + /** + * 通用文字识别(标准版) + * @param url + * @return + * @throws IOException + */ + public static JSONObject pictureOcr(String url) throws IOException { + MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded"); + RequestBody body = RequestBody.create(mediaType, "image="+getFileContentAsBase64(url,true)); + Request request = new Request.Builder() + .url("https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic?access_token="+getAccessToken()) + .method("POST", body) + .addHeader("Content-Type", "application/x-www-form-urlencoded") + .addHeader("Accept", "application/json") + .build(); + Response response = HTTP_CLIENT.newCall(request).execute(); + return JSONObject.parseObject(response.body().string()); + } + + public static String getAccessToken() throws IOException { + MediaType mediaType = MediaType.parse("application/json"); + RequestBody body = RequestBody.create(mediaType, ""); + Request request = new Request.Builder() + .url("https://aip.baidubce.com/oauth/2.0/token?client_id=2RkKEqd0ltIHPvnIf3G0VpHE&client_secret=RBpPt3O64e3e4BK7pG3lP0o8I6SGgiUy&grant_type=client_credentials") + .method("POST", body) + .addHeader("Content-Type", "application/json") + .addHeader("Accept", "application/json") + .build(); + Response response = HTTP_CLIENT.newCall(request).execute(); + JSONObject jsonObject = JSONObject.parseObject(response.body().string()); + return jsonObject.getString("access_token"); + } + + /** + * 获取文件base64编码 + * + * @param path 文件路径 + * @param urlEncode 如果Content-Type是application/x-www-form-urlencoded时,传true + * @return base64编码信息,不带文件头 + * @throws IOException IO异常 + */ + static String getFileContentAsBase64(String path, boolean urlEncode) throws IOException { + byte[] b = Files.readAllBytes(Paths.get(path)); + String base64 = Base64.getEncoder().encodeToString(b); + if (urlEncode) { + base64 = URLEncoder.encode(base64, "utf-8"); + } + return base64; + } + +} \ No newline at end of file diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/HttpClientUtil.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/HttpClientUtil.java new file mode 100644 index 0000000..2da9a44 --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/HttpClientUtil.java @@ -0,0 +1,79 @@ +package com.ruoyi.web.controller.tool; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; + +/** + * @author zhy + * @title: HttpClientUtil + * @projectName car_park + * @description: http连接工具类 + * @date 2019/10/2219:23 + */ +public class HttpClientUtil { + + + /** + * @param strUrl + * @return byte[] + * @throws + * @description: 获取网络图片转成字节流 + * @author zhy + * @date 2019/10/23 8:59 + */ + public static byte[] getImageFromNetByUrl(String strUrl) { + if (!isURL(strUrl)){ + return null; + } + try { + URL url = new URL(strUrl); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("GET"); + conn.setConnectTimeout(2 * 1000); + InputStream inStream = conn.getInputStream();// 通过输入流获取图片数据 + byte[] btImg = readInputStream(inStream);// 得到图片的二进制数据 + return btImg; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * 从输入流中获取字节流数据 + * + * @param inStream 输入流 + * @return + * @throws Exception + */ + public static byte[] readInputStream(InputStream inStream) throws Exception { + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[10240]; + int len = 0; + while ((len = inStream.read(buffer)) != -1) { + outStream.write(buffer, 0, len); + } + inStream.close(); + return outStream.toByteArray(); + } + + public static boolean isURL(String str) { + str = str.toLowerCase(); + String regex = "^((https|http|ftp|rtsp|mms)?://)" + + "?(([0-9a-z_!~*'().&=+$%-]+: )?[0-9a-z_!~*'().&=+$%-]+@)?" + + "(([0-9]{1,3}\\.){3}[0-9]{1,3}" + + "|" + + "([0-9a-z_!~*'()-]+\\.)*" + + "([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]\\." + + "[a-z]{2,6})" + + "(:[0-9]{1,5})?" + + "((/?)|" + + "(/[0-9a-z_!~*'().;?:@&=+$,%#-]+)+/?)$"; + return str.matches(regex); + } + + +} + \ No newline at end of file diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/ImportExcelUtil.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/ImportExcelUtil.java new file mode 100644 index 0000000..e488f03 --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/ImportExcelUtil.java @@ -0,0 +1,39 @@ +package com.ruoyi.web.controller.tool; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.common.core.domain.R; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.util.List; + +/** + * 导出返回信息 + */ +@Slf4j +public class ImportExcelUtil { + + /** + * @param errorLines 错误行数 + * @param successLines 成功行数 + * @param errorMessage 错误信息 + * @return + * @throws IOException + */ + public static R<String> importReturnMsg(int errorLines, int successLines, List<String> errorMessage) throws IOException { + if (errorLines == 0) { + return R.ok("共" + successLines + "行数据全部导入成功!"); + } else { + JSONObject result = new JSONObject(5); + int totalCount = successLines + errorLines; + result.put("totalCount", totalCount); + result.put("errorCount", errorLines); + result.put("errorMessage", errorMessage); + result.put("successCount", successLines); + result.put("msg", "总上传行数:" + totalCount + ",已导入行数:" + successLines + ",错误行数:" + errorLines); + return R.ok(JSON.toJSONString(result)); + } + } + +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/MsgCodeUtil.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/MsgCodeUtil.java new file mode 100644 index 0000000..073f315 --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/MsgCodeUtil.java @@ -0,0 +1,61 @@ +package com.ruoyi.web.controller.tool; + +import com.alibaba.fastjson.JSONObject; +import com.ruoyi.system.code.SubmitTemplateReg; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.binary.Hex; + +import java.io.Serializable; +import java.nio.charset.StandardCharsets; + +/** + * 短信工具类 + */ +public class MsgCodeUtil implements Serializable { + + /**接口账号用户名*/ + private static final String AP_ID = ""; + /**企业名称*/ + private static final String EC_NAME = ""; + /**签名*/ + private static final String SECRET_KEY = ""; + /**签名编码*/ + private static final String SIGN = ""; + /**模板ID*/ + private static final String TEMPLATE_ID = ""; + + + /** + * 实体封装 + * @param code + * @return + */ + public static SubmitTemplateReg getSubmitTemplateReg(String code,String mobiles) { + SubmitTemplateReg submitReg =new SubmitTemplateReg(); + String[] paramss = {code}; + submitReg.setApId(AP_ID); + submitReg.setEcName(EC_NAME); + submitReg.setSecretKey(SECRET_KEY); + submitReg.setParams(JSONObject.toJSONString(paramss)); + submitReg.setMobiles(mobiles); + submitReg.setAddSerial(""); + submitReg.setSign(SIGN); + submitReg.setTemplateId(TEMPLATE_ID); + submitReg.setMac(TEMPLATE_ID); + StringBuffer stringBuffer = new StringBuffer(); + stringBuffer.append(submitReg.getEcName( ));stringBuffer.append(submitReg.getApId()); + stringBuffer.append(submitReg.getSecretKey());stringBuffer.append(submitReg.getTemplateId());stringBuffer.append(submitReg.getMobiles()); + stringBuffer.append(submitReg.getParams());stringBuffer.append(submitReg.getSign());stringBuffer.append(submitReg.getAddSerial()); + submitReg.setMac(Hex.encodeHexString(stringBuffer.toString().getBytes(StandardCharsets.UTF_8))); + String regText = JSONObject.toJSONString(submitReg); + //加密 + String encode = Base64.encodeBase64String(regText.getBytes()); + System.err.println(encode); + return submitReg; + } + + public static void main(String[] args) { + getSubmitTemplateReg("123456","18398968484"); + } + +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/MsgUtils.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/MsgUtils.java new file mode 100644 index 0000000..119a5ea --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/MsgUtils.java @@ -0,0 +1,70 @@ +package com.ruoyi.web.controller.tool; + +import com.aliyun.dysmsapi20170525.models.SendSmsRequest; +import com.aliyun.dysmsapi20170525.models.SendSmsResponse; +import com.aliyun.tea.TeaException; +import com.aliyun.teaopenapi.models.Config; +import com.aliyun.teautil.models.RuntimeOptions; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class MsgUtils { + + @Value("${code.config.accessKeyId}") + private String accessKeyId; + @Value("${code.config.accessKeySecret}") + private String accessKeySecret; + @Value("${code.config.signName}") + private String signName; + @Value("${code.config.templateCode}") + private String templateCode; + @Value("${code.config.signNameTest}") + private String signNameTest; + @Value("${code.config.templateCodeTest}") + private String templateCodeTest; + + /** + * 使用AK&SK初始化账号Client + * @param accessKeyId + * @param accessKeySecret + * @return Client + * @throws Exception + */ + public static com.aliyun.dysmsapi20170525.Client createClient(String accessKeyId, String accessKeySecret) throws Exception { + Config config = new Config() + // 您的 AccessKey ID + .setAccessKeyId(accessKeyId) + // 您的 AccessKey Secret + .setAccessKeySecret(accessKeySecret); + // 访问的域名 + config.endpoint = "dysmsapi.aliyuncs.com"; + return new com.aliyun.dysmsapi20170525.Client(config); + } + + public void sendMsg(String phone,String code) throws Exception { + com.aliyun.dysmsapi20170525.Client client = MsgUtils.createClient(accessKeyId,accessKeySecret); + SendSmsRequest sendSmsRequest = new SendSmsRequest() + .setSignName(signName) + .setTemplateCode(templateCode) + .setPhoneNumbers(phone) + .setTemplateParam("{\"code\":\""+code+"\"}"); + RuntimeOptions runtime = new RuntimeOptions(); + try { + // 复制代码运行请自行打印 API 的返回值 + SendSmsResponse sendSmsResponse = client.sendSmsWithOptions(sendSmsRequest, runtime); + log.info("短信发送成功:{},{}",sendSmsResponse.getBody().getMessage(),sendSmsResponse.getStatusCode()); + } catch (TeaException error) { + // 如有需要,请打印 error + com.aliyun.teautil.Common.assertAsString(error.message); + log.info("短信发送失败:{}",error.message); + } catch (Exception _error) { + TeaException error = new TeaException(_error.getMessage(), _error); + // 如有需要,请打印 error + com.aliyun.teautil.Common.assertAsString(error.message); + log.info("短信发送失败:{}",error.message); + } + } +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/MyFileUtil.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/MyFileUtil.java new file mode 100644 index 0000000..40af28a --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/MyFileUtil.java @@ -0,0 +1,128 @@ +package com.ruoyi.web.controller.tool; + +import org.springframework.web.multipart.MultipartFile; +import java.io.*; +import java.nio.file.Files; + + +/** + * @author RainbowCloud + */ +public class MyFileUtil { + + /** + * 将 File 转换为 MultipartFile。 + * + * @param file 要转换的文件 + * @param fieldName 字段名,通常用于表单中的文件字段名 + * @return 转换后的 MultipartFile + * @throws IOException 如果发生I/O错误 + */ + public static MultipartFile fileToMultipartFile(File file, String fieldName) throws IOException { + try { + if (file == null || !file.exists()) { + throw new FileNotFoundException("文件未找到:" + file); + } + byte[] content = Files.readAllBytes(file.toPath()); + return new ByteArrayMultipartFile(content, file.getName(), fieldName, Files.probeContentType(file.toPath())); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + // 删除临时文件 + file.delete(); + } + } + + /** + * 将 MultipartFile 转换为 File。 + * + * @param multipartFile 要转换的 MultipartFile + * @return 转换后的 File + * @throws IOException 如果发生I/O错误 + */ + public static File multipartFileToFile(MultipartFile multipartFile) throws IOException { + if (multipartFile.isEmpty()) { + throw new IOException("传入的MultipartFile为空"); + } + String originalFilename = multipartFile.getOriginalFilename(); + String tempFileSuffix = originalFilename != null ? originalFilename.substring(originalFilename.lastIndexOf('.')) : ".tmp"; + File tempFile = File.createTempFile("temp", tempFileSuffix); + try (InputStream ins = multipartFile.getInputStream(); + OutputStream os = new FileOutputStream(tempFile)) { + byte[] buffer = new byte[8192]; + int bytesRead; + while ((bytesRead = ins.read(buffer)) != -1) { + os.write(buffer, 0, bytesRead); + } + } + return tempFile; + } + + /** + * 内置一个简单的 MultipartFile 实现类,用于File转换 + */ + private static class ByteArrayMultipartFile implements MultipartFile { + private final byte[] content; + private final String name; + private final String originalFilename; + private final String contentType; + + /** + * 构造函数 + * + * @param content 文件内容 + * @param originalFilename 文件原始名字 + * @param name 字段名 + * @param contentType 文件类型 + */ + public ByteArrayMultipartFile(byte[] content, String originalFilename, String name, String contentType) { + this.content = content; + this.originalFilename = originalFilename; + this.name = name; + this.contentType = contentType; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public String getOriginalFilename() { + return this.originalFilename; + } + + @Override + public String getContentType() { + return this.contentType; + } + + @Override + public boolean isEmpty() { + return (this.content == null || this.content.length == 0); + } + + @Override + public long getSize() { + return this.content.length; + } + + @Override + public byte[] getBytes() { + return this.content; + } + + @Override + public InputStream getInputStream() { + return new ByteArrayInputStream(this.content); + } + + @Override + public void transferTo(File dest) throws IOException, IllegalStateException { + try (OutputStream os = new FileOutputStream(dest)) { + os.write(this.content); + } + } + } + +} \ No newline at end of file diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/QRCodeUtil.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/QRCodeUtil.java new file mode 100644 index 0000000..9097589 --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/QRCodeUtil.java @@ -0,0 +1,124 @@ +package com.ruoyi.web.controller.tool; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.EncodeHintType; +import com.google.zxing.MultiFormatWriter; +import com.google.zxing.WriterException; +import com.google.zxing.common.BitMatrix; +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; +import com.ruoyi.common.utils.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.imageio.ImageIO; +import javax.swing.filechooser.FileSystemView; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Map; + +/** + * 二维码工具 + * @Author:debug (SteadyJack) + * @Link: weixin-> debug0868 qq-> 1948831260 + * @Date: 2020/11/16 22:38 + **/ +public class QRCodeUtil { + private static final Logger log= LoggerFactory.getLogger(QRCodeUtil.class); + + //CODE_WIDTH:二维码宽度,单位像素 + private static final int CODE_WIDTH = 400; + //CODE_HEIGHT:二维码高度,单位像素 + private static final int CODE_HEIGHT = 400; + //FRONT_COLOR:二维码前景色,0x000000 表示黑色 + private static final int FRONT_COLOR = 0x000000; + //BACKGROUND_COLOR:二维码背景色,0xFFFFFF 表示白色 + //演示用 16 进制表示,和前端页面 CSS 的取色是一样的,注意前后景颜色应该对比明显,如常见的黑白 + private static final int BACKGROUND_COLOR = 0xFFFFFF; + + + public static void main(String[] args) { + createCodeToFile("5261548530",new File("C:\\Users\\Admin\\Desktop\\qrcode"),"5261548530.png"); + } + + public static void createCodeToFile(String content, File codeImgFileSaveDir, String fileName) { + try { + if (StringUtils.isBlank(content) || StringUtils.isBlank(fileName)) { + return; + } + content = content.trim(); + if (codeImgFileSaveDir==null || codeImgFileSaveDir.isFile()) { + //二维码图片存在目录为空,默认放在桌面... + codeImgFileSaveDir = FileSystemView.getFileSystemView().getHomeDirectory(); + } + if (!codeImgFileSaveDir.exists()) { + //二维码图片存在目录不存在,开始创建... + codeImgFileSaveDir.mkdirs(); + } + + //核心代码-生成二维码 + BufferedImage bufferedImage = getBufferedImage(content); + + File codeImgFile = new File(codeImgFileSaveDir, fileName); + ImageIO.write(bufferedImage, "png", codeImgFile); + + log.info("二维码图片生成成功:" + codeImgFile.getPath()); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 生成二维码并输出到输出流, 通常用于输出到网页上进行显示,输出到网页与输出到磁盘上的文件中,区别在于最后一句 ImageIO.write + * write(RenderedImage im,String formatName,File output):写到文件中 + * write(RenderedImage im,String formatName,OutputStream output):输出到输出流中 + * @param content :二维码内容 + * @param outputStream :输出流,比如 HttpServletResponse 的 getOutputStream + */ + public static void createCodeToOutputStream(String content, OutputStream outputStream) { + try { + if (StringUtils.isBlank(content)) { + return; + } + content = content.trim(); + //核心代码-生成二维码 + BufferedImage bufferedImage = getBufferedImage(content); + + //区别就是这一句,输出到输出流中,如果第三个参数是 File,则输出到文件中 + ImageIO.write(bufferedImage, "png", outputStream); + + log.info("二维码图片生成到输出流成功..."); + } catch (Exception e) { + e.printStackTrace(); + } + } + + //核心代码-生成二维码 + private static BufferedImage getBufferedImage(String content) throws WriterException { + + //com.google.zxing.EncodeHintType:编码提示类型,枚举类型 + Map<EncodeHintType, Object> hints = new HashMap(); + + //EncodeHintType.CHARACTER_SET:设置字符编码类型 + hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); + + //EncodeHintType.ERROR_CORRECTION:设置误差校正 + //ErrorCorrectionLevel:误差校正等级,L = ~7% correction、M = ~15% correction、Q = ~25% correction、H = ~30% correction + //不设置时,默认为 L 等级,等级不一样,生成的图案不同,但扫描的结果是一样的 + hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M); + + //EncodeHintType.MARGIN:设置二维码边距,单位像素,值越小,二维码距离四周越近 + hints.put(EncodeHintType.MARGIN, 1); + + MultiFormatWriter multiFormatWriter = new MultiFormatWriter(); + BitMatrix bitMatrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, CODE_WIDTH, CODE_HEIGHT, hints); + BufferedImage bufferedImage = new BufferedImage(CODE_WIDTH, CODE_HEIGHT, BufferedImage.TYPE_INT_BGR); + for (int x = 0; x < CODE_WIDTH; x++) { + for (int y = 0; y < CODE_HEIGHT; y++) { + bufferedImage.setRGB(x, y, bitMatrix.get(x, y) ? FRONT_COLOR : BACKGROUND_COLOR); + } + } + return bufferedImage; + } +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/TencentCosUtil.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/TencentCosUtil.java new file mode 100644 index 0000000..f86298b --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/TencentCosUtil.java @@ -0,0 +1,187 @@ +package com.ruoyi.web.controller.tool; + +import com.qcloud.cos.COSClient; +import com.qcloud.cos.ClientConfig; +import com.qcloud.cos.auth.BasicCOSCredentials; +import com.qcloud.cos.auth.COSCredentials; +import com.qcloud.cos.http.HttpProtocol; +import com.qcloud.cos.model.ObjectMetadata; +import com.qcloud.cos.model.PutObjectResult; +import com.qcloud.cos.region.Region; +import com.ruoyi.common.utils.WebUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletResponse; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.util.Base64; +import java.util.UUID; + +/** + * @author HJL + */ +@Component +public class TencentCosUtil { + + /** + * COS的SecretId + */ + @Value("${cos.client.accessKey}") + private String secretId; + /** + * COS的SecretKey + */ + @Value("${cos.client.secretKey}") + private String secretKey; + /** + * 文件上传后访问路径的根路径,后面要最佳文件名字与类型 + */ + @Value("${cos.client.rootSrc}") + private String rootSrc; + /** + * 上传的存储桶的地域 + */ + @Value("${cos.client.bucketAddr}") + private String bucketAddr; + /** + * 存储桶的名字,是自己在存储空间自己创建的,我创建的名字是:qq-test-1303****** + */ + @Value("${cos.client.bucket}") + private String bucketName; + /** + * 文件存放位置 + */ + @Value("${cos.client.location}") + private String location; + + /** + * 1.调用静态方法getCosClient()就会获得COSClient实例 + * 2.本方法根据永久密钥初始化 COSClient的,官方是不推荐,官方推荐使用临时密钥,是可以限制密钥使用权限,创建cred时有些区别 + * + * @return COSClient实例 + */ + private COSClient getCosClient() { + // 1 初始化用户身份信息(secretId, secretKey)。 + COSCredentials cred = new BasicCOSCredentials(secretId, secretKey); + // 2.1 设置存储桶的地域(上文获得) + Region region = new Region(bucketAddr); + ClientConfig clientConfig = new ClientConfig(region); + // 2.2 使用https协议传输 + clientConfig.setHttpProtocol(HttpProtocol.https); + // 生成 cos 客户端 + return new COSClient(cred, clientConfig); + } + + /** + * 只要调用静态方法upLoadFile(MultipartFile multipartFile)就可以获取上传后文件的全路径 + * + * @param file + * @return 返回文件的浏览全路径 + */ + public String upLoadFile(MultipartFile file) { + try { + // 获取上传的文件的输入流 + InputStream inputStream = file.getInputStream(); + // 避免文件覆盖,获取文件的原始名称,如123.jpg,然后通过截取获得文件的后缀,也就是文件的类型 + String originalFilename = file.getOriginalFilename(); + //获取文件的类型 + String fileType = originalFilename.substring(originalFilename.lastIndexOf(".")); + //使用UUID工具 创建唯一名称,放置文件重名被覆盖,在拼接上上命令获取的文件类型 + String fileName = UUID.randomUUID() + fileType; + // 指定文件上传到 COS 上的路径,即对象键。最终文件会传到存储桶名字中的images文件夹下的fileName名字 + String key = location+"/" + fileName; + // 创建上传Object的Metadata + ObjectMetadata objectMetadata = new ObjectMetadata(); + // - 使用输入流存储,需要设置请求长度 + objectMetadata.setContentLength(inputStream.available()); + // - 设置缓存 + objectMetadata.setCacheControl("no-cache"); + // - 设置Content-Type + objectMetadata.setContentType(fileType); + //上传文件 + PutObjectResult putResult = getCosClient().putObject(bucketName, key, inputStream, objectMetadata); + // 创建文件的网络访问路径 + String url = rootSrc + key; + //关闭 cosClient,并释放 HTTP 连接的后台管理线程 + getCosClient().shutdown(); + return url; + } catch (Exception e) { + e.printStackTrace(); + // 发生IO异常、COS连接异常等,返回空 + return null; + } + } + + /** + * 下载文件 + * @param file + * @return + */ + public void downLoadFile(String file) { + HttpServletResponse response = WebUtils.response(); + String replace = file.replace(rootSrc, ""); + response.setHeader("Access-Control-Expose-Headers","File-Type"); + COSCredentials cred = new BasicCOSCredentials( + secretId, + secretKey); + // 2.1 设置存储桶的地域(上文获得) + Region region = new Region(bucketAddr); + ClientConfig clientConfig = new ClientConfig(region); + // 2.2 使用https协议传输 + clientConfig.setHttpProtocol(HttpProtocol.https); + COSClient cosClient = new COSClient(cred, clientConfig); + try { + // 5. 下载文件并获取输入流 + InputStream inputStream = cosClient.getObject(bucketName, replace).getObjectContent(); + ServletOutputStream outputStream = response.getOutputStream(); + // 6. 处理输入流,例如读取内容或保存到本地文件 + // 这里仅作示例,实际应用中需要根据需求处理输入流 + byte[] buffer = new byte[1024]; + int len; + while ((len = inputStream.read(buffer)) != -1) { + // 处理读取到的数据 + outputStream.write(buffer, 0, len); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + // 7. 关闭输入流 + cosClient.shutdown(); + } + } + public String downLoadFileImg(String file) { + byte[] data = null; + String replace = file.replace(rootSrc, ""); + COSCredentials cred = new BasicCOSCredentials( + secretId, + secretKey); + // 2.1 设置存储桶的地域(上文获得) + Region region = new Region(bucketAddr); + ClientConfig clientConfig = new ClientConfig(region); + // 2.2 使用https协议传输 + clientConfig.setHttpProtocol(HttpProtocol.https); + COSClient cosClient = new COSClient(cred, clientConfig); + try { + // 5. 下载文件并获取输入流 + InputStream inputStream = cosClient.getObject(bucketName, replace).getObjectContent(); + ByteArrayOutputStream swapStream = new ByteArrayOutputStream(); + // 6. 处理输入流,例如读取内容或保存到本地文件 + byte[] buffer = new byte[1024]; + int len; + while ((len = inputStream.read(buffer)) != -1) { + // 处理读取到的数据 + swapStream.write(buffer, 0, len); + } + data = swapStream.toByteArray(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + // 7. 关闭输入流 + cosClient.shutdown(); + } + return Base64.getEncoder().encodeToString(data); + } +} \ No newline at end of file diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/TestController.java b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/TestController.java new file mode 100644 index 0000000..1a14c49 --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/controller/tool/TestController.java @@ -0,0 +1,177 @@ +package com.ruoyi.web.controller.tool; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.StringUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import io.swagger.annotations.ApiOperation; + +/** + * swagger 用户测试方法 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/test/user") +public class TestController extends BaseController +{ + private final static Map<Integer, UserEntity> users = new LinkedHashMap<Integer, UserEntity>(); + { + users.put(1, new UserEntity(1, "admin", "admin123", "15888888888")); + users.put(2, new UserEntity(2, "ry", "admin123", "15666666666")); + } + + @GetMapping("/list") + public R<List<UserEntity>> userList() + { + List<UserEntity> userList = new ArrayList<UserEntity>(users.values()); + return R.ok(userList); + } + + @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class) + @GetMapping("/{userId}") + public R<UserEntity> getUser(@PathVariable Integer userId) + { + if (!users.isEmpty() && users.containsKey(userId)) + { + return R.ok(users.get(userId)); + } + else + { + return R.fail("用户不存在"); + } + } + + @ApiImplicitParams({ + @ApiImplicitParam(name = "userId", value = "用户id", dataType = "Integer", dataTypeClass = Integer.class), + @ApiImplicitParam(name = "username", value = "用户名称", dataType = "String", dataTypeClass = String.class), + @ApiImplicitParam(name = "password", value = "用户密码", dataType = "String", dataTypeClass = String.class), + @ApiImplicitParam(name = "mobile", value = "用户手机", dataType = "String", dataTypeClass = String.class) + }) + @PostMapping("/save") + public R<String> save(UserEntity user) + { + if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())) + { + return R.fail("用户ID不能为空"); + } + users.put(user.getUserId(), user); + return R.ok(); + } + + @PutMapping("/update") + public R<String> update(@RequestBody UserEntity user) + { + if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())) + { + return R.fail("用户ID不能为空"); + } + if (users.isEmpty() || !users.containsKey(user.getUserId())) + { + return R.fail("用户不存在"); + } + users.remove(user.getUserId()); + users.put(user.getUserId(), user); + return R.ok(); + } + + @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class) + @DeleteMapping("/{userId}") + public R<String> delete(@PathVariable Integer userId) + { + if (!users.isEmpty() && users.containsKey(userId)) + { + users.remove(userId); + return R.ok(); + } + else + { + return R.fail("用户不存在"); + } + } +} + +@ApiModel(value = "UserEntity", description = "用户实体") +class UserEntity +{ + @ApiModelProperty("用户ID") + private Integer userId; + + @ApiModelProperty("用户名称") + private String username; + + @ApiModelProperty("用户密码") + private String password; + + @ApiModelProperty("用户手机") + private String mobile; + + public UserEntity() + { + + } + + public UserEntity(Integer userId, String username, String password, String mobile) + { + this.userId = userId; + this.username = username; + this.password = password; + this.mobile = mobile; + } + + public Integer getUserId() + { + return userId; + } + + public void setUserId(Integer userId) + { + this.userId = userId; + } + + public String getUsername() + { + return username; + } + + public void setUsername(String username) + { + this.username = username; + } + + public String getPassword() + { + return password; + } + + public void setPassword(String password) + { + this.password = password; + } + + public String getMobile() + { + return mobile; + } + + public void setMobile(String mobile) + { + this.mobile = mobile; + } +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/core/config/DataUpdateHandlerConfig.java b/ruoyi-applet/src/main/java/com/ruoyi/web/core/config/DataUpdateHandlerConfig.java new file mode 100644 index 0000000..e27f2a5 --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/core/config/DataUpdateHandlerConfig.java @@ -0,0 +1,61 @@ +package com.ruoyi.web.core.config; + +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.reflection.MetaObject; +import org.springframework.context.annotation.Configuration; + +/** + * @author xiaochen + * @ClassName DataUpdateInterceptor + * @Description 数据更新操作处理 + * @date 2021-12-15 + * <p> + * 注意,之前在此处注入了 JwtTokenUtils + * <p> + * 造成spring循环依赖,项目支棱不起来 + */ +@Slf4j +@Configuration +public class DataUpdateHandlerConfig implements MetaObjectHandler { + + /** + * 新增数据执行 + * + * @param metaObject + */ + @Override + public void insertFill(MetaObject metaObject) { + // 获取登录信息 +// String userName = SecurityUtils.getUsernameApplet(); +// if (StringUtils.isNotBlank(userName)) { +// this.setFieldValByName("createBy", userName, metaObject); +// this.setFieldValByName("updateBy", userName, metaObject); +// } else { +// this.setFieldValByName("createBy", userName, metaObject); +// this.setFieldValByName("updateBy", userName, metaObject); +// } + + } + + /** + * 修改数据执行 + * + * @param metaObject + */ + @Override + public void updateFill(MetaObject metaObject) { + // 获取登录信息 +// String userName = SecurityUtils.getUsernameApplet(); +// if (StringUtils.isNotBlank(userName)){ +// this.setFieldValByName("createBy", userName, metaObject); +// this.setFieldValByName("updateBy", userName, metaObject); +// } else { +// this.setFieldValByName("createBy", userName, metaObject); +// this.setFieldValByName("updateBy", userName, metaObject); +// } + + } +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/core/config/MybatisPlusConfig.java b/ruoyi-applet/src/main/java/com/ruoyi/web/core/config/MybatisPlusConfig.java new file mode 100644 index 0000000..fac9a9f --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/core/config/MybatisPlusConfig.java @@ -0,0 +1,51 @@ +package com.ruoyi.web.core.config; + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.core.config.GlobalConfig; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author liheng + * @ClassName MybatisPlusConfig + * @Description MybatisPlus相关配置 + * @date 2020-09-22 11:22、 + * 直接以实现类作为bean的注入(有事务管理的类) + * @EnableTransactionManagement(proxyTargetClass = true) + */ +@Configuration +public class MybatisPlusConfig { + private final DataUpdateHandlerConfig dataUpdateHandler; + + @Autowired + public MybatisPlusConfig(DataUpdateHandlerConfig dataUpdateHandler) { + this.dataUpdateHandler = dataUpdateHandler; + } + + /** + * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题 + */ + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); + return interceptor; + } + + /** + * 自动填充功能 + * + * @return + */ + @Bean + public GlobalConfig globalConfig() { + GlobalConfig globalConfig = new GlobalConfig(); + globalConfig.setMetaObjectHandler(dataUpdateHandler); + return globalConfig; + } + + +} diff --git a/ruoyi-applet/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java b/ruoyi-applet/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java new file mode 100644 index 0000000..0b7961f --- /dev/null +++ b/ruoyi-applet/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java @@ -0,0 +1,125 @@ +package com.ruoyi.web.core.config; + +import java.util.ArrayList; +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import com.ruoyi.common.config.RuoYiConfig; +import io.swagger.annotations.ApiOperation; +import io.swagger.models.auth.In; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.ApiKey; +import springfox.documentation.service.AuthorizationScope; +import springfox.documentation.service.Contact; +import springfox.documentation.service.SecurityReference; +import springfox.documentation.service.SecurityScheme; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spi.service.contexts.SecurityContext; +import springfox.documentation.spring.web.plugins.Docket; + +/** + * Swagger2的接口配置 + * + * @author ruoyi + */ +@Configuration +public class SwaggerConfig +{ + /** 系统基础配置 */ + @Autowired + private RuoYiConfig ruoyiConfig; + + /** 是否开启swagger */ + @Value("${swagger.enabled}") + private boolean enabled; + + /** 设置请求的统一前缀 */ + @Value("${swagger.pathMapping}") + private String pathMapping; + + /** + * 创建API + */ + @Bean + public Docket createRestApi() + { + return new Docket(DocumentationType.OAS_30) + // 是否启用Swagger + .enable(enabled) + // 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息) + .apiInfo(apiInfo()) + // 设置哪些接口暴露给Swagger展示 + .select() + // 扫描所有有注解的api,用这种方式更灵活 + .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) + // 扫描指定包中的swagger注解 + // .apis(RequestHandlerSelectors.basePackage("com.ruoyi.project.tool.swagger")) + // 扫描所有 .apis(RequestHandlerSelectors.any()) + .paths(PathSelectors.any()) + .build() + /* 设置安全模式,swagger可以设置访问token */ + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()) + .pathMapping(pathMapping); + } + + /** + * 安全模式,这里指定token通过Authorization头请求头传递 + */ + private List<SecurityScheme> securitySchemes() + { + List<SecurityScheme> apiKeyList = new ArrayList<SecurityScheme>(); + apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue())); + return apiKeyList; + } + + /** + * 安全上下文 + */ + private List<SecurityContext> securityContexts() + { + List<SecurityContext> securityContexts = new ArrayList<>(); + securityContexts.add( + SecurityContext.builder() + .securityReferences(defaultAuth()) + .operationSelector(o -> o.requestMappingPattern().matches("/.*")) + .build()); + return securityContexts; + } + + /** + * 默认的安全上引用 + */ + private List<SecurityReference> defaultAuth() + { + AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); + AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; + authorizationScopes[0] = authorizationScope; + List<SecurityReference> securityReferences = new ArrayList<>(); + securityReferences.add(new SecurityReference("Authorization", authorizationScopes)); + return securityReferences; + } + + /** + * 添加摘要信息 + */ + private ApiInfo apiInfo() + { + // 用ApiInfoBuilder进行定制 + return new ApiInfoBuilder() + // 设置标题 + .title("标题:证省心_接口文档") + // 描述 + .description("证省心接口文档") + // 作者信息 + .contact(new Contact(ruoyiConfig.getName(), null, null)) + // 版本 + .version("版本号:" + ruoyiConfig.getVersion()) + .build(); + } +} diff --git a/ruoyi-applet/src/main/resources/META-INF/spring-devtools.properties b/ruoyi-applet/src/main/resources/META-INF/spring-devtools.properties new file mode 100644 index 0000000..2b23f85 --- /dev/null +++ b/ruoyi-applet/src/main/resources/META-INF/spring-devtools.properties @@ -0,0 +1 @@ +restart.include.json=/com.alibaba.fastjson.*.jar \ No newline at end of file diff --git a/ruoyi-applet/src/main/resources/application-prod.yml b/ruoyi-applet/src/main/resources/application-prod.yml new file mode 100644 index 0000000..10c4c27 --- /dev/null +++ b/ruoyi-applet/src/main/resources/application-prod.yml @@ -0,0 +1,230 @@ +# 项目相关配置 +ruoyi: + # 名称 + name: RuoYi + # 版本 + version: 3.8.6 + # 版权年份 + copyrightYear: 2023 + # 实例演示开关 + demoEnabled: true + # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) + profile: D:/ruoyi/uploadPath + # 获取ip地址开关 + addressEnabled: false + # 验证码类型 math 数字计算 char 字符验证 + captchaType: math + +# 开发环境配置 +server: + # 服务器的HTTP端口,默认为8080 + port: 8082 + servlet: + # 应用的访问路径 + context-path: / + tomcat: + # tomcat的URI编码 + uri-encoding: UTF-8 + # 连接数满后的排队数,默认为100 + accept-count: 1000 + threads: + # tomcat最大线程数,默认为200 + max: 800 + # Tomcat启动初始化的线程数,默认值10 + min-spare: 100 + +# 日志配置 +logging: + level: + com.ruoyi: debug + org.springframework: warn + +# 用户配置 +user: + password: + # 密码最大错误次数 + maxRetryCount: 5 + # 密码锁定时间(默认10分钟) + lockTime: 10 + +# Spring配置 +spring: + main: + allow-bean-definition-overriding: true + # 资源信息 + messages: + # 国际化资源文件路径 + basename: i18n/messages + # 文件上传 + servlet: + multipart: + # 单个文件大小 + max-file-size: 500MB + # 设置总上传的文件大小 + max-request-size: 2000MB + # 服务模块 + devtools: + restart: + # 热部署开关 + enabled: true + # redis 配置 + redis: + # 地址 + # host: 127.0.0.1 + # # 端口,默认为6379 + # port: 6379 + # # 数据库索引 + # database: 0 + # # 密码 + # password: 123456 + host: 127.0.0.1 + # 端口,默认为6379 + port: 16379 + # 数据库索引 + database: 0 + # 密码 + password: 8f5z9g52gx4bg + # 连接超时时间 + timeout: 10s + lettuce: + pool: + # 连接池中的最小空闲连接 + min-idle: 0 + # 连接池中的最大空闲连接 + max-idle: 8 + # 连接池的最大数据库连接数 + max-active: 8 + # #连接池最大阻塞等待时间(使用负值表示没有限制) + max-wait: -1ms + # 数据源配置 + datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + druid: + # 主库数据源 + master: + url: jdbc:mysql://172.27.0.13:3306/xizang?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai + username: xzgt + password: changyun!6f2gshj6h3j + # 从库数据源 + slave: + # 从数据源开关/默认关闭 + enabled: false + url: + username: + password: + # 初始连接数 + initialSize: 5 + # 最小连接池数量 + minIdle: 10 + # 最大连接池数量 + maxActive: 20 + # 配置获取连接等待超时的时间 + maxWait: 60000 + # 配置连接超时时间 + connectTimeout: 30000 + # 配置网络超时时间 + socketTimeout: 60000 + # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 60000 + # 配置一个连接在池中最小生存的时间,单位是毫秒 + minEvictableIdleTimeMillis: 300000 + # 配置一个连接在池中最大生存的时间,单位是毫秒 + maxEvictableIdleTimeMillis: 900000 + # 配置检测连接是否有效 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + webStatFilter: + enabled: true + statViewServlet: + enabled: true + # 设置白名单,不填则允许所有访问 + allow: + url-pattern: /druid/* + # 控制台管理用户名和密码 + login-username: ruoyi + login-password: 123456 + filter: + stat: + enabled: true + # 慢SQL记录 + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: true + wall: + config: + multi-statement-allow: true +# token配置 +token: + # 令牌自定义标识 + header: Authorization + # 令牌密钥 + secret: abcdefghijklmnopqrstuvwxyz + # 令牌有效期(默认30分钟) + expireTime: 120 + +mybatis-plus: + # 此处在多数据源中生效 + config-location: classpath:/mybatis-config.xml + global-config: + banner: false + db-config: + logic-not-delete-value: 0 + logic-delete-value: 1 + type-aliases-package: com.ruoyi.**.domain,com.ruoyi.**.vo,com.ruoyi.**.model + # 指定Mapper文件位置 + mapper-locations: classpath*:mapper/**/*.xml + +# Swagger配置 +swagger: + # 是否开启swagger + enabled: true + # 请求前缀 + pathMapping: / + +# 防止XSS攻击 +xss: + # 过滤开关 + enabled: true + # 排除链接(多个用逗号分隔) + excludes: /system/notice + # 匹配链接 + urlPatterns: /system/*,/monitor/*,/tool/* +# file upload +file: + upload: + location: /file/ + qrLocation: /file/qrCode/ + accessPath: /file/ + allowExt: .jpg|.png|.gif|.jpeg|.doc|.docx|.apk|.MP4|.mp4|.pdf|.PDF + url: + prefix: https://xzgt.test.591taxi.cn:${server.port}${server.servlet.context-path} +wx: + conf: + appId: wxe91f1af7638aa5dd + secretId: a787e1a462715604e0c9528b6d8960d1 +#OSS及短信配置 +code: + config: + templateCodeTest: "SMS_154950909" + signNameTest: "阿里云短信测试" + accessKeyId: LTAI5tAdba8HtT1C6UqtSxBt + accessKeySecret: 0SRb6XGkciQDPWn2rYqbJtq2qRMDY8 + signName: "四川金达通信工程" + templateCode: "SMS_293985284" +cos: + client: + accessKey: AKIDCF5EF2c0DE1e5JK8r4EGJF4mNsMgp26x + secretKey: lLl184rUyFOOE0d5KNGC3kmfNsCWk4GU + bucket: xzgttest-1305134071 + bucketAddr: ap-chengdu + rootSrc: https://xzgttest-1305134071.cos.ap-chengdu.myqcloud.com/ + location: xizang +sms: + enable: true + appId: 1400957506 + secretid: AKIDCF5EF2c0DE1e5JK8r4EGJF4mNsMgp26x + secretkey: lLl184rUyFOOE0d5KNGC3kmfNsCWk4GU + sign: 畅云出行 diff --git a/ruoyi-applet/src/main/resources/application-test.yml b/ruoyi-applet/src/main/resources/application-test.yml new file mode 100644 index 0000000..5debd2a --- /dev/null +++ b/ruoyi-applet/src/main/resources/application-test.yml @@ -0,0 +1,230 @@ +# 项目相关配置 +ruoyi: + # 名称 + name: RuoYi + # 版本 + version: 3.8.6 + # 版权年份 + copyrightYear: 2023 + # 实例演示开关 + demoEnabled: true + # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) + profile: D:/ruoyi/uploadPath + # 获取ip地址开关 + addressEnabled: false + # 验证码类型 math 数字计算 char 字符验证 + captchaType: math + +# 开发环境配置 +server: + # 服务器的HTTP端口,默认为8080 + port: 9082 + servlet: + # 应用的访问路径 + context-path: / + tomcat: + # tomcat的URI编码 + uri-encoding: UTF-8 + # 连接数满后的排队数,默认为100 + accept-count: 1000 + threads: + # tomcat最大线程数,默认为200 + max: 800 + # Tomcat启动初始化的线程数,默认值10 + min-spare: 100 + +# 日志配置 +logging: + level: + com.ruoyi: debug + org.springframework: warn + +# 用户配置 +user: + password: + # 密码最大错误次数 + maxRetryCount: 5 + # 密码锁定时间(默认10分钟) + lockTime: 10 + +# Spring配置 +spring: + main: + allow-bean-definition-overriding: true + # 资源信息 + messages: + # 国际化资源文件路径 + basename: i18n/messages + # 文件上传 + servlet: + multipart: + # 单个文件大小 + max-file-size: 500MB + # 设置总上传的文件大小 + max-request-size: 2000MB + # 服务模块 + devtools: + restart: + # 热部署开关 + enabled: true + # redis 配置 + redis: + # 地址 + host: 127.0.0.1 +# # 端口,默认为6379 +# port: 6379 +# # 数据库索引 +# database: 0 +# # 密码 +# password: 123456 + # 端口,默认为6379 + port: 6379 + # 数据库索引 + database: 0 + # 密码 + password: 123456 + # 连接超时时间 + timeout: 10s + lettuce: + pool: + # 连接池中的最小空闲连接 + min-idle: 0 + # 连接池中的最大空闲连接 + max-idle: 8 + # 连接池的最大数据库连接数 + max-active: 8 + # #连接池最大阻塞等待时间(使用负值表示没有限制) + max-wait: -1ms +# 数据源配置 + datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + druid: + # 主库数据源 + master: + url: jdbc:mysql://192.168.110.111:3306/zhengshengxin?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai + username: root + password: 123456 + # 从库数据源 + slave: + # 从数据源开关/默认关闭 + enabled: false + url: + username: + password: + # 初始连接数 + initialSize: 5 + # 最小连接池数量 + minIdle: 10 + # 最大连接池数量 + maxActive: 20 + # 配置获取连接等待超时的时间 + maxWait: 60000 + # 配置连接超时时间 + connectTimeout: 30000 + # 配置网络超时时间 + socketTimeout: 60000 + # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 60000 + # 配置一个连接在池中最小生存的时间,单位是毫秒 + minEvictableIdleTimeMillis: 300000 + # 配置一个连接在池中最大生存的时间,单位是毫秒 + maxEvictableIdleTimeMillis: 900000 + # 配置检测连接是否有效 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + webStatFilter: + enabled: true + statViewServlet: + enabled: true + # 设置白名单,不填则允许所有访问 + allow: + url-pattern: /druid/* + # 控制台管理用户名和密码 + login-username: ruoyi + login-password: 123456 + filter: + stat: + enabled: true + # 慢SQL记录 + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: true + wall: + config: + multi-statement-allow: true +# token配置 +token: + # 令牌自定义标识 + header: Authorization + # 令牌密钥 + secret: abcdefghijklmnopqrstuvwxyz + # 令牌有效期(默认30分钟) + expireTime: 120 + +mybatis-plus: + # 此处在多数据源中生效 + config-location: classpath:/mybatis-config.xml + global-config: + banner: false + db-config: + logic-not-delete-value: 0 + logic-delete-value: 1 + type-aliases-package: com.ruoyi.**.domain,com.ruoyi.**.vo,com.ruoyi.**.model + # 指定Mapper文件位置 + mapper-locations: classpath*:mapper/**/*.xml + +# Swagger配置 +swagger: + # 是否开启swagger + enabled: true + # 请求前缀 + pathMapping: / + +# 防止XSS攻击 +xss: + # 过滤开关 + enabled: true + # 排除链接(多个用逗号分隔) + excludes: /system/notice + # 匹配链接 + urlPatterns: /system/*,/monitor/*,/tool/* +# file upload +file: + upload: + location: /file/ + qrLocation: /file/qrCode/ + accessPath: /file/ + allowExt: .jpg|.png|.gif|.jpeg|.doc|.docx|.apk|.MP4|.mp4|.pdf|.PDF + url: +# prefix: http://localhost:${server.port}${server.servlet.context-path} + prefix: https://xzgt.test.591taxi.cn:${server.port}${server.servlet.context-path} +wx: + conf: + appId: wxe91f1af7638aa5dd + secretId: a787e1a462715604e0c9528b6d8960d1 +#OSS及短信配置 +code: + config: + templateCodeTest: "SMS_154950909" + signNameTest: "阿里云短信测试" + accessKeyId: LTAI5tAdba8HtT1C6UqtSxBt + accessKeySecret: 0SRb6XGkciQDPWn2rYqbJtq2qRMDY8 + signName: "四川金达通信工程" + templateCode: "SMS_293985284" +cos: + client: + accessKey: AKIDCF5EF2c0DE1e5JK8r4EGJF4mNsMgp26x + secretKey: lLl184rUyFOOE0d5KNGC3kmfNsCWk4GU + bucket: xzgttest-1305134071 + bucketAddr: ap-chengdu + rootSrc: https://xzgttest-1305134071.cos.ap-chengdu.myqcloud.com/ + location: xizang +sms: + enable: true + appId: 1400957506 + secretid: AKIDCF5EF2c0DE1e5JK8r4EGJF4mNsMgp26x + secretkey: lLl184rUyFOOE0d5KNGC3kmfNsCWk4GU + sign: 畅云出行 diff --git a/ruoyi-applet/src/main/resources/application.yml b/ruoyi-applet/src/main/resources/application.yml new file mode 100644 index 0000000..dcc106d --- /dev/null +++ b/ruoyi-applet/src/main/resources/application.yml @@ -0,0 +1,4 @@ +# 项目相关配置 +spring: + profiles: + active: test diff --git a/ruoyi-applet/src/main/resources/banner.txt b/ruoyi-applet/src/main/resources/banner.txt new file mode 100644 index 0000000..0931cb8 --- /dev/null +++ b/ruoyi-applet/src/main/resources/banner.txt @@ -0,0 +1,24 @@ +Application Version: ${ruoyi.version} +Spring Boot Version: ${spring-boot.version} +//////////////////////////////////////////////////////////////////// +// _ooOoo_ // +// o8888888o // +// 88" . "88 // +// (| ^_^ |) // +// O\ = /O // +// ____/`---'\____ // +// .' \\| |// `. // +// / \\||| : |||// \ // +// / _||||| -:- |||||- \ // +// | | \\\ - /// | | // +// | \_| ''\---/'' | | // +// \ .-\__ `-` ___/-. / // +// ___`. .' /--.--\ `. . ___ // +// ."" '< `.___\_<|>_/___.' >'"". // +// | | : `- \`.;`\ _ /`;.`/ - ` : | | // +// \ \ `-. \_ __\ /__ _/ .-` / / // +// ========`-.____`-.___\_____/___.-`____.-'======== // +// `=---=' // +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // +// 佛祖保佑 永不宕机 永无BUG // +//////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/ruoyi-applet/src/main/resources/i18n/messages.properties b/ruoyi-applet/src/main/resources/i18n/messages.properties new file mode 100644 index 0000000..93de005 --- /dev/null +++ b/ruoyi-applet/src/main/resources/i18n/messages.properties @@ -0,0 +1,38 @@ +#错误消息 +not.null=* 必须填写 +user.jcaptcha.error=验证码错误 +user.jcaptcha.expire=验证码已失效 +user.not.exists=用户不存在/密码错误 +user.password.not.match=用户不存在/密码错误 +user.password.retry.limit.count=密码输入错误{0}次 +user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟 +user.password.delete=对不起,您的账号已被删除 +user.blocked=用户已封禁,请联系管理员 +role.blocked=角色已封禁,请联系管理员 +login.blocked=很遗憾,访问IP已被列入系统黑名单 +user.logout.success=退出成功 + +length.not.valid=长度必须在{min}到{max}个字符之间 + +user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头 +user.password.not.valid=* 5-50个字符 + +user.email.not.valid=邮箱格式错误 +user.mobile.phone.number.not.valid=手机号格式错误 +user.login.success=登录成功 +user.register.success=注册成功 +user.notfound=请重新登录 +user.forcelogout=管理员强制退出,请重新登录 +user.unknown.error=未知错误,请重新登录 + +##文件上传消息 +upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB! +upload.filename.exceed.length=上传的文件名最长{0}个字符 + +##权限 +no.permission=您没有数据的权限,请联系管理员添加权限 [{0}] +no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}] +no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}] +no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}] +no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}] +no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}] diff --git a/ruoyi-applet/src/main/resources/logback.xml b/ruoyi-applet/src/main/resources/logback.xml new file mode 100644 index 0000000..a360583 --- /dev/null +++ b/ruoyi-applet/src/main/resources/logback.xml @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="UTF-8"?> +<configuration> + <!-- 日志存放路径 --> + <property name="log.path" value="/home/ruoyi/logs" /> + <!-- 日志输出格式 --> + <property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" /> + + <!-- 控制台输出 --> + <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> + <encoder> + <pattern>${log.pattern}</pattern> + </encoder> + </appender> + + <!-- 系统日志输出 --> + <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>${log.path}/sys-info.log</file> + <!-- 循环政策:基于时间创建日志文件 --> + <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> + <!-- 日志文件名格式 --> + <fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern> + <!-- 日志最大的历史 60天 --> + <maxHistory>60</maxHistory> + </rollingPolicy> + <encoder> + <pattern>${log.pattern}</pattern> + </encoder> + <filter class="ch.qos.logback.classic.filter.LevelFilter"> + <!-- 过滤的级别 --> + <level>INFO</level> + <!-- 匹配时的操作:接收(记录) --> + <onMatch>ACCEPT</onMatch> + <!-- 不匹配时的操作:拒绝(不记录) --> + <onMismatch>DENY</onMismatch> + </filter> + </appender> + + <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>${log.path}/sys-error.log</file> + <!-- 循环政策:基于时间创建日志文件 --> + <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> + <!-- 日志文件名格式 --> + <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern> + <!-- 日志最大的历史 60天 --> + <maxHistory>60</maxHistory> + </rollingPolicy> + <encoder> + <pattern>${log.pattern}</pattern> + </encoder> + <filter class="ch.qos.logback.classic.filter.LevelFilter"> + <!-- 过滤的级别 --> + <level>ERROR</level> + <!-- 匹配时的操作:接收(记录) --> + <onMatch>ACCEPT</onMatch> + <!-- 不匹配时的操作:拒绝(不记录) --> + <onMismatch>DENY</onMismatch> + </filter> + </appender> + + <!-- 用户访问日志输出 --> + <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>${log.path}/sys-user.log</file> + <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> + <!-- 按天回滚 daily --> + <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern> + <!-- 日志最大的历史 60天 --> + <maxHistory>60</maxHistory> + </rollingPolicy> + <encoder> + <pattern>${log.pattern}</pattern> + </encoder> + </appender> + + <!-- 系统模块日志级别控制 --> + <logger name="com.ruoyi" level="info" /> + <!-- Spring日志级别控制 --> + <logger name="org.springframework" level="warn" /> + + <root level="info"> + <appender-ref ref="console" /> + </root> + + <!--系统操作日志--> + <root level="info"> + <appender-ref ref="file_info" /> + <appender-ref ref="file_error" /> + </root> + + <!--系统用户操作日志--> + <logger name="sys-user" level="info"> + <appender-ref ref="sys-user"/> + </logger> +</configuration> \ No newline at end of file diff --git a/ruoyi-applet/src/main/resources/mybatis-config.xml b/ruoyi-applet/src/main/resources/mybatis-config.xml new file mode 100644 index 0000000..53c5587 --- /dev/null +++ b/ruoyi-applet/src/main/resources/mybatis-config.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> +<configuration> + + <settings> + <!-- 打印查询语句 不会写入到日志文件中--> + <setting name="logImpl" value="STDOUT_LOGGING"/> + <!--<setting name="logImpl" value="LOG4J" />--> + <!-- 控制全局缓存(二级缓存),按美团技术团队的说法,尽量别用缓存机制 emmmm.... --> + <setting name="cacheEnabled" value="true"/> + <!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认 false --> + <!-- <setting name="lazyLoadingEnabled" value="true"/> --> + <setting name="mapUnderscoreToCamelCase" value="true"/><!--是否将map下划线方式转为驼峰式命名--> + <!-- 当开启时,任何方法的调用都会加载该对象的所有属性。默认 false,可通过select标签的 fetchType来覆盖--> + <!-- <setting name="aggressiveLazyLoading" value="false"/>--> + <!-- Mybatis 创建具有延迟加载能力的对象所用到的代理工具,默认JAVASSIST --> + <!--<setting name="proxyFactory" value="CGLIB" />--> + <!-- 关于mybatis的一二级缓存 请参照:https://tech.meituan.com/2018/01/19/mybatis-cache.html --> + <!-- 一级缓存范围默认:SESSION ,此范围在复杂应用场景中可能会出现脏读数据--> + <!-- STATEMENT级别的缓存,使一级缓存,只针对当前执行的这一statement有效 --> + <!--<setting name="localCacheScope" value="STATEMENT"/>--> + <setting name="localCacheScope" value="STATEMENT"/> + </settings> + +</configuration> diff --git a/ruoyi-applet/src/main/resources/mybatis/mybatis-config.xml b/ruoyi-applet/src/main/resources/mybatis/mybatis-config.xml new file mode 100644 index 0000000..ac47c03 --- /dev/null +++ b/ruoyi-applet/src/main/resources/mybatis/mybatis-config.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE configuration +PUBLIC "-//mybatis.org//DTD Config 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-config.dtd"> +<configuration> + <!-- 全局参数 --> + <settings> + <!-- 使全局的映射器启用或禁用缓存 --> + <setting name="cacheEnabled" value="true" /> + <!-- 允许JDBC 支持自动生成主键 --> + <setting name="useGeneratedKeys" value="true" /> + <!-- 配置默认的执行器.SIMPLE就是普通执行器;REUSE执行器会重用预处理语句(prepared statements);BATCH执行器将重用语句并执行批量更新 --> + <setting name="defaultExecutorType" value="SIMPLE" /> + <!-- 指定 MyBatis 所用日志的具体实现 --> + <setting name="logImpl" value="SLF4J" /> + <!-- 使用驼峰命名法转换字段 --> + <!-- <setting name="mapUnderscoreToCamelCase" value="true"/> --> + </settings> + +</configuration> diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml new file mode 100644 index 0000000..6a1ae59 --- /dev/null +++ b/ruoyi-common/pom.xml @@ -0,0 +1,211 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>zhengshengxin</artifactId> + <groupId>com.ruoyi</groupId> + <version>3.8.6</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>ruoyi-common</artifactId> + + <description> + common通用工具 + </description> + <properties> + <swagger.fox.version>3.0.0</swagger.fox.version> + <swagger.core.version>1.6.2</swagger.core.version> + </properties> + <dependencies> + <dependency> + <groupId>com.documents4j</groupId> + <artifactId>documents4j-local</artifactId> + <version>1.0.3</version> + + </dependency> + <dependency> + <groupId>com.documents4j</groupId> + <artifactId>documents4j-transformer-msoffice-word</artifactId> + <version>1.0.3</version> + </dependency> + <dependency> + <groupId>org.freemarker</groupId> + <artifactId>freemarker</artifactId> + <version>2.3.33</version> + </dependency> + <!-- Spring框架基本的核心工具 --> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-context-support</artifactId> + </dependency> + + <!-- SpringWeb模块 --> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-web</artifactId> + </dependency> + + <!-- spring security 安全认证 --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-security</artifactId> + </dependency> + + <!-- pagehelper 分页插件 --> +<!-- <dependency>--> +<!-- <groupId>com.github.pagehelper</groupId>--> +<!-- <artifactId>pagehelper-spring-boot-starter</artifactId>--> +<!-- </dependency>--> + + <!-- 自定义验证注解 --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-validation</artifactId> + </dependency> + + <!--常用工具类 --> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + </dependency> + + <!-- JSON工具类 --> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + </dependency> + + <!-- 动态数据源 --> + <dependency> + <groupId>com.baomidou</groupId> + <artifactId>dynamic-datasource-spring-boot-starter</artifactId> + <version>3.5.2</version> + </dependency> + + <!-- 阿里JSON解析器 --> + <dependency> + <groupId>com.alibaba.fastjson2</groupId> + <artifactId>fastjson2</artifactId> + </dependency> + + <!-- io常用工具类 --> + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + </dependency> + + <!-- excel工具 --> +<!-- <dependency>--> +<!-- <groupId>org.apache.poi</groupId>--> +<!-- <artifactId>poi-ooxml</artifactId>--> +<!-- </dependency>--> + + <!-- yml解析器 --> + <dependency> + <groupId>org.yaml</groupId> + <artifactId>snakeyaml</artifactId> + </dependency> + + <!-- Token生成与解析--> + <dependency> + <groupId>io.jsonwebtoken</groupId> + <artifactId>jjwt</artifactId> + </dependency> + + <!-- Jaxb --> + <dependency> + <groupId>javax.xml.bind</groupId> + <artifactId>jaxb-api</artifactId> + </dependency> + + <!-- redis 缓存操作 --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-data-redis</artifactId> + </dependency> + + <!-- pool 对象池 --> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-pool2</artifactId> + </dependency> + + <!-- 解析客户端操作系统、浏览器等 --> + <dependency> + <groupId>eu.bitwalker</groupId> + <artifactId>UserAgentUtils</artifactId> + </dependency> + + <!-- servlet包 --> + <dependency> + <groupId>javax.servlet</groupId> + <artifactId>javax.servlet-api</artifactId> + </dependency> + <!--mybatis-plus--> + <dependency> + <groupId>com.baomidou</groupId> + <artifactId>mybatis-plus-boot-starter</artifactId> + <version>3.5.2</version> + </dependency> + <!--lombok--> + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <version>1.18.32</version> <!-- 确保兼容 JDK 8 --> + </dependency> + + <dependency> + <groupId>io.springfox</groupId> + <artifactId>springfox-swagger-ui</artifactId> + <version>3.0.0</version> + </dependency> + <dependency> + <groupId>com.github.xiaoymin</groupId> + <artifactId>knife4j-spring-boot-starter</artifactId> + <version>3.0.3</version> + </dependency> +<!-- <dependency>--> +<!-- <groupId>io.swagger</groupId>--> +<!-- <artifactId>swagger-models</artifactId>--> +<!-- <version>${swagger.core.version}</version>--> +<!-- </dependency>--> +<!-- <dependency>--> +<!-- <groupId>io.swagger</groupId>--> +<!-- <artifactId>swagger-annotations</artifactId>--> +<!-- <version>${swagger.core.version}</version>--> +<!-- </dependency>--> + + <dependency> + <groupId>ws.schild</groupId> + <artifactId>jave-all-deps</artifactId> + <version>2.5.1</version> + </dependency> + <dependency> + <groupId>com.tencentcloudapi</groupId> + <artifactId>tencentcloud-sdk-java</artifactId> + <version>4.0.11</version> + </dependency> + + <!-- 工作流--> + <dependency> + <groupId>com.aizuda</groupId> + <artifactId>flowlong-spring-boot-starter</artifactId> + <version>1.0.4</version> + </dependency> + + <!-- hutool--> + <dependency> + <groupId>cn.hutool</groupId> + <artifactId>hutool-all</artifactId> + <version>5.8.4</version> + </dependency> + <dependency> + <groupId>com.sun.mail</groupId> + <artifactId>javax.mail</artifactId> + <version>1.6.2</version> <!-- 请检查是否有更新的版本 --> + </dependency> + </dependencies> + +</project> \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Anonymous.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Anonymous.java new file mode 100644 index 0000000..1d6d4f4 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Anonymous.java @@ -0,0 +1,19 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 匿名访问不鉴权注解 + * + * @author ruoyi + */ +@Target({ ElementType.METHOD, ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Anonymous +{ +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java new file mode 100644 index 0000000..be49c80 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java @@ -0,0 +1,33 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 数据权限过滤注解 + * + * @author ruoyi + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DataScope +{ + /** + * 部门表的别名 + */ + public String deptAlias() default ""; + + /** + * 用户表的别名 + */ + public String userAlias() default ""; + + /** + * 权限字符(用于多个角色匹配符合要求的权限)默认根据权限注解@ss获取,多个权限用逗号分隔开来 + */ + public String permission() default ""; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java new file mode 100644 index 0000000..79cd191 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java @@ -0,0 +1,28 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import com.ruoyi.common.enums.DataSourceType; + +/** + * 自定义多数据源切换注解 + * + * 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准 + * + * @author ruoyi + */ +@Target({ ElementType.METHOD, ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +public @interface DataSource +{ + /** + * 切换数据源名称 + */ + public DataSourceType value() default DataSourceType.MASTER; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java new file mode 100644 index 0000000..9b4411d --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java @@ -0,0 +1,187 @@ +//package com.ruoyi.common.annotation; +// +//import java.lang.annotation.ElementType; +//import java.lang.annotation.Retention; +//import java.lang.annotation.RetentionPolicy; +//import java.lang.annotation.Target; +//import java.math.BigDecimal; +//import org.apache.poi.ss.usermodel.HorizontalAlignment; +//import org.apache.poi.ss.usermodel.IndexedColors; +//import com.ruoyi.common.utils.poi.ExcelHandlerAdapter; +// +///** +// * 自定义导出Excel数据注解 +// * +// * @author ruoyi +// */ +//@Retention(RetentionPolicy.RUNTIME) +//@Target(ElementType.FIELD) +//public @interface Excel +//{ +// /** +// * 导出时在excel中排序 +// */ +// public int sort() default Integer.MAX_VALUE; +// +// /** +// * 导出到Excel中的名字. +// */ +// public String name() default ""; +// +// /** +// * 日期格式, 如: yyyy-MM-dd +// */ +// public String dateFormat() default ""; +// +// /** +// * 如果是字典类型,请设置字典的type值 (如: sys_user_sex) +// */ +// public String dictType() default ""; +// +// /** +// * 读取内容转表达式 (如: 0=男,1=女,2=未知) +// */ +// public String readConverterExp() default ""; +// +// /** +// * 分隔符,读取字符串组内容 +// */ +// public String separator() default ","; +// +// /** +// * BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化) +// */ +// public int scale() default -1; +// +// /** +// * BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN +// */ +// public int roundingMode() default BigDecimal.ROUND_HALF_EVEN; +// +// /** +// * 导出时在excel中每个列的高度 +// */ +// public double height() default 14; +// +// /** +// * 导出时在excel中每个列的宽度 +// */ +// public double width() default 16; +// +// /** +// * 文字后缀,如% 90 变成90% +// */ +// public String suffix() default ""; +// +// /** +// * 当值为空时,字段的默认值 +// */ +// public String defaultValue() default ""; +// +// /** +// * 提示信息 +// */ +// public String prompt() default ""; +// +// /** +// * 设置只能选择不能输入的列内容. +// */ +// public String[] combo() default {}; +// +// /** +// * 是否需要纵向合并单元格,应对需求:含有list集合单元格) +// */ +// public boolean needMerge() default false; +// +// /** +// * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写. +// */ +// public boolean isExport() default true; +// +// /** +// * 另一个类中的属性名称,支持多级获取,以小数点隔开 +// */ +// public String targetAttr() default ""; +// +// /** +// * 是否自动统计数据,在最后追加一行统计数据总和 +// */ +// public boolean isStatistics() default false; +// +// /** +// * 导出类型(0数字 1字符串 2图片) +// */ +// public ColumnType cellType() default ColumnType.STRING; +// +// /** +// * 导出列头背景颜色 +// */ +// public IndexedColors headerBackgroundColor() default IndexedColors.GREY_50_PERCENT; +// +// /** +// * 导出列头字体颜色 +// */ +// public IndexedColors headerColor() default IndexedColors.WHITE; +// +// /** +// * 导出单元格背景颜色 +// */ +// public IndexedColors backgroundColor() default IndexedColors.WHITE; +// +// /** +// * 导出单元格字体颜色 +// */ +// public IndexedColors color() default IndexedColors.BLACK; +// +// /** +// * 导出字段对齐方式 +// */ +// public HorizontalAlignment align() default HorizontalAlignment.CENTER; +// +// /** +// * 自定义数据处理器 +// */ +// public Class<?> handler() default ExcelHandlerAdapter.class; +// +// /** +// * 自定义数据处理器参数 +// */ +// public String[] args() default {}; +// +// /** +// * 字段类型(0:导出导入;1:仅导出;2:仅导入) +// */ +// Type type() default Type.ALL; +// +// public enum Type +// { +// ALL(0), EXPORT(1), IMPORT(2); +// private final int value; +// +// Type(int value) +// { +// this.value = value; +// } +// +// public int value() +// { +// return this.value; +// } +// } +// +// public enum ColumnType +// { +// NUMERIC(0), STRING(1), IMAGE(2); +// private final int value; +// +// ColumnType(int value) +// { +// this.value = value; +// } +// +// public int value() +// { +// return this.value; +// } +// } +//} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excels.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excels.java new file mode 100644 index 0000000..fa5c430 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excels.java @@ -0,0 +1,18 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Excel注解集 + * + * @author ruoyi + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Excels +{ +// public Excel[] value(); +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Log.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Log.java new file mode 100644 index 0000000..1eb8e49 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Log.java @@ -0,0 +1,51 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.enums.OperatorType; + +/** + * 自定义操作日志记录注解 + * + * @author ruoyi + * + */ +@Target({ ElementType.PARAMETER, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Log +{ + /** + * 模块 + */ + public String title() default ""; + + /** + * 功能 + */ + public BusinessType businessType() default BusinessType.OTHER; + + /** + * 操作人类别 + */ + public OperatorType operatorType() default OperatorType.MANAGE; + + /** + * 是否保存请求的参数 + */ + public boolean isSaveRequestData() default true; + + /** + * 是否保存响应的参数 + */ + public boolean isSaveResponseData() default true; + + /** + * 排除指定的请求参数 + */ + public String[] excludeParamNames() default {}; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java new file mode 100644 index 0000000..0f024c7 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java @@ -0,0 +1,40 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.enums.LimitType; + +/** + * 限流注解 + * + * @author ruoyi + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RateLimiter +{ + /** + * 限流key + */ + public String key() default CacheConstants.RATE_LIMIT_KEY; + + /** + * 限流时间,单位秒 + */ + public int time() default 60; + + /** + * 限流次数 + */ + public int count() default 100; + + /** + * 限流类型 + */ + public LimitType limitType() default LimitType.DEFAULT; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java new file mode 100644 index 0000000..b769748 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java @@ -0,0 +1,31 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义注解防止表单重复提交 + * + * @author ruoyi + * + */ +@Inherited +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RepeatSubmit +{ + /** + * 间隔时间(ms),小于此时间视为重复提交 + */ + public int interval() default 5000; + + /** + * 提示消息 + */ + public String message() default "不允许重复提交,请稍候再试"; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/basic/PageInfo.java b/ruoyi-common/src/main/java/com/ruoyi/common/basic/PageInfo.java new file mode 100644 index 0000000..9de94e9 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/basic/PageInfo.java @@ -0,0 +1,70 @@ +package com.ruoyi.common.basic; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +import java.io.Serializable; +import java.util.List; + +/** + * @Author liheng + * @Date 2019/08/26 10:28 AM + * @Description + */ +@JsonIgnoreProperties({"orders", "optimizeCountSql", "hitCount", "countId", "maxLimit", "searchCount"}) +public class PageInfo<T> extends Page<T> implements Serializable { + + private static final long serialVersionUID = 1L; + private boolean hasNextPage; + private boolean hasPrevPage; + private long startIndex; + + public PageInfo(long currentPage, long pageShowCount) { + super(currentPage, pageShowCount); + this.startIndex = super.offset(); + } + + public PageInfo() { + super(); + } + + private static boolean hasPrevPage(long currentPage) { + return currentPage != 1; + } + + @Override + public PageInfo<T> setRecords(List<T> records) { + super.setRecords(records); + this.hasNextPage = super.hasNext(); + this.hasPrevPage = super.hasPrevious(); + return this; + } + + public boolean getHasNextPage() { + return hasNextPage; + } + + public void setHasNextPage(boolean hasNextPage) { + this.hasNextPage = hasNextPage; + } + + public boolean getHasPrevPage() { + return hasPrevPage; + } + + public void setHasPrevPage(boolean hasPrevPage) { + this.hasPrevPage = hasPrevPage; + } + + private boolean hasNextPage(long currentPage, long totalPage) { + return currentPage < totalPage && totalPage != 0; + } + + public long getStartIndex() { + return startIndex; + } + + public void setStartIndex(long startIndex) { + this.startIndex = startIndex; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/config/FileUploadConfig.java b/ruoyi-common/src/main/java/com/ruoyi/common/config/FileUploadConfig.java new file mode 100644 index 0000000..32c0b2a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/config/FileUploadConfig.java @@ -0,0 +1,23 @@ +package com.ruoyi.common.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * <p>文件上传配置</p> + * + * @author mouseyCat + * @date 2020/10/13 16:10 + */ +@Data +@Component +@ConfigurationProperties(prefix = "file.upload") +public class FileUploadConfig { + private String accessPath; + private String allowExt; + private String location; + private String qrLocation; + private String fileUrlPrefix; + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/config/MailProperties.java b/ruoyi-common/src/main/java/com/ruoyi/common/config/MailProperties.java new file mode 100644 index 0000000..b3cc7e2 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/config/MailProperties.java @@ -0,0 +1,32 @@ +package com.ruoyi.common.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Data +@Component +@ConfigurationProperties(prefix = "mail") +public class MailProperties { + + private String smtpHost="gz-smtp.qcloudmail.com"; + + private Integer smtpPort = 465; + + private String userAddr = "test@xzgtmail.591taxi.cn"; + + private String password = "CY20250226pass"; + + private String userName = "测试"; + + /** + * 账单提醒 ,同一个用户离上次发送短信的最小间隔 + * 单位分钟 + */ + private Integer billSmsDelayPeriod = 60; + /** + * 账单提醒 ,同一个用户离上次发送邮件的最小间隔 + * 单位分钟 + */ + private Integer billMailDelayPeriod = 60; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java b/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java new file mode 100644 index 0000000..00f70f6 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java @@ -0,0 +1,135 @@ +package com.ruoyi.common.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 读取项目相关配置 + * + * @author ruoyi + */ +@Component +@ConfigurationProperties(prefix = "ruoyi") +public class RuoYiConfig +{ + /** 项目名称 */ + private String name; + + /** 版本 */ + private String version; + + /** 版权年份 */ + private String copyrightYear; + + /** 实例演示开关 */ + private boolean demoEnabled; + + /** 上传路径 */ + private static String profile; + + /** 获取地址开关 */ + private static boolean addressEnabled; + + /** 验证码类型 */ + private static String captchaType; + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getVersion() + { + return version; + } + + public void setVersion(String version) + { + this.version = version; + } + + public String getCopyrightYear() + { + return copyrightYear; + } + + public void setCopyrightYear(String copyrightYear) + { + this.copyrightYear = copyrightYear; + } + + public boolean isDemoEnabled() + { + return demoEnabled; + } + + public void setDemoEnabled(boolean demoEnabled) + { + this.demoEnabled = demoEnabled; + } + + public static String getProfile() + { + return profile; + } + + public void setProfile(String profile) + { + RuoYiConfig.profile = profile; + } + + public static boolean isAddressEnabled() + { + return addressEnabled; + } + + public void setAddressEnabled(boolean addressEnabled) + { + RuoYiConfig.addressEnabled = addressEnabled; + } + + public static String getCaptchaType() { + return captchaType; + } + + public void setCaptchaType(String captchaType) { + RuoYiConfig.captchaType = captchaType; + } + + /** + * 获取导入上传路径 + */ + public static String getImportPath() + { + return getProfile() + "/import"; + } + + /** + * 获取头像上传路径 + */ + public static String getAvatarPath() + { + return getProfile() + "/avatar"; + } + + /** + * 获取下载路径 + */ + public static String getDownloadPath() + { + return getProfile() + "/download/"; + } + + /** + * 获取上传路径 + */ + public static String getUploadPath() + { + return getProfile() + "/upload"; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/config/SmsProperties.java b/ruoyi-common/src/main/java/com/ruoyi/common/config/SmsProperties.java new file mode 100644 index 0000000..7a45914 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/config/SmsProperties.java @@ -0,0 +1,38 @@ +package com.ruoyi.common.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Data +@Component +@ConfigurationProperties(prefix = "sms") +public class SmsProperties { + + public static final String ENABLE_KEY = "sms.enable"; + + private Boolean enable; + + private String appId; + + private String secretid; + + private String secretkey; + /** + * 短信签名 + */ + private String sign; + + /** + * 账单提醒 ,同一个用户离上次发送短信的最小间隔 + * 单位分钟 + */ + private Integer billSmsDelayPeriod = 60; + /** + * 账单提醒 ,同一个用户离上次发送邮件的最小间隔 + * 单位分钟 + */ + private Integer billMailDelayPeriod = 60; + + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/config/WxConfig.java b/ruoyi-common/src/main/java/com/ruoyi/common/config/WxConfig.java new file mode 100644 index 0000000..770412a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/config/WxConfig.java @@ -0,0 +1,37 @@ +package com.ruoyi.common.config; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + + +/** + * 项目中需继承此类 + * + * @author lihen + */ +@Data +@Component +@ConfigurationProperties(prefix = "wx.config") +public class WxConfig { + + /** + * 获取 App ID + * + * @return App ID + */ + @JsonProperty("appId") + private String appId; + + /** + * 获取 Secret + * + * @return Secret + */ + @JsonProperty("secret") + private String secret; + + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/AmountConstant.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/AmountConstant.java new file mode 100644 index 0000000..64c916f --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/AmountConstant.java @@ -0,0 +1,15 @@ +package com.ruoyi.common.constant; + +import java.math.BigDecimal; + +public class AmountConstant { + + public static final BigDecimal b100 = new BigDecimal("100"); + + public static final BigDecimal b1000 = new BigDecimal("1000"); + + public static final BigDecimal b001 = new BigDecimal("0.01"); + + + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java new file mode 100644 index 0000000..a3e4868 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java @@ -0,0 +1,50 @@ +package com.ruoyi.common.constant; + +/** + * 缓存的key 常量 + * + * @author ruoyi + */ +public class CacheConstants +{ + /** + * 登录用户 redis key + */ + public static final String LOGIN_TOKEN_KEY = "login_tokens:"; + + /** + * 验证码 redis key + */ + public static final String CAPTCHA_CODE_KEY = "captcha_codes:"; + + /** + * 参数管理 cache key + */ + public static final String SYS_CONFIG_KEY = "sys_config:"; + + /** + * 字典管理 cache key + */ + public static final String SYS_DICT_KEY = "sys_dict:"; + + /** + * 防重提交 redis key + */ + public static final String REPEAT_SUBMIT_KEY = "repeat_submit:"; + + /** + * 限流 redis key + */ + public static final String RATE_LIMIT_KEY = "rate_limit:"; + + /** + * 登录账户密码错误次数 redis key + */ + public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:"; + + + public static final String BILL_UPDATE_LOCK_KEY = "bill_update_lock:"; + + + public static final String COMPLETE_PAY_LOCK_KEY = "complete_pay_lock:"; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java new file mode 100644 index 0000000..82b912c --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java @@ -0,0 +1,165 @@ +package com.ruoyi.common.constant; + +import io.jsonwebtoken.Claims; + +/** + * 通用常量信息 + * + * @author ruoyi + */ +public class Constants +{ + /** + * UTF-8 字符集 + */ + public static final String UTF8 = "UTF-8"; + + /** + * GBK 字符集 + */ + public static final String GBK = "GBK"; + + /** + * www主域 + */ + public static final String WWW = "www."; + + /** + * http请求 + */ + public static final String HTTP = "http://"; + + /** + * https请求 + */ + public static final String HTTPS = "https://"; + + /** + * 通用成功标识 + */ + public static final String SUCCESS = "0"; + + /** + * 通用失败标识 + */ + public static final String FAIL = "1"; + + /** + * 登录成功 + */ + public static final String LOGIN_SUCCESS = "Success"; + + /** + * 注销 + */ + public static final String LOGOUT = "Logout"; + + /** + * 注册 + */ + public static final String REGISTER = "Register"; + + /** + * 登录失败 + */ + public static final String LOGIN_FAIL = "Error"; + + /** + * 验证码有效期(分钟) + */ + public static final Integer CAPTCHA_EXPIRATION = 2; + + /** + * 令牌 + */ + public static final String TOKEN = "token"; + + /** + * 令牌前缀 + */ + public static final String TOKEN_PREFIX = "Bearer "; + + /** + * 令牌前缀 + */ + public static final String LOGIN_USER_KEY = "login_user_key"; + /** + * 小程序 + */ + public static final String LOGIN_USER_APPLET_KEY = "login_user_applet_key"; + + /** + * 用户ID + */ + public static final String JWT_USERID = "userid"; + + /** + * 用户名称 + */ + public static final String JWT_USERNAME = Claims.SUBJECT; + + /** + * 用户头像 + */ + public static final String JWT_AVATAR = "avatar"; + + /** + * 创建时间 + */ + public static final String JWT_CREATED = "created"; + + /** + * 用户权限 + */ + public static final String JWT_AUTHORITIES = "authorities"; + + /** + * 资源映射路径 前缀 + */ + public static final String RESOURCE_PREFIX = "/profile"; + + /** + * RMI 远程方法调用 + */ + public static final String LOOKUP_RMI = "rmi:"; + + /** + * LDAP 远程方法调用 + */ + public static final String LOOKUP_LDAP = "ldap:"; + + /** + * LDAPS 远程方法调用 + */ + public static final String LOOKUP_LDAPS = "ldaps:"; + /** + * LDAPS 远程方法调用 + */ + public static final String INFORMATION_VIEW = "information_view:"; + + /** + * 自动识别json对象白名单配置(仅允许解析的包名,范围越小越安全) + */ + public static final String[] JSON_WHITELIST_STR = { "org.springframework", "com.ruoyi" }; + + /** + * 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加) + */ + public static final String[] JOB_WHITELIST_STR = { "com.ruoyi" }; + + /** + * 定时任务违规的字符 + */ + public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml", + "org.springframework", "org.apache", "com.ruoyi.common.utils.file", "com.ruoyi.common.config" }; + + /** + * 时间格式化 + */ + public static final String DATE_FORMATTER_TIME = "yyyy-MM-dd HH:mm:ss"; + + /** + * 用户类型 + */ + public static final String USER_TYPE = "用户类型"; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/DictConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/DictConstants.java new file mode 100644 index 0000000..95169c0 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/DictConstants.java @@ -0,0 +1,50 @@ +package com.ruoyi.common.constant; + +/** + * 缓存的key 常量 + * + * @author ruoyi + */ +public class DictConstants +{ + /** + * 租户属性 + */ + public static final String DICT_TYPE_TENANT_ATTRIBUTE = "t_tenant_attribute"; + /** + * 租户类型 + */ + public static final String DICT_TYPE_TENANT_TYPE = "t_tenant_type"; + /** + * 租赁状态 1=待出租 2=已出租 3=维修中 + */ + public static final String DICT_TYPE_LEASE_STATUS = "t_lease_status"; + /** + * 租金支付方式 1=月付 季付 年付 + */ + public static final String DICT_TYPE_CONTRACT_PAY_TYPE = "t_contract_pay_type"; + /** + * 业务属性 1住宅2商业3工业4车位5办公6仓储 + */ + public static final String DICT_TYPE_BUSINESS_ATTRIBUTES = "t_business_attributes"; + /** + * 合同状态 1=待提交 2=待审批 3=未签订 4=已签订 5=已驳回 6=已终止 7=待结算 8=已结算 9合同已签订待审 + */ + public static final String DICT_TYPE_CONTRACT_STATUS = "t_contract_status"; + /** + * 验收状态 1=待验收 2=已验收 + */ + public static final String DICT_TYPE_CHECK_STATUS = "t_check_status"; + /** + * 缴费状态 1=未缴费 2=待确认 3=已缴费 4=已逾期 5=已失效 + */ + public static final String DICT_TYPE_PAY_FEES_STATUS = "t_pay_fees_status"; + /** + * 账单类型 1=租金 2=押金 3=生活费用 4=房屋验收 + */ + public static final String DICT_TYPE_BILL_TYPE = "t_bill_type"; + /** + * 验收记录情况 1=良好 2=一般 3=较差 + */ + public static final String DICT_TYPE_CHECK_SITUATION = "t_check_situation"; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java new file mode 100644 index 0000000..7d899d4 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java @@ -0,0 +1,117 @@ +package com.ruoyi.common.constant; + +/** + * 代码生成通用常量 + * + * @author ruoyi + */ +public class GenConstants +{ + /** 单表(增删改查) */ + public static final String TPL_CRUD = "crud"; + + /** 树表(增删改查) */ + public static final String TPL_TREE = "tree"; + + /** 主子表(增删改查) */ + public static final String TPL_SUB = "sub"; + + /** 树编码字段 */ + public static final String TREE_CODE = "treeCode"; + + /** 树父编码字段 */ + public static final String TREE_PARENT_CODE = "treeParentCode"; + + /** 树名称字段 */ + public static final String TREE_NAME = "treeName"; + + /** 上级菜单ID字段 */ + public static final String PARENT_MENU_ID = "parentMenuId"; + + /** 上级菜单名称字段 */ + public static final String PARENT_MENU_NAME = "parentMenuName"; + + /** 数据库字符串类型 */ + public static final String[] COLUMNTYPE_STR = { "char", "varchar", "nvarchar", "varchar2" }; + + /** 数据库文本类型 */ + public static final String[] COLUMNTYPE_TEXT = { "tinytext", "text", "mediumtext", "longtext" }; + + /** 数据库时间类型 */ + public static final String[] COLUMNTYPE_TIME = { "datetime", "time", "date", "timestamp" }; + + /** 数据库数字类型 */ + public static final String[] COLUMNTYPE_NUMBER = { "tinyint", "smallint", "mediumint", "int", "number", "integer", + "bit", "bigint", "float", "double", "decimal" }; + + /** 页面不需要编辑字段 */ + public static final String[] COLUMNNAME_NOT_EDIT = { "id", "create_by", "create_time", "del_flag" }; + + /** 页面不需要显示的列表字段 */ + public static final String[] COLUMNNAME_NOT_LIST = { "id", "create_by", "create_time", "del_flag", "update_by", + "update_time" }; + + /** 页面不需要查询字段 */ + public static final String[] COLUMNNAME_NOT_QUERY = { "id", "create_by", "create_time", "del_flag", "update_by", + "update_time", "remark" }; + + /** Entity基类字段 */ + public static final String[] BASE_ENTITY = { "createBy", "createTime", "updateBy", "updateTime", "remark" }; + + /** Tree基类字段 */ + public static final String[] TREE_ENTITY = { "parentName", "parentId", "orderNum", "ancestors", "children" }; + + /** 文本框 */ + public static final String HTML_INPUT = "input"; + + /** 文本域 */ + public static final String HTML_TEXTAREA = "textarea"; + + /** 下拉框 */ + public static final String HTML_SELECT = "select"; + + /** 单选框 */ + public static final String HTML_RADIO = "radio"; + + /** 复选框 */ + public static final String HTML_CHECKBOX = "checkbox"; + + /** 日期控件 */ + public static final String HTML_DATETIME = "datetime"; + + /** 图片上传控件 */ + public static final String HTML_IMAGE_UPLOAD = "imageUpload"; + + /** 文件上传控件 */ + public static final String HTML_FILE_UPLOAD = "fileUpload"; + + /** 富文本控件 */ + public static final String HTML_EDITOR = "editor"; + + /** 字符串类型 */ + public static final String TYPE_STRING = "String"; + + /** 整型 */ + public static final String TYPE_INTEGER = "Integer"; + + /** 长整型 */ + public static final String TYPE_LONG = "Long"; + + /** 浮点型 */ + public static final String TYPE_DOUBLE = "Double"; + + /** 高精度计算类型 */ + public static final String TYPE_BIGDECIMAL = "BigDecimal"; + + /** 时间类型 */ + public static final String TYPE_DATE = "Date"; + + /** 模糊查询 */ + public static final String QUERY_LIKE = "LIKE"; + + /** 相等查询 */ + public static final String QUERY_EQ = "EQ"; + + /** 需要 */ + public static final String REQUIRE = "1"; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/HttpStatus.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/HttpStatus.java new file mode 100644 index 0000000..a983c77 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/HttpStatus.java @@ -0,0 +1,94 @@ +package com.ruoyi.common.constant; + +/** + * 返回状态码 + * + * @author ruoyi + */ +public class HttpStatus +{ + /** + * 操作成功 + */ + public static final int SUCCESS = 200; + + /** + * 对象创建成功 + */ + public static final int CREATED = 201; + + /** + * 请求已经被接受 + */ + public static final int ACCEPTED = 202; + + /** + * 操作已经执行成功,但是没有返回数据 + */ + public static final int NO_CONTENT = 204; + + /** + * 资源已被移除 + */ + public static final int MOVED_PERM = 301; + + /** + * 重定向 + */ + public static final int SEE_OTHER = 303; + + /** + * 资源没有被修改 + */ + public static final int NOT_MODIFIED = 304; + + /** + * 参数列表错误(缺少,格式不匹配) + */ + public static final int BAD_REQUEST = 400; + + /** + * 未授权 + */ + public static final int UNAUTHORIZED = 401; + + /** + * 访问受限,授权过期 + */ + public static final int FORBIDDEN = 403; + + /** + * 资源,服务未找到 + */ + public static final int NOT_FOUND = 404; + + /** + * 不允许的http方法 + */ + public static final int BAD_METHOD = 405; + + /** + * 资源冲突,或者资源被锁 + */ + public static final int CONFLICT = 409; + + /** + * 不支持的数据,媒体类型 + */ + public static final int UNSUPPORTED_TYPE = 415; + + /** + * 系统内部错误 + */ + public static final int ERROR = 500; + + /** + * 接口未实现 + */ + public static final int NOT_IMPLEMENTED = 501; + + /** + * 系统警告消息 + */ + public static final int WARN = 601; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java new file mode 100644 index 0000000..62ad815 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java @@ -0,0 +1,50 @@ +package com.ruoyi.common.constant; + +/** + * 任务调度通用常量 + * + * @author ruoyi + */ +public class ScheduleConstants +{ + public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME"; + + /** 执行目标key */ + public static final String TASK_PROPERTIES = "TASK_PROPERTIES"; + + /** 默认 */ + public static final String MISFIRE_DEFAULT = "0"; + + /** 立即触发执行 */ + public static final String MISFIRE_IGNORE_MISFIRES = "1"; + + /** 触发一次执行 */ + public static final String MISFIRE_FIRE_AND_PROCEED = "2"; + + /** 不触发立即执行 */ + public static final String MISFIRE_DO_NOTHING = "3"; + + public enum Status + { + /** + * 正常 + */ + NORMAL("0"), + /** + * 暂停 + */ + PAUSE("1"); + + private String value; + + private Status(String value) + { + this.value = value; + } + + public String getValue() + { + return value; + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java new file mode 100644 index 0000000..96b149c --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java @@ -0,0 +1,78 @@ +package com.ruoyi.common.constant; + +/** + * 用户常量信息 + * + * @author ruoyi + */ +public class UserConstants +{ + /** + * 平台内系统用户的唯一标志 + */ + public static final String SYS_USER = "SYS_USER"; + + /** 正常状态 */ + public static final String NORMAL = "0"; + + /** 异常状态 */ + public static final String EXCEPTION = "1"; + + /** 用户封禁状态 */ + public static final String USER_DISABLE = "1"; + + /** 角色封禁状态 */ + public static final String ROLE_DISABLE = "1"; + + /** 部门正常状态 */ + public static final String DEPT_NORMAL = "0"; + + /** 部门停用状态 */ + public static final String DEPT_DISABLE = "1"; + + /** 字典正常状态 */ + public static final String DICT_NORMAL = "0"; + + /** 是否为系统默认(是) */ + public static final String YES = "Y"; + + /** 是否菜单外链(是) */ + public static final String YES_FRAME = "0"; + + /** 是否菜单外链(否) */ + public static final String NO_FRAME = "1"; + + /** 菜单类型(目录) */ + public static final String TYPE_DIR = "M"; + + /** 菜单类型(菜单) */ + public static final String TYPE_MENU = "C"; + + /** 菜单类型(按钮) */ + public static final String TYPE_BUTTON = "F"; + + /** Layout组件标识 */ + public final static String LAYOUT = "Layout"; + + /** ParentView组件标识 */ + public final static String PARENT_VIEW = "ParentView"; + + /** InnerLink组件标识 */ + public final static String INNER_LINK = "InnerLink"; + + /** 校验是否唯一的返回标识 */ + public final static boolean UNIQUE = true; + public final static boolean NOT_UNIQUE = false; + + /** + * 用户名长度限制 + */ + public static final int USERNAME_MIN_LENGTH = 2; + public static final int USERNAME_MAX_LENGTH = 20; + + /** + * 密码长度限制 + */ + public static final int PASSWORD_MIN_LENGTH = 5; + public static final int PASSWORD_MAX_LENGTH = 20; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/WxConstant.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/WxConstant.java new file mode 100644 index 0000000..3e54fb9 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/WxConstant.java @@ -0,0 +1,22 @@ +package com.ruoyi.common.constant; + +/** + * @Description 对接微信小程序的常量 + * @Author xiaochen + * @Date 2021/10/19/01914:01 + */ +public class WxConstant { + + /** + * 生成小程序码地址 + */ + public static final String CREATE_CODE_URL = "https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode?access_token=ACCESS_TOKEN"; + + /** + * 高德地图坐标转换 + */ + public static final String ADDRESS_CONVERT_TO_COORDINATE = "https://restapi.amap.com/v3/geocode/geo?key=KEY&address=ADDRESS"; + + + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java new file mode 100644 index 0000000..7497d8c --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java @@ -0,0 +1,205 @@ +package com.ruoyi.common.core.controller; + +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +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; +import java.util.List; + +/** + * web层通用数据处理 + * + * @author ruoyi + */ +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)); + } + }); + } + + /** + * 设置请求分页数据 + */ +// protected void startPage() +// { +// PageUtils.startPage(); +// } + + /** + * 设置请求分页数据 + */ +// protected void startPage(Integer pageNum,Integer pageSize) +// { +// PageUtils.startPage(pageNum,pageSize); +// } + + /** + * 设置请求排序数据 + */ + protected void startOrderBy() + { +// PageDomain pageDomain = TableSupport.buildPageRequest(); +// if (StringUtils.isNotEmpty(pageDomain.getOrderBy())) +// { +// String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); +// PageHelper.orderBy(orderBy); +// } + } + + /** + * 清理分页的线程变量 + */ +// protected void clearPage() +// { +// PageUtils.clearPage(); +// } + + /** + * 响应请求分页数据 + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + protected TableDataInfo getDataTable(List<?> list) + { + TableDataInfo rspData = new TableDataInfo(); + rspData.setCode(HttpStatus.SUCCESS); + rspData.setMsg("查询成功"); + rspData.setRecords(list); +// rspData.setTotal(new PageInfo(list).getTotal()); + return rspData; + } + + /** + * 返回成功 + */ + public AjaxResult success() + { + return AjaxResult.success(); + } + + /** + * 返回失败消息 + */ + public AjaxResult error() + { + return AjaxResult.error(); + } + + /** + * 返回成功消息 + */ + public AjaxResult success(String message) + { + return AjaxResult.success(message); + } + + /** + * 返回成功消息 + */ + public AjaxResult success(Object data) + { + return AjaxResult.success(data); + } + + /** + * 返回失败消息 + */ + public AjaxResult error(String message) + { + return AjaxResult.error(message); + } + + /** + * 返回警告消息 + */ + public AjaxResult warn(String message) + { + return AjaxResult.warn(message); + } + + /** + * 响应返回结果 + * + * @param rows 影响行数 + * @return 操作结果 + */ + protected AjaxResult toAjax(int rows) + { + return rows > 0 ? AjaxResult.success() : AjaxResult.error(); + } + + /** + * 响应返回结果 + * + * @param result 结果 + * @return 操作结果 + */ + protected AjaxResult toAjax(boolean result) + { + return result ? success() : error(); + } + + /** + * 页面跳转 + */ + public String redirect(String url) + { + return StringUtils.format("redirect:{}", url); + } + + /** + * 获取用户缓存信息 + */ + public LoginUser getLoginUser() + { + return SecurityUtils.getLoginUser(); + } + + /** + * 获取登录用户id + */ + public Long getUserId() + { + return getLoginUser().getUserId(); + } + + /** + * 获取登录部门id + */ + public Long getDeptId() + { + return getLoginUser().getDeptId(); + } + + /** + * 获取登录用户名 + */ + public String getUsername() + { + return getLoginUser().getUsername(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/FileController.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/FileController.java new file mode 100644 index 0000000..c02d68a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/FileController.java @@ -0,0 +1,140 @@ +package com.ruoyi.common.core.controller; + +import com.ruoyi.common.config.FileUploadConfig; +import com.ruoyi.common.core.domain.AjaxResult; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.awt.*; +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * 文件上传控制类 + * + * @author junelee + * @date 2020/3/20 20:21 + */ +@Api(tags = "服务器文件上传") +@RestController +@CrossOrigin +@RequestMapping("/file/") +public class FileController { + + @Autowired + private FileUploadConfig fileUploadConfig; + + + + @ApiOperation(value = "单文件上传", notes = "单文件上传,rename 默认不重命名") + @PostMapping(value = "upload", headers = "content-type=multipart/form-data") + public AjaxResult uploadImageMany(@RequestParam(value = "file") MultipartFile mf) throws IOException { + if (mf.isEmpty()) { + return AjaxResult.error("请传入文件!"); + } + String TimeDir =new SimpleDateFormat("yyyy-MM-dd").format(new Date()); +// String realPath = fileUploadConfig.getLocation() + TimeDir; + String realPath = "C:\\Users\\Admin\\Desktop\\qrcode\\" + TimeDir; + File file = new File(realPath); + // 没有目录就创建 + if (!file.exists()) { + file.mkdirs(); + } + // 获取文件名称 + String filename = mf.getOriginalFilename(); + // 获取文件后缀 + String ext = filename.substring(filename.lastIndexOf("."), filename.length()); + // 检查文件类型 + if (!fileUploadConfig.getAllowExt().contains(ext)) { + return AjaxResult.error("上传文件格式不正确,仅支持" + fileUploadConfig.getAllowExt()); + } + File targetFile = new File(realPath, filename);//目标文件 + //开始从源文件拷贝到目标文件 + //传图片一步到位 + mf.transferTo(targetFile); + //拼接数据 +// String imgstr = fileUploadConfig.getAccessPath() + TimeDir +"\\"+ filename; + String imgstr = TimeDir +"/"+ filename; + return AjaxResult.success(imgstr); + } + + +// @ApiOperation(value = "单文件上传", notes = "单文件上传,rename 默认不重命名") +// @PostMapping(value = "strUpload", headers = "content-type=multipart/form-data") +// public String strUpload(@RequestParam(value = "file") MultipartFile mf,@RequestParam(value = "fileName")String fileName) throws IOException { +// if (mf.isEmpty()) { +// return "请传入文件!"; +// } +// String TimeDir =new SimpleDateFormat("yyyy-MM-dd").format(new Date()); +// String realPath = fileUploadConfig.getQrLocation() + TimeDir; +// File file = new File(realPath); +// // 没有目录就创建 +// if (!file.exists()) { +// file.mkdirs(); +// } +// File targetFile = new File(realPath, fileName);//目标文件 +// //开始从源文件拷贝到目标文件 +// //传图片一步到位 +// mf.transferTo(targetFile); +// //拼接数据 +// return fileUploadConfig.getQrLocation() + TimeDir +"\\"+ fileName; +// } + + @ApiOperation(value = "单文件上传", notes = "单文件上传,rename 默认不重命名") + @PostMapping(value = "strUpload", headers = "content-type=multipart/form-data") + public String strUpload(@RequestParam(value = "file") MultipartFile mf,@RequestParam(value = "fileName")String fileName) throws IOException { + if (mf.isEmpty()) { + return "请传入文件!"; + } + String TimeDir =new SimpleDateFormat("yyyy-MM-dd").format(new Date()); + String realPath = "C:\\Users\\Admin\\Desktop\\qrcode\\" + TimeDir; + File file = new File(realPath); + // 没有目录就创建 + if (!file.exists()) { + file.mkdirs(); + } + File targetFile = new File(realPath, fileName);//目标文件 + //开始从源文件拷贝到目标文件 + //传图片一步到位 + mf.transferTo(targetFile); + //拼接数据 + return TimeDir +"/"+ fileName; + } + + @ApiOperation(value = "单文件上传(覆盖服务器原文件)", notes = "单文件上传,rename 默认不重命名") + @PostMapping(value = "test/upload", headers = "content-type=multipart/form-data") + public AjaxResult uploadTest(@RequestParam(value = "file") MultipartFile mf) throws IOException { + if (mf.isEmpty()) { + return AjaxResult.error("请传入文件!"); + } + String TimeDir = new SimpleDateFormat("yyyy-MM-dd").format(new Date()); + String realPath = fileUploadConfig.getLocation() + "2021-11-17"; + File file = new File(realPath); + // 没有目录就创建 + if (!file.exists()) { + file.mkdirs(); + } + // 判断文件大小 + String filename = "6u2mGlqHkeE75e2ab51b4a03c6982ff7d68f4c024d43.jpg"; + // 获取文件后缀 + String ext = filename.substring(filename.lastIndexOf("."), filename.length()); + // 检查文件类型 + if (!fileUploadConfig.getAllowExt().contains(ext)) { + return AjaxResult.error("上传文件格式不正确,仅支持" + fileUploadConfig.getAllowExt()); + } + File targetFile = new File(realPath, filename);//目标文件 + //开始从源文件拷贝到目标文件 + //传图片一步到位 + mf.transferTo(targetFile); + //拼接数据 + String imgstr = fileUploadConfig.getAccessPath() + "2021-11-17" + "/" + filename; + return AjaxResult.success(imgstr); + } + + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/AjaxResult.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/AjaxResult.java new file mode 100644 index 0000000..908ea18 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/AjaxResult.java @@ -0,0 +1,216 @@ +package com.ruoyi.common.core.domain; + +import java.util.HashMap; +import java.util.Objects; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.utils.StringUtils; + +/** + * 操作消息提醒 + * + * @author ruoyi + */ +public class AjaxResult<T> extends HashMap<String, Object> +{ + private static final long serialVersionUID = 1L; + + /** 状态码 */ + public static final String CODE_TAG = "code"; + + /** 返回内容 */ + public static final String MSG_TAG = "msg"; + + /** 数据对象 */ + public static final String DATA_TAG = "data"; + + /** + * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。 + */ + public AjaxResult() + { + } + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param code 状态码 + * @param msg 返回内容 + */ + public AjaxResult(int code, String msg) + { + super.put(CODE_TAG, code); + super.put(MSG_TAG, msg); + } + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param code 状态码 + * @param msg 返回内容 + * @param data 数据对象 + */ + public AjaxResult(int code, String msg, Object data) + { + super.put(CODE_TAG, code); + super.put(MSG_TAG, msg); + if (StringUtils.isNotNull(data)) + { + super.put(DATA_TAG, data); + } + } + + /** + * 返回成功消息 + * + * @return 成功消息 + */ + public static AjaxResult success() + { + return AjaxResult.success("操作成功"); + } + + /** + * 返回成功数据 + * + * @return 成功消息 + */ + public static AjaxResult success(Object data) + { + return AjaxResult.success("操作成功", data); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @return 成功消息 + */ + public static AjaxResult success(String msg) + { + return AjaxResult.success(msg, null); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 成功消息 + */ + public static AjaxResult success(String msg, Object data) + { + return new AjaxResult(HttpStatus.SUCCESS, msg, data); + } + + /** + * 返回警告消息 + * + * @param msg 返回内容 + * @return 警告消息 + */ + public static AjaxResult warn(String msg) + { + return AjaxResult.warn(msg, null); + } + + /** + * 返回警告消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 警告消息 + */ + public static AjaxResult warn(String msg, Object data) + { + return new AjaxResult(HttpStatus.WARN, msg, data); + } + + /** + * 返回错误消息 + * + * @return 错误消息 + */ + public static AjaxResult error() + { + return AjaxResult.error("操作失败"); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @return 错误消息 + */ + public static AjaxResult error(String msg) + { + return AjaxResult.error(msg, null); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 错误消息 + */ + public static AjaxResult error(String msg, Object data) + { + return new AjaxResult(HttpStatus.ERROR, msg, data); + } + + /** + * 返回错误消息 + * + * @param code 状态码 + * @param msg 返回内容 + * @return 错误消息 + */ + public static AjaxResult error(int code, String msg) + { + return new AjaxResult(code, msg, null); + } + + /** + * 是否为成功消息 + * + * @return 结果 + */ + public boolean isSuccess() + { + return Objects.equals(HttpStatus.SUCCESS, this.get(CODE_TAG)); + } + + /** + * 是否为警告消息 + * + * @return 结果 + */ + public boolean isWarn() + { + return Objects.equals(HttpStatus.WARN, this.get(CODE_TAG)); + } + + /** + * 是否为错误消息 + * + * @return 结果 + */ + public boolean isError() + { + return Objects.equals(HttpStatus.ERROR, this.get(CODE_TAG)); + } + + /** + * 方便链式调用 + * + * @param key 键 + * @param value 值 + * @return 数据对象 + */ + @Override + public AjaxResult put(String key, Object value) + { + super.put(key, value); + return this; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java new file mode 100644 index 0000000..4b27433 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java @@ -0,0 +1,159 @@ +package com.ruoyi.common.core.domain; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.annotations.ApiModelProperty; + +/** + * Entity基类 + * + * @author ruoyi + */ +public class BaseEntity implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 搜索值 */ + @JsonIgnore + private String searchValue; + + /** 创建者 */ + 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; + + /** 备注 */ + private String remark; + + @ApiModelProperty(value = "禁用备注") + @TableField("disable_remark") + private String disableRemark; + + @ApiModelProperty(value = "操作时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @TableField("operating_time") + private LocalDateTime operatingTime; + + @ApiModelProperty(value = "操作人 登录名(登录账号)") + @TableField("operating_person") + private String operatingPerson; + + public String getDisableRemark() { + return disableRemark; + } + + public void setDisableRemark(String disableRemark) { + this.disableRemark = disableRemark; + } + + public LocalDateTime getOperatingTime() { + return operatingTime; + } + + public void setOperatingTime(LocalDateTime operatingTime) { + this.operatingTime = operatingTime; + } + + public String getOperatingPerson() { + return operatingPerson; + } + + public void setOperatingPerson(String operatingPerson) { + this.operatingPerson = operatingPerson; + } + + /** 请求参数 */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private Map<String, Object> params; + + public String getSearchValue() + { + return searchValue; + } + + public void setSearchValue(String searchValue) + { + this.searchValue = searchValue; + } + + public String getCreateBy() + { + return createBy; + } + + public void setCreateBy(String createBy) + { + this.createBy = createBy; + } + + public Date getCreateTime() + { + return createTime; + } + + public void setCreateTime(Date createTime) + { + this.createTime = createTime; + } + + public String getUpdateBy() + { + return updateBy; + } + + public void setUpdateBy(String updateBy) + { + this.updateBy = updateBy; + } + + public Date getUpdateTime() + { + return updateTime; + } + + public void setUpdateTime(Date updateTime) + { + this.updateTime = updateTime; + } + + public String getRemark() + { + return remark; + } + + public void setRemark(String remark) + { + this.remark = remark; + } + + public Map<String, Object> getParams() + { + if (params == null) + { + params = new HashMap<>(); + } + return params; + } + + public void setParams(Map<String, Object> params) + { + this.params = params; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseModel.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseModel.java new file mode 100644 index 0000000..94db5c6 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseModel.java @@ -0,0 +1,107 @@ +package com.ruoyi.common.core.domain; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * @author xiaochen + * @ClassName BaseModel + * @Description + * @date 2024-04-02 19:38 + */ +@Data +public class BaseModel implements Serializable { + /** + * 字段常量属性 + */ + public static final String ID = "id"; + + public static final String CREATE_TIME = "create_time"; + + public static final String LAST_TIME = "last_time"; + + private static final long serialVersionUID = 2553749188490103197L; + /** + * 新增执行 + */ + @ApiModelProperty(value = "记录创建人,前端忽略") + @JsonIgnore + @TableField(value = "create_by", fill = FieldFill.INSERT) + private String createBy; + + /** + * 新增和更新执行 + */ + @ApiModelProperty(value = "记录修改人,前端忽略") + @TableField(value = "update_by", fill = FieldFill.INSERT_UPDATE) + private String updateBy; + /** + * 删除 未删除 + */ + @JsonIgnore + @TableField("`disabled`") + @TableLogic + private Boolean disabled; + + @ApiModelProperty(value = "记录创建时间,前端忽略") + @TableField("create_time") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime createTime; + + /** + * 最后修改时间 + */ + @ApiModelProperty(value = "记录修改时间,前端忽略") + @TableField("update_time") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private LocalDateTime updateTime; + + + public String getCreateBy() { + return createBy; + } + + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public String getUpdateBy() { + return updateBy; + } + + public void setUpdateBy(String updateBy) { + this.updateBy = updateBy; + } + + public Boolean getDisabled() { + return disabled; + } + + public void setDisabled(Boolean disabled) { + this.disabled = disabled; + } + + public LocalDateTime getCreateTime() { + return createTime; + } + + public void setCreateTime(LocalDateTime createTime) { + this.createTime = createTime; + } + + public LocalDateTime getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(LocalDateTime updateTime) { + this.updateTime = updateTime; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BasePage.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BasePage.java new file mode 100644 index 0000000..b7d844c --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BasePage.java @@ -0,0 +1,50 @@ +package com.ruoyi.common.core.domain; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import java.io.Serializable; + +/** + * <p></p> + * + * @author mouseyCat + * @date 2020/8/24 16:39 + */ +@ApiModel(value = "基础查询列表dto") +public class BasePage implements Serializable { + /** + * 分页参数,当前页码 + */ + @ApiModelProperty(value = "分页参数,当前页码") + private Integer pageNum = 1; + /** + * 分页参数,每页数量 + */ + @ApiModelProperty(value = "分页参数,每页数量,默认为10") + private Integer pageSize = 10; + + public Integer getPageNum() { + return pageNum; + } + + public void setPageNum(Integer pageNum) { + this.pageNum = pageNum; + } + + public Integer getPageSize() { + return pageSize; + } + + public void setPageSize(Integer pageSize) { + this.pageSize = pageSize; + } + + @Override + public String toString() { + return "BasePage{" + + "pageNum=" + pageNum + + ", pageSize=" + pageSize + + '}'; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/R.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/R.java new file mode 100644 index 0000000..ef15802 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/R.java @@ -0,0 +1,115 @@ +package com.ruoyi.common.core.domain; + +import java.io.Serializable; +import com.ruoyi.common.constant.HttpStatus; + +/** + * 响应信息主体 + * + * @author ruoyi + */ +public class R<T> implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 成功 */ + public static final int SUCCESS = HttpStatus.SUCCESS; + + /** 失败 */ + public static final int FAIL = HttpStatus.ERROR; + + private int code; + + private String msg; + + private T data; + + public static <T> R<T> ok() + { + return restResult(null, SUCCESS, "操作成功"); + } + + public static <T> R<T> ok(T data) + { + return restResult(data, SUCCESS, "操作成功"); + } + + public static <T> R<T> ok(T data, String msg) + { + return restResult(data, SUCCESS, msg); + } + + public static <T> R<T> fail() + { + return restResult(null, FAIL, "操作失败"); + } + + public static <T> R<T> fail(String msg) + { + return restResult(null, FAIL, msg); + } + + public static <T> R<T> fail(T data) + { + return restResult(data, FAIL, "操作失败"); + } + + public static <T> R<T> fail(T data, String msg) + { + return restResult(data, FAIL, msg); + } + + public static <T> R<T> fail(int code, String msg) + { + return restResult(null, code, msg); + } + + private static <T> R<T> restResult(T data, int code, String msg) + { + R<T> apiResult = new R<>(); + apiResult.setCode(code); + apiResult.setData(data); + apiResult.setMsg(msg); + return apiResult; + } + + public int getCode() + { + return code; + } + + public void setCode(int code) + { + this.code = code; + } + + public String getMsg() + { + return msg; + } + + public void setMsg(String msg) + { + this.msg = msg; + } + + public T getData() + { + return data; + } + + public void setData(T data) + { + this.data = data; + } + + public static <T> Boolean isError(R<T> ret) + { + return !isSuccess(ret); + } + + public static <T> Boolean isSuccess(R<T> ret) + { + return R.SUCCESS == ret.getCode(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java new file mode 100644 index 0000000..a180a18 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java @@ -0,0 +1,79 @@ +package com.ruoyi.common.core.domain; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tree基类 + * + * @author ruoyi + */ +public class TreeEntity extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 父菜单名称 */ + private String parentName; + + /** 父菜单ID */ + private Long parentId; + + /** 显示顺序 */ + private Integer orderNum; + + /** 祖级列表 */ + private String ancestors; + + /** 子部门 */ + private List<?> children = new ArrayList<>(); + + public String getParentName() + { + return parentName; + } + + public void setParentName(String parentName) + { + this.parentName = parentName; + } + + public Long getParentId() + { + return parentId; + } + + public void setParentId(Long parentId) + { + this.parentId = parentId; + } + + public Integer getOrderNum() + { + return orderNum; + } + + public void setOrderNum(Integer orderNum) + { + this.orderNum = orderNum; + } + + public String getAncestors() + { + return ancestors; + } + + public void setAncestors(String ancestors) + { + this.ancestors = ancestors; + } + + public List<?> getChildren() + { + return children; + } + + public void setChildren(List<?> children) + { + this.children = children; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java new file mode 100644 index 0000000..bd835db --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java @@ -0,0 +1,77 @@ +package com.ruoyi.common.core.domain; + +import java.io.Serializable; +import java.util.List; +import java.util.stream.Collectors; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.common.core.domain.entity.SysMenu; + +/** + * Treeselect树结构实体类 + * + * @author ruoyi + */ +public class TreeSelect implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 节点ID */ + private Long id; + + /** 节点名称 */ + private String label; + + /** 子节点 */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private List<TreeSelect> children; + + public TreeSelect() + { + + } + + public TreeSelect(SysDept dept) + { + this.id = dept.getDeptId(); + this.label = dept.getDeptName(); + this.children = dept.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + public TreeSelect(SysMenu menu) + { + this.id = menu.getMenuId(); + this.label = menu.getMenuName(); + this.children = menu.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + public Long getId() + { + return id; + } + + public void setId(Long id) + { + this.id = id; + } + + public String getLabel() + { + return label; + } + + public void setLabel(String label) + { + this.label = label; + } + + public List<TreeSelect> getChildren() + { + return children; + } + + public void setChildren(List<TreeSelect> children) + { + this.children = children; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java new file mode 100644 index 0000000..fb18c5c --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java @@ -0,0 +1,203 @@ +package com.ruoyi.common.core.domain.entity; + +import java.util.ArrayList; +import java.util.List; +import javax.validation.constraints.Email; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 部门表 sys_dept + * + * @author ruoyi + */ +public class SysDept extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 部门ID */ + private Long deptId; + + /** 父部门ID */ + private Long parentId; + + /** 祖级列表 */ + private String ancestors; + + /** 部门名称 */ + private String deptName; + + /** 显示顺序 */ + private Integer orderNum; + + /** 负责人 */ + private String leader; + + /** 联系电话 */ + private String phone; + + /** 邮箱 */ + private String email; + + /** 部门状态:0正常,1停用 */ + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + private String delFlag; + + /** 父部门名称 */ + private String parentName; + + /** 子部门 */ + private List<SysDept> children = new ArrayList<SysDept>(); + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + public Long getParentId() + { + return parentId; + } + + public void setParentId(Long parentId) + { + this.parentId = parentId; + } + + public String getAncestors() + { + return ancestors; + } + + public void setAncestors(String ancestors) + { + this.ancestors = ancestors; + } + + @NotBlank(message = "部门名称不能为空") + @Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符") + public String getDeptName() + { + return deptName; + } + + public void setDeptName(String deptName) + { + this.deptName = deptName; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getOrderNum() + { + return orderNum; + } + + public void setOrderNum(Integer orderNum) + { + this.orderNum = orderNum; + } + + public String getLeader() + { + return leader; + } + + public void setLeader(String leader) + { + this.leader = leader; + } + + @Size(min = 0, max = 11, message = "联系电话长度不能超过11个字符") + public String getPhone() + { + return phone; + } + + public void setPhone(String phone) + { + this.phone = phone; + } + + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") + public String getEmail() + { + return email; + } + + public void setEmail(String email) + { + this.email = email; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getDelFlag() + { + return delFlag; + } + + public void setDelFlag(String delFlag) + { + this.delFlag = delFlag; + } + + public String getParentName() + { + return parentName; + } + + public void setParentName(String parentName) + { + this.parentName = parentName; + } + + public List<SysDept> getChildren() + { + return children; + } + + public void setChildren(List<SysDept> children) + { + this.children = children; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("deptId", getDeptId()) + .append("parentId", getParentId()) + .append("ancestors", getAncestors()) + .append("deptName", getDeptName()) + .append("orderNum", getOrderNum()) + .append("leader", getLeader()) + .append("phone", getPhone()) + .append("email", getEmail()) + .append("status", getStatus()) + .append("delFlag", getDelFlag()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java new file mode 100644 index 0000000..9e921ff --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java @@ -0,0 +1,174 @@ +package com.ruoyi.common.core.domain.entity; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 字典数据表 sys_dict_data + * + * @author ruoyi + */ +public class SysDictData extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 字典编码 */ + //@Excel(name = "字典编码", cellType = ColumnType.NUMERIC) + private Long dictCode; + + /** 字典排序 */ + //@Excel(name = "字典排序", cellType = ColumnType.NUMERIC) + private Long dictSort; + + /** 字典标签 */ + //@Excel(name = "字典标签") + private String dictLabel; + + /** 字典键值 */ + //@Excel(name = "字典键值") + private String dictValue; + + /** 字典类型 */ + //@Excel(name = "字典类型") + private String dictType; + + /** 样式属性(其他样式扩展) */ + private String cssClass; + + /** 表格字典样式 */ + private String listClass; + + /** 是否默认(Y是 N否) */ + //@Excel(name = "是否默认", readConverterExp = "Y=是,N=否") + private String isDefault; + + /** 状态(0正常 1停用) */ + //@Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private String status; + + public Long getDictCode() + { + return dictCode; + } + + public void setDictCode(Long dictCode) + { + this.dictCode = dictCode; + } + + public Long getDictSort() + { + return dictSort; + } + + public void setDictSort(Long dictSort) + { + this.dictSort = dictSort; + } + + @NotBlank(message = "字典标签不能为空") + @Size(min = 0, max = 100, message = "字典标签长度不能超过100个字符") + public String getDictLabel() + { + return dictLabel; + } + + public void setDictLabel(String dictLabel) + { + this.dictLabel = dictLabel; + } + + @NotBlank(message = "字典键值不能为空") + @Size(min = 0, max = 100, message = "字典键值长度不能超过100个字符") + public String getDictValue() + { + return dictValue; + } + + public void setDictValue(String dictValue) + { + this.dictValue = dictValue; + } + + @NotBlank(message = "字典类型不能为空") + @Size(min = 0, max = 100, message = "字典类型长度不能超过100个字符") + public String getDictType() + { + return dictType; + } + + public void setDictType(String dictType) + { + this.dictType = dictType; + } + + @Size(min = 0, max = 100, message = "样式属性长度不能超过100个字符") + public String getCssClass() + { + return cssClass; + } + + public void setCssClass(String cssClass) + { + this.cssClass = cssClass; + } + + public String getListClass() + { + return listClass; + } + + public void setListClass(String listClass) + { + this.listClass = listClass; + } + + public boolean getDefault() + { + return UserConstants.YES.equals(this.isDefault); + } + + public String getIsDefault() + { + return isDefault; + } + + public void setIsDefault(String isDefault) + { + this.isDefault = isDefault; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("dictCode", getDictCode()) + .append("dictSort", getDictSort()) + .append("dictLabel", getDictLabel()) + .append("dictValue", getDictValue()) + .append("dictType", getDictType()) + .append("cssClass", getCssClass()) + .append("listClass", getListClass()) + .append("isDefault", getIsDefault()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java new file mode 100644 index 0000000..03504a7 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java @@ -0,0 +1,94 @@ +package com.ruoyi.common.core.domain.entity; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 字典类型表 sys_dict_type + * + * @author ruoyi + */ +public class SysDictType extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 字典主键 */ + //@Excel(name = "字典主键", cellType = ColumnType.NUMERIC) + private Long dictId; + + /** 字典名称 */ + //@Excel(name = "字典名称") + private String dictName; + + /** 字典类型 */ + //@Excel(name = "字典类型") + private String dictType; + + /** 状态(0正常 1停用) */ + //@Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private String status; + + public Long getDictId() + { + return dictId; + } + + public void setDictId(Long dictId) + { + this.dictId = dictId; + } + + @NotBlank(message = "字典名称不能为空") + @Size(min = 0, max = 100, message = "字典类型名称长度不能超过100个字符") + public String getDictName() + { + return dictName; + } + + public void setDictName(String dictName) + { + this.dictName = dictName; + } + + @NotBlank(message = "字典类型不能为空") + @Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符") + @Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)") + public String getDictType() + { + return dictType; + } + + public void setDictType(String dictType) + { + this.dictType = dictType; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("dictId", getDictId()) + .append("dictName", getDictName()) + .append("dictType", getDictType()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java new file mode 100644 index 0000000..94e1a18 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java @@ -0,0 +1,259 @@ +package com.ruoyi.common.core.domain.entity; + +import java.util.ArrayList; +import java.util.List; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 菜单权限表 sys_menu + * + * @author ruoyi + */ +public class SysMenu extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 菜单ID */ + private Long menuId; + + /** 菜单名称 */ + private String menuName; + + /** 父菜单名称 */ + private String parentName; + + /** 父菜单ID */ + private Long parentId; + + /** 显示顺序 */ + private Integer orderNum; + + /** 路由地址 */ + private String path; + + /** 组件路径 */ + private String component; + + /** 路由参数 */ + private String query; + + /** 是否为外链(0是 1否) */ + private String isFrame; + + /** 是否缓存(0缓存 1不缓存) */ + private String isCache; + + /** 类型(M目录 C菜单 F按钮) */ + private String menuType; + + /** 显示状态(0显示 1隐藏) */ + private String visible; + + /** 菜单状态(0正常 1停用) */ + private String status; + + /** 权限字符串 */ + private String perms; + + /** 菜单图标 */ + private String icon; + + /** 子菜单 */ + private List<SysMenu> children = new ArrayList<SysMenu>(); + + public Long getMenuId() + { + return menuId; + } + + public void setMenuId(Long menuId) + { + this.menuId = menuId; + } + + @NotBlank(message = "菜单名称不能为空") + @Size(min = 0, max = 50, message = "菜单名称长度不能超过50个字符") + public String getMenuName() + { + return menuName; + } + + public void setMenuName(String menuName) + { + this.menuName = menuName; + } + + public String getParentName() + { + return parentName; + } + + public void setParentName(String parentName) + { + this.parentName = parentName; + } + + public Long getParentId() + { + return parentId; + } + + public void setParentId(Long parentId) + { + this.parentId = parentId; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getOrderNum() + { + return orderNum; + } + + public void setOrderNum(Integer orderNum) + { + this.orderNum = orderNum; + } + + @Size(min = 0, max = 200, message = "路由地址不能超过200个字符") + public String getPath() + { + return path; + } + + public void setPath(String path) + { + this.path = path; + } + + @Size(min = 0, max = 200, message = "组件路径不能超过255个字符") + public String getComponent() + { + return component; + } + + public void setComponent(String component) + { + this.component = component; + } + + public String getQuery() + { + return query; + } + + public void setQuery(String query) + { + this.query = query; + } + + public String getIsFrame() + { + return isFrame; + } + + public void setIsFrame(String isFrame) + { + this.isFrame = isFrame; + } + + public String getIsCache() + { + return isCache; + } + + public void setIsCache(String isCache) + { + this.isCache = isCache; + } + + @NotBlank(message = "菜单类型不能为空") + public String getMenuType() + { + return menuType; + } + + public void setMenuType(String menuType) + { + this.menuType = menuType; + } + + public String getVisible() + { + return visible; + } + + public void setVisible(String visible) + { + this.visible = visible; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + @Size(min = 0, max = 100, message = "权限标识长度不能超过100个字符") + public String getPerms() + { + return perms; + } + + public void setPerms(String perms) + { + this.perms = perms; + } + + public String getIcon() + { + return icon; + } + + public void setIcon(String icon) + { + this.icon = icon; + } + + public List<SysMenu> getChildren() + { + return children; + } + + public void setChildren(List<SysMenu> children) + { + this.children = children; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("menuId", getMenuId()) + .append("menuName", getMenuName()) + .append("parentId", getParentId()) + .append("orderNum", getOrderNum()) + .append("path", getPath()) + .append("component", getComponent()) + .append("isFrame", getIsFrame()) + .append("IsCache", getIsCache()) + .append("menuType", getMenuType()) + .append("visible", getVisible()) + .append("status ", getStatus()) + .append("perms", getPerms()) + .append("icon", getIcon()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java new file mode 100644 index 0000000..b889f27 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java @@ -0,0 +1,279 @@ +package com.ruoyi.common.core.domain.entity; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.ruoyi.common.core.domain.BaseModel; +import lombok.Data; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import java.util.Set; + +/** + * 角色表 sys_role + * + * @author ruoyi + */ +@Data +public class SysRole extends BaseModel +{ + private static final long serialVersionUID = 1L; + + /** 角色ID */ + //@Excel(name = "角色序号", cellType = ColumnType.NUMERIC) + private Long roleId; + + /** 角色名称 */ + //@Excel(name = "角色名称") + private String roleName; + + /** 角色权限 */ + //@Excel(name = "角色权限") + private String roleKey; + + /** 角色排序 */ + //@Excel(name = "角色排序") + private Integer roleSort; + + /** 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) */ + //@Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限") + private String dataScope; + + /** 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) */ + private boolean menuCheckStrictly; + + /** 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ) */ + private boolean deptCheckStrictly; + + /** 角色状态(0正常 1停用) */ + //@Excel(name = "角色状态", readConverterExp = "0=正常,1=停用") + private Integer status; + + /** 删除标志(0代表存在 2代表删除) */ + private Integer delFlag; + + /** 用户是否存在此角色标识 默认不存在 */ + private boolean flag = false; + + /** 菜单组 */ + private Long[] menuIds; + + /** 部门组(数据权限) */ + private Long[] deptIds; + + /** 角色菜单权限 */ + private Set<String> permissions; + + private String remark; + + /** + * 删除天数 + */ + private Integer removeDays; + /** + * 岗位类型 1=经理 2=负责人 3=专员 + */ + private Integer postType; + /** + * 角色人数 + */ + @TableField(exist = false) + private Integer userCount; + + public Integer getPostType() { + return postType; + } + + public void setPostType(Integer postType) { + this.postType = postType; + } + + public SysRole() + { + + } + + public SysRole(Long roleId) + { + this.roleId = roleId; + } + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + public boolean isAdmin() + { + return isAdmin(this.roleId); + } + + public static boolean isAdmin(Long roleId) + { + return roleId != null && 1L == roleId; + } + + @NotBlank(message = "角色名称不能为空") + @Size(min = 0, max = 30, message = "角色名称长度不能超过30个字符") + public String getRoleName() + { + return roleName; + } + + public void setRoleName(String roleName) + { + this.roleName = roleName; + } + + @Size(min = 0, max = 100, message = "权限字符长度不能超过100个字符") + public String getRoleKey() + { + return roleKey; + } + + public void setRoleKey(String roleKey) + { + this.roleKey = roleKey; + } + + public Integer getRoleSort() + { + return roleSort; + } + + public void setRoleSort(Integer roleSort) + { + this.roleSort = roleSort; + } + + public String getDataScope() + { + return dataScope; + } + + public void setDataScope(String dataScope) + { + this.dataScope = dataScope; + } + + public boolean isMenuCheckStrictly() + { + return menuCheckStrictly; + } + + public void setMenuCheckStrictly(boolean menuCheckStrictly) + { + this.menuCheckStrictly = menuCheckStrictly; + } + + public boolean isDeptCheckStrictly() + { + return deptCheckStrictly; + } + + public void setDeptCheckStrictly(boolean deptCheckStrictly) + { + this.deptCheckStrictly = deptCheckStrictly; + } + + public Integer getStatus() + { + return status; + } + + public void setStatus(Integer status) + { + this.status = status; + } + + public Integer getDelFlag() + { + return delFlag; + } + + public void setDelFlag(Integer delFlag) + { + this.delFlag = delFlag; + } + + public boolean isFlag() + { + return flag; + } + + public void setFlag(boolean flag) + { + this.flag = flag; + } + + public Long[] getMenuIds() + { + return menuIds; + } + + public void setMenuIds(Long[] menuIds) + { + this.menuIds = menuIds; + } + + public Long[] getDeptIds() + { + return deptIds; + } + + public void setDeptIds(Long[] deptIds) + { + this.deptIds = deptIds; + } + + public Set<String> getPermissions() + { + return permissions; + } + + public void setPermissions(Set<String> permissions) + { + this.permissions = permissions; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + public Integer getRemoveDays() { + return removeDays; + } + + public void setRemoveDays(Integer removeDays) { + this.removeDays = removeDays; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("roleId", getRoleId()) + .append("roleName", getRoleName()) + .append("roleKey", getRoleKey()) + .append("roleSort", getRoleSort()) + .append("dataScope", getDataScope()) + .append("menuCheckStrictly", isMenuCheckStrictly()) + .append("deptCheckStrictly", isDeptCheckStrictly()) + .append("status", getStatus()) + .append("delFlag", getDelFlag()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java new file mode 100644 index 0000000..9a6eea2 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java @@ -0,0 +1,389 @@ +package com.ruoyi.common.core.domain.entity; + +import java.util.Date; +import java.util.List; +import javax.validation.constraints.*; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.xss.Xss; + +/** + * 用户对象 sys_user + * + * @author ruoyi + */ +@Data +public class SysUser extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 用户ID */ + //@Excel(name = "用户序号", cellType = ColumnType.NUMERIC, prompt = "用户编号") + @ApiModelProperty(value = "用户id") + @TableField("user_id") + private Long userId; + + /** 部门ID */ + //@Excel(name = "部门编号", type = Type.IMPORT) + @ApiModelProperty(value = "部门id") + private Long deptId; + + /** 用户账号 */ + //@Excel(name = "登录名称") + @ApiModelProperty(value = "登录名称") + private String userName; + + /** 用户昵称 */ + //@Excel(name = "用户名称") + @ApiModelProperty(value = "用户名称") + private String nickName; + + /** 用户邮箱 */ + //@Excel(name = "用户邮箱") + @ApiModelProperty(value = "用户邮箱") + private String email; + + /** 手机号码 */ + //@Excel(name = "手机号码") + @ApiModelProperty(value = "手机号码") + private String phonenumber; + + /** 用户性别 */ + //@Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知") + @ApiModelProperty(value = "用户性别 0=男,1=女,2=未知") + private String sex; + + /** 用户头像 */ + @ApiModelProperty(value = "用户头像") + private String avatar; + + /** 密码 */ + @ApiModelProperty(value = "密码") + private String password; + + /** 帐号状态(0正常 1停用) */ + //@Excel(name = "帐号状态", readConverterExp = "0=正常,1=停用") + @ApiModelProperty(value = "帐号状态 0=正常,1=停用") + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + @ApiModelProperty(value = "删除标志(0代表存在 2代表删除)") + private String delFlag; + + /** 最后登录IP */ + //@Excel(name = "最后登录IP", type = Type.EXPORT) + @ApiModelProperty(value = "最后登录IP") + private String loginIp; + + /** 最后登录时间 */ + //@Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "最后登录时间") + private Date loginDate; + + /** 部门对象 */ + //@Excels({ + //@Excel(name = "部门名称", targetAttr = "deptName", type = Type.EXPORT), + //@Excel(name = "部门负责人", targetAttr = "leader", type = Type.EXPORT) +// }) + @ApiModelProperty(value = "部门对象") + private SysDept dept; + + /** 角色对象 */ + @ApiModelProperty(value = "角色对象") + private List<SysRole> roles; + + /** 角色组 */ + @ApiModelProperty(value = "角色组") + private Long[] roleIds; + + /** 岗位组 */ + @ApiModelProperty(value = "岗位组") + private Long[] postIds; + + /** 角色ID */ + @ApiModelProperty(value = "角色ID") + private Long roleId; + /** + * 是否为黑名单 1是 0否 + */ + @ApiModelProperty(value = "是否为黑名单 1是 0否") + private Integer ifBlack; + + /** + * 区县id + */ + @ApiModelProperty(value = "区县id") + private Integer districtId; + + @TableField(exist = false) + private String roleName; + @TableField(exist = false) + private String deptName; + @ApiModelProperty(value = "部门id集合") + @TableField(exist = false) + private List<String> deptIds; + + public String getRoleName() { + return roleName; + } + + public void setRoleName(String roleName) { + this.roleName = roleName; + } + + public Integer getDistrictId() { + return districtId; + } + + public void setDistrictId(Integer districtId) { + this.districtId = districtId; + } + + public SysUser() + { + + } + + public SysUser(Long userId) + { + this.userId = userId; + } + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public boolean isAdmin() + { + return isAdmin(this.userId); + } + + public static boolean isAdmin(Long userId) + { + return userId != null && 1L == userId; + } + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + @Xss(message = "用户昵称不能包含脚本字符") + @Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符") + public String getNickName() + { + return nickName; + } + + public void setNickName(String nickName) + { + this.nickName = nickName; + } + + @Xss(message = "用户账号不能包含脚本字符") + @Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符") + public String getUserName() + { + return userName; + } + + public void setUserName(String userName) + { + this.userName = userName; + } + + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") + public String getEmail() + { + return email; + } + + public void setEmail(String email) + { + this.email = email; + } + + @Size(min = 0, max = 11, message = "手机号码长度不能超过11个字符") + public String getPhonenumber() + { + return phonenumber; + } + + public void setPhonenumber(String phonenumber) + { + this.phonenumber = phonenumber; + } + + public String getSex() + { + return sex; + } + + public void setSex(String sex) + { + this.sex = sex; + } + + public String getAvatar() + { + return avatar; + } + + public void setAvatar(String avatar) + { + this.avatar = avatar; + } + + public String getPassword() + { + return password; + } + + public void setPassword(String password) + { + this.password = password; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getDelFlag() + { + return delFlag; + } + + public void setDelFlag(String delFlag) + { + this.delFlag = delFlag; + } + + public String getLoginIp() + { + return loginIp; + } + + public void setLoginIp(String loginIp) + { + this.loginIp = loginIp; + } + + public Date getLoginDate() + { + return loginDate; + } + + public void setLoginDate(Date loginDate) + { + this.loginDate = loginDate; + } + + public SysDept getDept() + { + return dept; + } + + public void setDept(SysDept dept) + { + this.dept = dept; + } + + public List<SysRole> getRoles() + { + return roles; + } + + public void setRoles(List<SysRole> roles) + { + this.roles = roles; + } + + public Long[] getRoleIds() + { + return roleIds; + } + + public void setRoleIds(Long[] roleIds) + { + this.roleIds = roleIds; + } + + public Long[] getPostIds() + { + return postIds; + } + + public void setPostIds(Long[] postIds) + { + this.postIds = postIds; + } + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + public Integer getIfBlack() { + return ifBlack; + } + + public void setIfBlack(Integer ifBlack) { + this.ifBlack = ifBlack; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("userId", getUserId()) + .append("deptId", getDeptId()) + .append("userName", getUserName()) + .append("nickName", getNickName()) + .append("email", getEmail()) + .append("phonenumber", getPhonenumber()) + .append("sex", getSex()) + .append("avatar", getAvatar()) + .append("password", getPassword()) + .append("status", getStatus()) + .append("delFlag", getDelFlag()) + .append("loginIp", getLoginIp()) + .append("loginDate", getLoginDate()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .append("dept", getDept()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/TTenantResp.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/TTenantResp.java new file mode 100644 index 0000000..e20907f --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/TTenantResp.java @@ -0,0 +1,69 @@ +package com.ruoyi.common.core.domain.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.core.domain.BaseModel; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.validation.constraints.NotBlank; +import java.time.LocalDate; + +/** + * <p> + * 租户 + * </p> + * + * @author xiaochen + * @since 2025-01-20 + */ +@Data +@ApiModel(value="TTenant对象", description="登录返回") +public class TTenantResp { + + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + + @ApiModelProperty(value = "住户名称") + private String residentName; + + @ApiModelProperty(value = "入住时间") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private LocalDate checkinTime; + + @ApiModelProperty(value = "租户属性") + private String tenantAttributes; + + @ApiModelProperty(value = "租户类型") + private String tenantType; + + @ApiModelProperty(value = "联系电话") + private String phone; + + @ApiModelProperty(value = "证件号码") + private String idCard; + + @ApiModelProperty(value = "邮箱") + private String email; + + @ApiModelProperty(value = "银行转账专号") + private String bankNumber; + + @ApiModelProperty(value = "通讯地址") + private String mailAddress; + + @ApiModelProperty(value = "登录账号") + private String account; + + @ApiModelProperty(value = "登录密码") + private String password; + @ApiModelProperty(value = "微信openid") + private String openId; + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java new file mode 100644 index 0000000..842f604 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java @@ -0,0 +1,37 @@ +package com.ruoyi.common.core.domain.model; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 用户登录对象 + * + * @author ruoyi + */ +@ApiModel(value = "登录对象Body") +@Data +public class LoginBody +{ + /** + * 用户名 + */ + @ApiModelProperty(value = "手机号",notes = "手机号") + @NotBlank(message = "手机号不能为空") + private String phone; + + /** + * 验证码 + */ + @ApiModelProperty(value = "验证码") + @NotBlank(message = "验证码不能为空") + private String code; + + + @ApiModelProperty(value = "邀请人用户id") + private String inviteId; + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java new file mode 100644 index 0000000..670e6b3 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java @@ -0,0 +1,266 @@ +package com.ruoyi.common.core.domain.model; + +import com.alibaba.fastjson2.annotation.JSONField; +import com.ruoyi.common.core.domain.entity.SysUser; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import java.util.Collection; +import java.util.Set; + +/** + * 登录用户身份权限 + * + * @author ruoyi + */ +public class LoginUser implements UserDetails +{ + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + private Long userId; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 用户唯一标识 + */ + private String token; + + /** + * 登录时间 + */ + private Long loginTime; + + /** + * 过期时间 + */ + private Long expireTime; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 权限列表 + */ + private Set<String> permissions; + + /** + * 用户信息 + */ + private SysUser user; + + public LoginUser() + { + } + + public LoginUser(SysUser user, Set<String> permissions) + { + this.user = user; + this.permissions = permissions; + } + + public LoginUser(Long userId, Long deptId, SysUser user, Set<String> permissions) + { + this.userId = userId; + this.deptId = deptId; + this.user = user; + this.permissions = permissions; + } + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + public String getToken() + { + return token; + } + + public void setToken(String token) + { + this.token = token; + } + + @JSONField(serialize = false) + @Override + public String getPassword() + { + return user.getPassword(); + } + + @Override + public String getUsername() + { + return user.getUserName(); + } + + /** + * 账户是否未过期,过期无法验证 + */ + @JSONField(serialize = false) + @Override + public boolean isAccountNonExpired() + { + return true; + } + + /** + * 指定用户是否解锁,锁定的用户无法进行身份验证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isAccountNonLocked() + { + return true; + } + + /** + * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isCredentialsNonExpired() + { + return true; + } + + /** + * 是否可用 ,禁用的用户不能身份验证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isEnabled() + { + return true; + } + + public Long getLoginTime() + { + return loginTime; + } + + public void setLoginTime(Long loginTime) + { + this.loginTime = loginTime; + } + + public String getIpaddr() + { + return ipaddr; + } + + public void setIpaddr(String ipaddr) + { + this.ipaddr = ipaddr; + } + + public String getLoginLocation() + { + return loginLocation; + } + + public void setLoginLocation(String loginLocation) + { + this.loginLocation = loginLocation; + } + + public String getBrowser() + { + return browser; + } + + public void setBrowser(String browser) + { + this.browser = browser; + } + + public String getOs() + { + return os; + } + + public void setOs(String os) + { + this.os = os; + } + + public Long getExpireTime() + { + return expireTime; + } + + public void setExpireTime(Long expireTime) + { + this.expireTime = expireTime; + } + + public Set<String> getPermissions() + { + return permissions; + } + + public void setPermissions(Set<String> permissions) + { + this.permissions = permissions; + } + + public SysUser getUser() + { + return user; + } + + public void setUser(SysUser user) + { + this.user = user; + } + + @Override + public Collection<? extends GrantedAuthority> getAuthorities() + { + return null; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUserApplet.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUserApplet.java new file mode 100644 index 0000000..186be08 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUserApplet.java @@ -0,0 +1,263 @@ +package com.ruoyi.common.core.domain.model; + +import com.alibaba.fastjson2.annotation.JSONField; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.entity.TTenantResp; +import lombok.Data; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; +import java.util.Set; + +/** + * 登录用户身份权限 + * + * @author ruoyi + */ +@Data +public class LoginUserApplet implements UserDetails +{ + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + private String userId; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 用户唯一标识 + */ + private String token; + + /** + * 登录时间 + */ + private Long loginTime; + + /** + * 过期时间 + */ + private Long expireTime; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 权限列表 + */ + private Set<String> permissions; + + /** + * 用户信息 + */ + private TTenantResp user; + + + public LoginUserApplet() + { + } + + public LoginUserApplet(TTenantResp user, Set<String> permissions) + { + this.user = user; + this.permissions = permissions; + } + + public LoginUserApplet(String userId, Long deptId, TTenantResp user, Set<String> permissions) + { + this.userId = userId; + this.deptId = deptId; + this.user = user; + this.permissions = permissions; + } + + public String getUserId() + { + return userId; + } + + public void setUserId(String userId) + { + this.userId = userId; + } + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + public String getToken() + { + return token; + } + + public void setToken(String token) + { + this.token = token; + } + + @JSONField(serialize = false) + @Override + public String getPassword() + { + return user.getPassword(); + } + + @Override + public String getUsername() + { + return user.getResidentName(); + } + + /** + * 账户是否未过期,过期无法验证 + */ + @JSONField(serialize = false) + @Override + public boolean isAccountNonExpired() + { + return true; + } + + /** + * 指定用户是否解锁,锁定的用户无法进行身份验证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isAccountNonLocked() + { + return true; + } + + /** + * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isCredentialsNonExpired() + { + return true; + } + + /** + * 是否可用 ,禁用的用户不能身份验证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isEnabled() + { + return true; + } + + public Long getLoginTime() + { + return loginTime; + } + + public void setLoginTime(Long loginTime) + { + this.loginTime = loginTime; + } + + public String getIpaddr() + { + return ipaddr; + } + + public void setIpaddr(String ipaddr) + { + this.ipaddr = ipaddr; + } + + public String getLoginLocation() + { + return loginLocation; + } + + public void setLoginLocation(String loginLocation) + { + this.loginLocation = loginLocation; + } + + public String getBrowser() + { + return browser; + } + + public void setBrowser(String browser) + { + this.browser = browser; + } + + public String getOs() + { + return os; + } + + public void setOs(String os) + { + this.os = os; + } + + public Long getExpireTime() + { + return expireTime; + } + + public void setExpireTime(Long expireTime) + { + this.expireTime = expireTime; + } + + public Set<String> getPermissions() + { + return permissions; + } + + public void setPermissions(Set<String> permissions) + { + this.permissions = permissions; + } + + + + @Override + public Collection<? extends GrantedAuthority> getAuthorities() + { + return null; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java new file mode 100644 index 0000000..868a1fc --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java @@ -0,0 +1,11 @@ +package com.ruoyi.common.core.domain.model; + +/** + * 用户注册对象 + * + * @author ruoyi + */ +public class RegisterBody extends LoginBody +{ + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/TimeRangeQueryBody.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/TimeRangeQueryBody.java new file mode 100644 index 0000000..7a9239f --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/TimeRangeQueryBody.java @@ -0,0 +1,45 @@ +package com.ruoyi.common.core.domain.model; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.core.domain.BasePage; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Objects; + +@ApiModel("时间范围分页dto") +public class TimeRangeQueryBody extends BasePage { + + @ApiModelProperty("开始时间 格式 yyyy-MM-dd") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private Date startTime; + + @ApiModelProperty("结束时间 格式 yyyy-MM-dd") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private Date endTime; + + public String getStartTime() { + if (Objects.nonNull(startTime)) { + return new SimpleDateFormat("yyyy-MM-dd").format(startTime) + " 00:00:00"; + } + return null; + } + + public void setStartTime(Date startTime) { + this.startTime = startTime; + } + + public String getEndTime() { + if (Objects.nonNull(endTime)) { + return new SimpleDateFormat("yyyy-MM-dd").format(endTime) + " 23:59:59"; + } + return null; + } + + public void setEndTime(Date endTime) { + this.endTime = endTime; + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/exception/ServiceException.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/exception/ServiceException.java new file mode 100644 index 0000000..4983866 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/exception/ServiceException.java @@ -0,0 +1,74 @@ +package com.ruoyi.common.core.exception; + +/** + * 业务异常 + * + * @author ruoyi + */ +public final class ServiceException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 错误码 + */ + private Integer code; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + * + * 和 {@link CommonResult#getDetailMessage()} 一致的设计 + */ + private String detailMessage; + + /** + * 空构造方法,避免反序列化问题 + */ + public ServiceException() + { + } + + public ServiceException(String message) + { + this.message = message; + } + + public ServiceException(String message, Integer code) + { + this.message = message; + this.code = code; + } + + public String getDetailMessage() + { + return detailMessage; + } + + @Override + public String getMessage() + { + return message; + } + + public Integer getCode() + { + return code; + } + + public ServiceException setMessage(String message) + { + this.message = message; + return this; + } + + public ServiceException setDetailMessage(String detailMessage) + { + this.detailMessage = detailMessage; + return this; + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/page/PageDomain.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/PageDomain.java new file mode 100644 index 0000000..8966cb4 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/PageDomain.java @@ -0,0 +1,101 @@ +package com.ruoyi.common.core.page; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 分页数据 + * + * @author ruoyi + */ +public class PageDomain +{ + /** 当前记录起始索引 */ + private Integer pageNum; + + /** 每页显示记录数 */ + private Integer pageSize; + + /** 排序列 */ + private String orderByColumn; + + /** 排序的方向desc或者asc */ + private String isAsc = "asc"; + + /** 分页参数合理化 */ + private Boolean reasonable = true; + + public String getOrderBy() + { + if (StringUtils.isEmpty(orderByColumn)) + { + return ""; + } + return StringUtils.toUnderScoreCase(orderByColumn) + " " + isAsc; + } + + public Integer getPageNum() + { + return pageNum; + } + + public void setPageNum(Integer pageNum) + { + this.pageNum = pageNum; + } + + public Integer getPageSize() + { + return pageSize; + } + + public void setPageSize(Integer pageSize) + { + this.pageSize = pageSize; + } + + public String getOrderByColumn() + { + return orderByColumn; + } + + public void setOrderByColumn(String orderByColumn) + { + this.orderByColumn = orderByColumn; + } + + public String getIsAsc() + { + return isAsc; + } + + public void setIsAsc(String isAsc) + { + if (StringUtils.isNotEmpty(isAsc)) + { + // 兼容前端排序类型 + if ("ascending".equals(isAsc)) + { + isAsc = "asc"; + } + else if ("descending".equals(isAsc)) + { + isAsc = "desc"; + } + this.isAsc = isAsc; + } + } + + public Boolean getReasonable() + { + if (StringUtils.isNull(reasonable)) + { + return Boolean.TRUE; + } + return reasonable; + } + + public void setReasonable(Boolean reasonable) + { + this.reasonable = reasonable; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java new file mode 100644 index 0000000..cea2364 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java @@ -0,0 +1,122 @@ +package com.ruoyi.common.core.page; + + +import java.io.Serializable; +import java.util.List; + +/** + * 表格分页数据对象 + * + * @author ruoyi + */ +public class TableDataInfo<T> implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 总记录数 */ + private long total; + + /** 列表数据 */ + private List<?> records; + + /** 消息状态码 */ + private int code; + + /** 消息内容 */ + private String msg; + +// private boolean hasNextPage; +// private boolean hasPrevPage; +// private long startIndex; + +// public boolean getHasNextPage() { +// return hasNextPage; +// } +// +// public void setHasNextPage(boolean hasNextPage) { +// this.hasNextPage = hasNextPage; +// } +// +// public boolean getHasPrevPage() { +// return hasPrevPage; +// } +// +// public void setHasPrevPage(boolean hasPrevPage) { +// this.hasPrevPage = hasPrevPage; +// } +// +// private boolean hasNextPage(long currentPage, long totalPage) { +// return currentPage < totalPage && totalPage != 0; +// } +// private static boolean hasPrevPage(long currentPage) { +// return currentPage != 1; +// } +// +// public long getStartIndex() { +// return startIndex; +// } +// +// public void setStartIndex(long startIndex) { +// this.startIndex = startIndex; +// } + + + /** + * 表格数据对象 + */ + public TableDataInfo() + { + } + + /** + * 分页 + * + * @param list 列表数据 + * @param total 总记录数 + */ + public TableDataInfo(List<?> list, int total) + { + this.records = list; + this.total = total; + } + + public long getTotal() + { + return total; + } + + public void setTotal(long total) + { + this.total = total; + } + + public List<?> getRecords() + { + return records; + } + + public void setRecords(List<?> records) + { + this.records = records; + } + + public int getCode() + { + return code; + } + + public void setCode(int code) + { + this.code = code; + } + + public String getMsg() + { + return msg; + } + + public void setMsg(String msg) + { + this.msg = msg; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableSupport.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableSupport.java new file mode 100644 index 0000000..a120c30 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableSupport.java @@ -0,0 +1,56 @@ +package com.ruoyi.common.core.page; + +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.utils.ServletUtils; + +/** + * 表格数据处理 + * + * @author ruoyi + */ +public class TableSupport +{ + /** + * 当前记录起始索引 + */ + public static final String PAGE_NUM = "pageNum"; + + /** + * 每页显示记录数 + */ + public static final String PAGE_SIZE = "pageSize"; + + /** + * 排序列 + */ + public static final String ORDER_BY_COLUMN = "orderByColumn"; + + /** + * 排序的方向 "desc" 或者 "asc". + */ + public static final String IS_ASC = "isAsc"; + + /** + * 分页参数合理化 + */ + public static final String REASONABLE = "reasonable"; + + /** + * 封装分页对象 + */ + public static PageDomain getPageDomain() + { + PageDomain pageDomain = new PageDomain(); + pageDomain.setPageNum(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1)); + pageDomain.setPageSize(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10)); + pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN)); + pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC)); + pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE)); + return pageDomain; + } + + public static PageDomain buildPageRequest() + { + return getPageDomain(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java new file mode 100644 index 0000000..96864ac --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java @@ -0,0 +1,343 @@ +package com.ruoyi.common.core.redis; + +import java.util.*; +import java.util.concurrent.TimeUnit; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.BoundSetOperations; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.data.redis.core.script.RedisScript; +import org.springframework.stereotype.Component; + +/** + * spring redis 工具类 + * + * @author ruoyi + **/ +@SuppressWarnings(value = { "unchecked", "rawtypes" }) +@Component +public class RedisCache +{ + private static final String LOCK_PREFIX = "lock:"; + private static final RedisScript<Long> UNLOCK_SCRIPT = new DefaultRedisScript<>( + "if redis.call('get', KEYS[1]) == ARGV[1] then " + + " return redis.call('del', KEYS[1]) " + + "else " + + " return 0 " + + "end", + Long.class + ); + @Autowired + public RedisTemplate redisTemplate; + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + */ + public <T> void setCacheObject(final String key, final T value) + { + redisTemplate.opsForValue().set(key, value); + } + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + * @param timeout 时间 + * @param timeUnit 时间颗粒度 + */ + public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) + { + redisTemplate.opsForValue().set(key, value, timeout, timeUnit); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @return true=设置成功;false=设置失败 + */ + public boolean expire(final String key, final long timeout) + { + return expire(key, timeout, TimeUnit.SECONDS); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @param unit 时间单位 + * @return true=设置成功;false=设置失败 + */ + public boolean expire(final String key, final long timeout, final TimeUnit unit) + { + return redisTemplate.expire(key, timeout, unit); + } + + /** + * 获取有效时间 + * + * @param key Redis键 + * @return 有效时间 + */ + public long getExpire(final String key) + { + return redisTemplate.getExpire(key); + } + + /** + * 判断 key是否存在 + * + * @param key 键 + * @return true 存在 false不存在 + */ + public Boolean hasKey(String key) + { + return redisTemplate.hasKey(key); + } + + /** + * 获得缓存的基本对象。 + * + * @param key 缓存键值 + * @return 缓存键值对应的数据 + */ + public <T> T getCacheObject(final String key) + { + ValueOperations<String, T> operation = redisTemplate.opsForValue(); + return operation.get(key); + } + + /** + * 删除单个对象 + * + * @param key + */ + public boolean deleteObject(final String key) + { + return redisTemplate.delete(key); + } + + /** + * 删除集合对象 + * + * @param collection 多个对象 + * @return + */ + public boolean deleteObject(final Collection collection) + { + return redisTemplate.delete(collection) > 0; + } + + /** + * 缓存List数据 + * + * @param key 缓存的键值 + * @param dataList 待缓存的List数据 + * @return 缓存的对象 + */ + public <T> long setCacheList(final String key, final List<T> dataList) + { + Long count = redisTemplate.opsForList().rightPushAll(key, dataList); + return count == null ? 0 : count; + } + + /** + * 获得缓存的list对象 + * + * @param key 缓存的键值 + * @return 缓存键值对应的数据 + */ + public <T> List<T> getCacheList(final String key) + { + return redisTemplate.opsForList().range(key, 0, -1); + } + + /** + * 缓存Set + * + * @param key 缓存键值 + * @param dataSet 缓存的数据 + * @return 缓存数据的对象 + */ + public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet) + { + BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key); + Iterator<T> it = dataSet.iterator(); + while (it.hasNext()) + { + setOperation.add(it.next()); + } + return setOperation; + } + + /** + * 获得缓存的set + * + * @param key + * @return + */ + public <T> Set<T> getCacheSet(final String key) + { + return redisTemplate.opsForSet().members(key); + } + + /** + * 缓存Map + * + * @param key + * @param dataMap + */ + public <T> void setCacheMap(final String key, final Map<String, T> dataMap) + { + if (dataMap != null) { + redisTemplate.opsForHash().putAll(key, dataMap); + } + } + + /** + * 获得缓存的Map + * + * @param key + * @return + */ + public <T> Map<String, T> getCacheMap(final String key) + { + return redisTemplate.opsForHash().entries(key); + } + + /** + * 往Hash中存入数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @param value 值 + */ + public <T> void setCacheMapValue(final String key, final String hKey, final T value) + { + redisTemplate.opsForHash().put(key, hKey, value); + } + + /** + * 获取Hash中的数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return Hash中的对象 + */ + public <T> T getCacheMapValue(final String key, final String hKey) + { + HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash(); + return opsForHash.get(key, hKey); + } + + /** + * 获取多个Hash中的数据 + * + * @param key Redis键 + * @param hKeys Hash键集合 + * @return Hash对象集合 + */ + public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) + { + return redisTemplate.opsForHash().multiGet(key, hKeys); + } + + /** + * 删除Hash中的某条数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return 是否成功 + */ + public boolean deleteCacheMapValue(final String key, final String hKey) + { + return redisTemplate.opsForHash().delete(key, hKey) > 0; + } + + /** + * 获得缓存的基本对象列表 + * + * @param pattern 字符串前缀 + * @return 对象列表 + */ + public Collection<String> keys(final String pattern) + { + return redisTemplate.keys(pattern); + } + + /** + * 尝试加锁 + * + * @param lockKey 锁的key + * @param requestId 锁的持有者标识符(如UUID) + * @param expireTime 锁的过期时间(秒) + * @return 加锁成功返回true,否则返回false + */ + public boolean tryLock(String lockKey, String requestId, long expireTime) { + String lockKeyWithPrefix = LOCK_PREFIX + lockKey; + // 使用SET命令的NX和PX选项来加锁 + Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKeyWithPrefix, requestId, expireTime, TimeUnit.SECONDS); + return result != null && result; + } + + /** + * 尝试加锁 + * + * @param lockKey 锁的key + * @param requestId 锁的持有者标识符(如UUID) + * @param expireTime 锁的过期时间(秒) + * @return 加锁成功返回true,否则返回false + */ + public boolean trylockLoop(String lockKey, String requestId,long expireTime) { + String lockKeyWithPrefix = LOCK_PREFIX + lockKey; + // 使用SET命令的NX和PX选项来加锁 + Boolean result = false; + int num = 50; + while (num>0){ + result = redisTemplate.opsForValue().setIfAbsent(lockKeyWithPrefix, requestId, expireTime, TimeUnit.SECONDS); + if (result){ + break; + } + try { + TimeUnit.MILLISECONDS.sleep(200); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + num--; + } + return result; + } + + + /** + * 解锁 + * + * @param lockKey 锁的key + * @param requestId 锁的持有者标识符(如UUID) + * @return 解锁成功返回true,否则返回false + */ + public boolean unlock(String lockKey, String requestId) { + String lockKeyWithPrefix = LOCK_PREFIX + lockKey; + // 使用Lua脚本来验证锁的持有者并解锁 + Long result = (Long) redisTemplate.execute(UNLOCK_SCRIPT, Collections.singletonList(lockKeyWithPrefix), requestId); + return result != null && result == 1L; + } + + /** + * 自增 + * @param key 要加一的键 + */ + public int increment(String key) { + // +1 操作 + return redisTemplate.opsForValue().increment(key).intValue(); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/text/CharsetKit.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/CharsetKit.java new file mode 100644 index 0000000..84124aa --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/CharsetKit.java @@ -0,0 +1,86 @@ +package com.ruoyi.common.core.text; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import com.ruoyi.common.utils.StringUtils; + +/** + * 字符集工具类 + * + * @author ruoyi + */ +public class CharsetKit +{ + /** ISO-8859-1 */ + public static final String ISO_8859_1 = "ISO-8859-1"; + /** UTF-8 */ + public static final String UTF_8 = "UTF-8"; + /** GBK */ + public static final String GBK = "GBK"; + + /** ISO-8859-1 */ + public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1); + /** UTF-8 */ + public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8); + /** GBK */ + public static final Charset CHARSET_GBK = Charset.forName(GBK); + + /** + * 转换为Charset对象 + * + * @param charset 字符集,为空则返回默认字符集 + * @return Charset + */ + public static Charset charset(String charset) + { + return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset); + } + + /** + * 转换字符串的字符集编码 + * + * @param source 字符串 + * @param srcCharset 源字符集,默认ISO-8859-1 + * @param destCharset 目标字符集,默认UTF-8 + * @return 转换后的字符集 + */ + public static String convert(String source, String srcCharset, String destCharset) + { + return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset)); + } + + /** + * 转换字符串的字符集编码 + * + * @param source 字符串 + * @param srcCharset 源字符集,默认ISO-8859-1 + * @param destCharset 目标字符集,默认UTF-8 + * @return 转换后的字符集 + */ + public static String convert(String source, Charset srcCharset, Charset destCharset) + { + if (null == srcCharset) + { + srcCharset = StandardCharsets.ISO_8859_1; + } + + if (null == destCharset) + { + destCharset = StandardCharsets.UTF_8; + } + + if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) + { + return source; + } + return new String(source.getBytes(srcCharset), destCharset); + } + + /** + * @return 系统字符集编码 + */ + public static String systemCharset() + { + return Charset.defaultCharset().name(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java new file mode 100644 index 0000000..c918e36 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java @@ -0,0 +1,1000 @@ +package com.ruoyi.common.core.text; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.text.NumberFormat; +import java.util.Set; +import com.ruoyi.common.utils.StringUtils; +import org.apache.commons.lang3.ArrayUtils; + +/** + * 类型转换器 + * + * @author ruoyi + */ +public class Convert +{ + /** + * 转换为字符串<br> + * 如果给定的值为null,或者转换失败,返回默认值<br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static String toStr(Object value, String defaultValue) + { + if (null == value) + { + return defaultValue; + } + if (value instanceof String) + { + return (String) value; + } + return value.toString(); + } + + /** + * 转换为字符串<br> + * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static String toStr(Object value) + { + return toStr(value, null); + } + + /** + * 转换为字符<br> + * 如果给定的值为null,或者转换失败,返回默认值<br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Character toChar(Object value, Character defaultValue) + { + if (null == value) + { + return defaultValue; + } + if (value instanceof Character) + { + return (Character) value; + } + + final String valueStr = toStr(value, null); + return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0); + } + + /** + * 转换为字符<br> + * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Character toChar(Object value) + { + return toChar(value, null); + } + + /** + * 转换为byte<br> + * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Byte toByte(Object value, Byte defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Byte) + { + return (Byte) value; + } + if (value instanceof Number) + { + return ((Number) value).byteValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Byte.parseByte(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为byte<br> + * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Byte toByte(Object value) + { + return toByte(value, null); + } + + /** + * 转换为Short<br> + * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Short toShort(Object value, Short defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Short) + { + return (Short) value; + } + if (value instanceof Number) + { + return ((Number) value).shortValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Short.parseShort(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Short<br> + * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Short toShort(Object value) + { + return toShort(value, null); + } + + /** + * 转换为Number<br> + * 如果给定的值为空,或者转换失败,返回默认值<br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Number toNumber(Object value, Number defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Number) + { + return (Number) value; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return NumberFormat.getInstance().parse(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Number<br> + * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Number toNumber(Object value) + { + return toNumber(value, null); + } + + /** + * 转换为int<br> + * 如果给定的值为空,或者转换失败,返回默认值<br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Integer toInt(Object value, Integer defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Integer) + { + return (Integer) value; + } + if (value instanceof Number) + { + return ((Number) value).intValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Integer.parseInt(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为int<br> + * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Integer toInt(Object value) + { + return toInt(value, null); + } + + /** + * 转换为Integer数组<br> + * + * @param str 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String str) + { + return toIntArray(",", str); + } + + /** + * 转换为Long数组<br> + * + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String str) + { + return toLongArray(",", str); + } + + /** + * 转换为Integer数组<br> + * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Integer[] {}; + } + String[] arr = str.split(split); + final Integer[] ints = new Integer[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Integer v = toInt(arr[i], 0); + ints[i] = v; + } + return ints; + } + + /** + * 转换为Long数组<br> + * + * @param split 分隔符 + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Long[] {}; + } + String[] arr = str.split(split); + final Long[] longs = new Long[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Long v = toLong(arr[i], null); + longs[i] = v; + } + return longs; + } + + /** + * 转换为String数组<br> + * + * @param str 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String str) + { + return toStrArray(",", str); + } + + /** + * 转换为String数组<br> + * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String split, String str) + { + return str.split(split); + } + + /** + * 转换为long<br> + * 如果给定的值为空,或者转换失败,返回默认值<br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Long toLong(Object value, Long defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Long) + { + return (Long) value; + } + if (value instanceof Number) + { + return ((Number) value).longValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).longValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为long<br> + * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Long toLong(Object value) + { + return toLong(value, null); + } + + /** + * 转换为double<br> + * 如果给定的值为空,或者转换失败,返回默认值<br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Double toDouble(Object value, Double defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Double) + { + return (Double) value; + } + if (value instanceof Number) + { + return ((Number) value).doubleValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).doubleValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为double<br> + * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Double toDouble(Object value) + { + return toDouble(value, null); + } + + /** + * 转换为Float<br> + * 如果给定的值为空,或者转换失败,返回默认值<br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Float toFloat(Object value, Float defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Float) + { + return (Float) value; + } + if (value instanceof Number) + { + return ((Number) value).floatValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Float.parseFloat(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Float<br> + * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Float toFloat(Object value) + { + return toFloat(value, null); + } + + /** + * 转换为boolean<br> + * String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值<br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Boolean toBool(Object value, Boolean defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Boolean) + { + return (Boolean) value; + } + String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + valueStr = valueStr.trim().toLowerCase(); + switch (valueStr) + { + case "true": + case "yes": + case "ok": + case "1": + return true; + case "false": + case "no": + case "0": + return false; + default: + return defaultValue; + } + } + + /** + * 转换为boolean<br> + * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Boolean toBool(Object value) + { + return toBool(value, null); + } + + /** + * 转换为Enum对象<br> + * 如果给定的值为空,或者转换失败,返回默认值<br> + * + * @param clazz Enum的Class + * @param value 值 + * @param defaultValue 默认值 + * @return Enum + */ + public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value, E defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (clazz.isAssignableFrom(value.getClass())) + { + @SuppressWarnings("unchecked") + E myE = (E) value; + return myE; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Enum.valueOf(clazz, valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Enum对象<br> + * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> + * + * @param clazz Enum的Class + * @param value 值 + * @return Enum + */ + public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value) + { + return toEnum(clazz, value, null); + } + + /** + * 转换为BigInteger<br> + * 如果给定的值为空,或者转换失败,返回默认值<br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value, BigInteger defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigInteger) + { + return (BigInteger) value; + } + if (value instanceof Long) + { + return BigInteger.valueOf((Long) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigInteger(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigInteger<br> + * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value) + { + return toBigInteger(value, null); + } + + /** + * 转换为BigDecimal<br> + * 如果给定的值为空,或者转换失败,返回默认值<br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigDecimal) + { + return (BigDecimal) value; + } + if (value instanceof Long) + { + return new BigDecimal((Long) value); + } + if (value instanceof Double) + { + return BigDecimal.valueOf((Double) value); + } + if (value instanceof Integer) + { + return new BigDecimal((Integer) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigDecimal(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigDecimal<br> + * 如果给定的值为空,或者转换失败,返回默认值<br> + * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value) + { + return toBigDecimal(value, null); + } + + /** + * 将对象转为字符串<br> + * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @return 字符串 + */ + public static String utf8Str(Object obj) + { + return str(obj, CharsetKit.CHARSET_UTF_8); + } + + /** + * 将对象转为字符串<br> + * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charsetName 字符集 + * @return 字符串 + */ + public static String str(Object obj, String charsetName) + { + return str(obj, Charset.forName(charsetName)); + } + + /** + * 将对象转为字符串<br> + * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(Object obj, Charset charset) + { + if (null == obj) + { + return null; + } + + if (obj instanceof String) + { + return (String) obj; + } + else if (obj instanceof byte[]) + { + return str((byte[]) obj, charset); + } + else if (obj instanceof Byte[]) + { + byte[] bytes = ArrayUtils.toPrimitive((Byte[]) obj); + return str(bytes, charset); + } + else if (obj instanceof ByteBuffer) + { + return str((ByteBuffer) obj, charset); + } + return obj.toString(); + } + + /** + * 将byte数组转为字符串 + * + * @param bytes byte数组 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(byte[] bytes, String charset) + { + return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset)); + } + + /** + * 解码字节码 + * + * @param data 字符串 + * @param charset 字符集,如果此字段为空,则解码的结果取决于平台 + * @return 解码后的字符串 + */ + public static String str(byte[] data, Charset charset) + { + if (data == null) + { + return null; + } + + if (null == charset) + { + return new String(data); + } + return new String(data, charset); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, String charset) + { + if (data == null) + { + return null; + } + + return str(data, Charset.forName(charset)); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, Charset charset) + { + if (null == charset) + { + charset = Charset.defaultCharset(); + } + return charset.decode(data).toString(); + } + + // ----------------------------------------------------------------------- 全角半角转换 + /** + * 半角转全角 + * + * @param input String. + * @return 全角字符串. + */ + public static String toSBC(String input) + { + return toSBC(input, null); + } + + /** + * 半角转全角 + * + * @param input String + * @param notConvertSet 不替换的字符集合 + * @return 全角字符串. + */ + public static String toSBC(String input, Set<Character> notConvertSet) + { + char[] c = input.toCharArray(); + for (int i = 0; i < c.length; i++) + { + if (null != notConvertSet && notConvertSet.contains(c[i])) + { + // 跳过不替换的字符 + continue; + } + + if (c[i] == ' ') + { + c[i] = '\u3000'; + } + else if (c[i] < '\177') + { + c[i] = (char) (c[i] + 65248); + + } + } + return new String(c); + } + + /** + * 全角转半角 + * + * @param input String. + * @return 半角字符串 + */ + public static String toDBC(String input) + { + return toDBC(input, null); + } + + /** + * 替换全角为半角 + * + * @param text 文本 + * @param notConvertSet 不替换的字符集合 + * @return 替换后的字符 + */ + public static String toDBC(String text, Set<Character> notConvertSet) + { + char[] c = text.toCharArray(); + for (int i = 0; i < c.length; i++) + { + if (null != notConvertSet && notConvertSet.contains(c[i])) + { + // 跳过不替换的字符 + continue; + } + + if (c[i] == '\u3000') + { + c[i] = ' '; + } + else if (c[i] > '\uFF00' && c[i] < '\uFF5F') + { + c[i] = (char) (c[i] - 65248); + } + } + String returnString = new String(c); + + return returnString; + } + + /** + * 数字金额大写转换 先写个完整的然后将如零拾替换成零 + * + * @param n 数字 + * @return 中文大写数字 + */ + public static String digitUppercase(double n) + { + String[] fraction = { "角", "分" }; + String[] digit = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" }; + String[][] unit = { { "元", "万", "亿" }, { "", "拾", "佰", "仟" } }; + + String head = n < 0 ? "负" : ""; + n = Math.abs(n); + + String s = ""; + for (int i = 0; i < fraction.length; i++) + { + s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", ""); + } + if (s.length() < 1) + { + s = "整"; + } + int integerPart = (int) Math.floor(n); + + for (int i = 0; i < unit[0].length && integerPart > 0; i++) + { + String p = ""; + for (int j = 0; j < unit[1].length && n > 0; j++) + { + p = digit[integerPart % 10] + unit[1][j] + p; + integerPart = integerPart / 10; + } + s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s; + } + return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整"); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/text/StrFormatter.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/StrFormatter.java new file mode 100644 index 0000000..c78ac77 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/StrFormatter.java @@ -0,0 +1,92 @@ +package com.ruoyi.common.core.text; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 字符串格式化 + * + * @author ruoyi + */ +public class StrFormatter +{ + public static final String EMPTY_JSON = "{}"; + public static final char C_BACKSLASH = '\\'; + public static final char C_DELIM_START = '{'; + public static final char C_DELIM_END = '}'; + + /** + * 格式化字符串<br> + * 此方法只是简单将占位符 {} 按照顺序替换为参数<br> + * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br> + * 例:<br> + * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br> + * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br> + * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br> + * + * @param strPattern 字符串模板 + * @param argArray 参数列表 + * @return 结果 + */ + public static String format(final String strPattern, final Object... argArray) + { + if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray)) + { + return strPattern; + } + final int strPatternLength = strPattern.length(); + + // 初始化定义好的长度以获得更好的性能 + StringBuilder sbuf = new StringBuilder(strPatternLength + 50); + + int handledPosition = 0; + int delimIndex;// 占位符所在位置 + for (int argIndex = 0; argIndex < argArray.length; argIndex++) + { + delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition); + if (delimIndex == -1) + { + if (handledPosition == 0) + { + return strPattern; + } + else + { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果 + sbuf.append(strPattern, handledPosition, strPatternLength); + return sbuf.toString(); + } + } + else + { + if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) + { + if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) + { + // 转义符之前还有一个转义符,占位符依旧有效 + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(Convert.utf8Str(argArray[argIndex])); + handledPosition = delimIndex + 2; + } + else + { + // 占位符被转义 + argIndex--; + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(C_DELIM_START); + handledPosition = delimIndex + 1; + } + } + else + { + // 正常占位符 + sbuf.append(strPattern, handledPosition, delimIndex); + sbuf.append(Convert.utf8Str(argArray[argIndex])); + handledPosition = delimIndex + 2; + } + } + } + // 加入最后一个占位符后所有的字符 + sbuf.append(strPattern, handledPosition, strPattern.length()); + + return sbuf.toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/utils/Constants.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/utils/Constants.java new file mode 100644 index 0000000..8144c3a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/utils/Constants.java @@ -0,0 +1,143 @@ +package com.ruoyi.common.core.utils; + +/** + * 通用常量信息 + * + * @author ruoyi + */ +public class Constants +{ + /** + * UTF-8 字符集 + */ + public static final String UTF8 = "UTF-8"; + + /** + * GBK 字符集 + */ + public static final String GBK = "GBK"; + + /** + * www主域 + */ + public static final String WWW = "www."; + + /** + * RMI 远程方法调用 + */ + public static final String LOOKUP_RMI = "rmi:"; + + /** + * LDAP 远程方法调用 + */ + public static final String LOOKUP_LDAP = "ldap:"; + + /** + * LDAPS 远程方法调用 + */ + public static final String LOOKUP_LDAPS = "ldaps:"; + + /** + * http请求 + */ + public static final String HTTP = "http://"; + + /** + * https请求 + */ + public static final String HTTPS = "https://"; + + /** + * 成功标记 + */ + public static final Integer SUCCESS = 200; + + /** + * 失败标记 + */ + public static final Integer FAIL = 500; + + /** + * 登录成功状态 + */ + public static final String LOGIN_SUCCESS_STATUS = "1"; + + /** + * 登录失败状态 + */ + public static final String LOGIN_FAIL_STATUS = "2"; + + /** + * 登录成功 + */ + public static final String LOGIN_SUCCESS = "Success"; + + /** + * 注销 + */ + public static final String LOGOUT = "Logout"; + + /** + * 注册 + */ + public static final String REGISTER = "Register"; + + /** + * 登录失败 + */ + public static final String LOGIN_FAIL = "Error"; + + /** + * 当前记录起始索引 + */ + public static final String PAGE_NUM = "pageNum"; + + /** + * 每页显示记录数 + */ + public static final String PAGE_SIZE = "pageSize"; + + /** + * 排序列 + */ + public static final String ORDER_BY_COLUMN = "orderByColumn"; + + /** + * 排序的方向 "desc" 或者 "asc". + */ + public static final String IS_ASC = "isAsc"; + + /** + * 验证码有效期(分钟) + */ + public static final long CAPTCHA_EXPIRATION = 2; + + /** + * 资源映射路径 前缀 + */ + public static final String RESOURCE_PREFIX = "/profile"; + + /** + * 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加) + */ + public static final String[] JOB_WHITELIST_STR = { "com.ruoyi" }; + + /** + * 时间格式化 + */ + public static final String DATE_FORMATTER_TIME = "yyyy-MM-dd HH:mm:ss"; + public static final String DATE_FORMATTER_DATE = "yyyy-MM-dd"; + /** + * 修改手机号后缀 + */ + public static final String UPDATE_PHONE = "_updatePhone"; + /** + * 申请建桩后缀 + */ + public static final String APPLY_CHARGING = "_applyCharging"; + /** + * 定时任务违规的字符 + */ + public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml", + "org.springframework", "org.apache", "com.ruoyi.common.core.utils.file" }; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/utils/HttpUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/utils/HttpUtils.java new file mode 100644 index 0000000..7d25f7a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/utils/HttpUtils.java @@ -0,0 +1,311 @@ +package com.ruoyi.common.core.utils; + +import com.ruoyi.common.core.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.*; +import java.io.*; +import java.net.*; +import java.nio.charset.StandardCharsets; +import java.security.cert.X509Certificate; + +/** + * 通用http发送方法 + * + * @author ruoyi + */ +public class HttpUtils +{ + private static final Logger log = LoggerFactory.getLogger(HttpUtils.class); + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url) + { + return sendGet(url, StringUtils.EMPTY); + } + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url, String param) + { + return sendGet(url, param, Constants.UTF8); + } + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @param contentType 编码类型 + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url, String param, String contentType) + { + StringBuilder result = new StringBuilder(); + BufferedReader in = null; + try + { + String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url; + log.info("sendGet - {}", urlNameString); + URL realUrl = new URL(urlNameString); + URLConnection connection = realUrl.openConnection(); + connection.setRequestProperty("accept", "*/*"); + connection.setRequestProperty("connection", "Keep-Alive"); + connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + connection.connect(); + in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType)); + String line; + while ((line = in.readLine()) != null) + { + result.append(line); + } + log.info("recv - {}", result); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e); + } + finally + { + try + { + if (in != null) + { + in.close(); + } + } + catch (Exception ex) + { + log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); + } + } + return result.toString(); + } + + /** + * 向指定 URL 发送POST方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendPost(String url, String param) + { + PrintWriter out = null; + BufferedReader in = null; + StringBuilder result = new StringBuilder(); + try + { + log.info("sendPost - {}", url); + URL realUrl = new URL(url); + URLConnection conn = realUrl.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("contentType", "utf-8"); + conn.setDoOutput(true); + conn.setDoInput(true); + out = new PrintWriter(conn.getOutputStream()); + out.print(param); + out.flush(); + in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); + String line; + while ((line = in.readLine()) != null) + { + result.append(line); + } + log.info("recv - {}", result); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e); + } + finally + { + try + { + if (out != null) + { + out.close(); + } + if (in != null) + { + in.close(); + } + } + catch (IOException ex) + { + log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); + } + } + return result.toString(); + } + + public static String sendSSLPost(String url, String param) + { + StringBuilder result = new StringBuilder(); + String urlNameString = url + "?" + param; + try + { + log.info("sendSSLPost - {}", urlNameString); + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom()); + URL console = new URL(urlNameString); + HttpsURLConnection conn = (HttpsURLConnection) console.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("contentType", "utf-8"); + conn.setDoOutput(true); + conn.setDoInput(true); + + conn.setSSLSocketFactory(sc.getSocketFactory()); + conn.setHostnameVerifier(new TrustAnyHostnameVerifier()); + conn.connect(); + InputStream is = conn.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + String ret = ""; + while ((ret = br.readLine()) != null) + { + if (ret != null && !"".equals(ret.trim())) + { + result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8)); + } + } + log.info("recv - {}", result); + conn.disconnect(); + br.close(); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e); + } + return result.toString(); + } + + public static String post(String strURL, String params) { + String result = ""; + BufferedReader reader = null; + try { + URL url = new URL(strURL);// 创建连接 + HttpURLConnection connection = (HttpURLConnection) url + .openConnection(); + connection.setDoOutput(true); + connection.setDoInput(true); + connection.setUseCaches(false); + connection.setInstanceFollowRedirects(true); + connection.setRequestMethod("POST"); // 设置请求方式 + connection.setRequestProperty("Accept", "application/json"); // 设置接收数据的格式 + connection.setRequestProperty("Content-Type", "application/json"); // 设置发送数据的格式 + connection.connect(); + if (params != null && !StringUtils.isEmpty(params)) { + byte[] writebytes = params.getBytes(); + // 设置文件长度 + // connection.setRequestProperty("Content-Length", String.valueOf(writebytes.length)); + OutputStream outwritestream = connection.getOutputStream(); + outwritestream.write(params.getBytes()); + outwritestream.flush(); + outwritestream.close(); + // Log.d("hlhupload", "doJsonPost: conn"+connection.getResponseCode()); + } + if (connection.getResponseCode() == 200) { + log.info("<<<<<<<<<<<<<请求响应:{}", connection.getResponseMessage()); + reader = new BufferedReader( + new InputStreamReader(connection.getInputStream())); + result = reader.readLine(); + log.info("<<<<<<<<<<<<<请求响应:{}", result); + } else { + throw new ServiceException(connection.getResponseMessage()); + } + } catch (Exception e) { + throw new ServiceException("http的post请求异常!" + e.getMessage()); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return result; + } + + private static class TrustAnyTrustManager implements X509TrustManager + { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) + { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) + { + } + + @Override + public X509Certificate[] getAcceptedIssuers() + { + return new X509Certificate[] {}; + } + } + + private static class TrustAnyHostnameVerifier implements HostnameVerifier + { + @Override + public boolean verify(String hostname, SSLSession session) + { + return true; + } + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/BillTypeEnum.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/BillTypeEnum.java new file mode 100644 index 0000000..422c873 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/BillTypeEnum.java @@ -0,0 +1,48 @@ +package com.ruoyi.common.enums; + +import lombok.Getter; + +public enum BillTypeEnum { + + Zujin(1,"租金"), + Yajin(2,"押金"), + ShenghuoFee(3,"生活费用"), + FangwuYanshou(4,"房屋验收"), + ; + + private Integer code; + + private String name; + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + BillTypeEnum(Integer code, String name){ + this.code = code; + this.name = name; + } + + public static BillTypeEnum getByCode(Integer code){ + BillTypeEnum[] values = BillTypeEnum.values(); + for (BillTypeEnum value : values) { + if (value.code==code){ + return value; + } + } + return null; + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessStatus.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessStatus.java new file mode 100644 index 0000000..10b7306 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessStatus.java @@ -0,0 +1,20 @@ +package com.ruoyi.common.enums; + +/** + * 操作状态 + * + * @author ruoyi + * + */ +public enum BusinessStatus +{ + /** + * 成功 + */ + SUCCESS, + + /** + * 失败 + */ + FAIL, +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessType.java new file mode 100644 index 0000000..5a5ce48 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessType.java @@ -0,0 +1,63 @@ +package com.ruoyi.common.enums; + +/** + * 业务操作类型 + * + * @author ruoyi + */ +public enum BusinessType +{ + /** + * 其它 + */ + OTHER, + + /** + * 新增 + */ + INSERT, + + /** + * 修改 + */ + UPDATE, + + /** + * 删除 + */ + DELETE, + + /** + * 授权 + */ + GRANT, + + /** + * 导出 + */ + EXPORT, + + /** + * 导入 + */ + IMPORT, + + /** + * 强退 + */ + FORCE, + + /** + * 生成代码 + */ + GENCODE, + + /** + * 清空数据 + */ + CLEAN, + /** + * 查询 + */ + SELECT, +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java new file mode 100644 index 0000000..0d945be --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java @@ -0,0 +1,19 @@ +package com.ruoyi.common.enums; + +/** + * 数据源 + * + * @author ruoyi + */ +public enum DataSourceType +{ + /** + * 主库 + */ + MASTER, + + /** + * 从库 + */ + SLAVE +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/DisabledEnum.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DisabledEnum.java new file mode 100644 index 0000000..d383dff --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DisabledEnum.java @@ -0,0 +1,57 @@ +package com.ruoyi.common.enums; + +import lombok.Getter; + +/** + * @author xiaochen + * @ClassName Disable + * @Description + * @date 2022-06-08 16:55 + */ +public enum DisabledEnum { + NO(0, "否"), + YES(1, "是"); + + private String desc; + + + private int code; + + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + DisabledEnum(int code, String desc) { + this.code = code; + this.desc = desc; + } + + /** + * 通过code获取枚举 + * + * @param code + * @return + */ + public static DisabledEnum fromCode(Integer code) { + DisabledEnum[] resultTypes = DisabledEnum.values(); + for (DisabledEnum resultType : resultTypes) { + if (code.equals(resultType.getCode())) { + return resultType; + } + } + return null; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/HttpMethod.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/HttpMethod.java new file mode 100644 index 0000000..be6f739 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/HttpMethod.java @@ -0,0 +1,36 @@ +package com.ruoyi.common.enums; + +import java.util.HashMap; +import java.util.Map; +import org.springframework.lang.Nullable; + +/** + * 请求方式 + * + * @author ruoyi + */ +public enum HttpMethod +{ + GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE; + + private static final Map<String, HttpMethod> mappings = new HashMap<>(16); + + static + { + for (HttpMethod httpMethod : values()) + { + mappings.put(httpMethod.name(), httpMethod); + } + } + + @Nullable + public static HttpMethod resolve(@Nullable String method) + { + return (method != null ? mappings.get(method) : null); + } + + public boolean matches(String method) + { + return (this == resolve(method)); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/LimitType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/LimitType.java new file mode 100644 index 0000000..c609fd8 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/LimitType.java @@ -0,0 +1,20 @@ +package com.ruoyi.common.enums; + +/** + * 限流类型 + * + * @author ruoyi + */ + +public enum LimitType +{ + /** + * 默认策略全局限流 + */ + DEFAULT, + + /** + * 根据请求者IP进行限流 + */ + IP +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/OperatorType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/OperatorType.java new file mode 100644 index 0000000..bdd143c --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/OperatorType.java @@ -0,0 +1,24 @@ +package com.ruoyi.common.enums; + +/** + * 操作人类别 + * + * @author ruoyi + */ +public enum OperatorType +{ + /** + * 其它 + */ + OTHER, + + /** + * 后台用户 + */ + MANAGE, + + /** + * 手机端用户 + */ + MOBILE +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/ProcessCategoryEnum.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/ProcessCategoryEnum.java new file mode 100644 index 0000000..3cd5f01 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/ProcessCategoryEnum.java @@ -0,0 +1,58 @@ +package com.ruoyi.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 1. 入户调查 + * 2. 价格评估 + * 3. 协议签订 + * 固定对应表 state_process_module + */ +@AllArgsConstructor +public enum ProcessCategoryEnum { + CATEGORY0(0, "错误分类"), + CATEGORY1(1, "合同新增审批"), + CATEGORY2(2, "合同签订审批"), + CATEGORY3(3, "合同提前终止审批"), + ; + + + private final Integer value; + private final String text; + + public Integer getValue() { + return value; + } + + public String getText() { + return text; + } + + public static Integer getValue(String text) { + for (ProcessCategoryEnum v : ProcessCategoryEnum.values()) { + if (v.text.equals(text)) { + return v.value; + } + } + return 0; + } + + public static String getValueByKey(Integer key) { + for (ProcessCategoryEnum v : ProcessCategoryEnum.values()) { + if (v.getValue().equals(key)) { + return v.getText(); + } + } + return ""; + } + + public static ProcessCategoryEnum getEnumByKey(Integer key) { + for (ProcessCategoryEnum v : ProcessCategoryEnum.values()) { + if (v.getValue().equals(key)) { + return v; + } + } + return CATEGORY0; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/StateProcessActionEnum.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/StateProcessActionEnum.java new file mode 100644 index 0000000..101868c --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/StateProcessActionEnum.java @@ -0,0 +1,28 @@ +package com.ruoyi.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 流程动作枚举 + * + */ +@AllArgsConstructor +public enum StateProcessActionEnum { + + START(0), // "发起" + REJECTED(3), // "拒绝" + APPROVED(4), // "通过" + CANCELED(5), // "撤销" + BACK(7), // "回退" + COPY(12), // "抄送" + FORWARD(13), // "转发" + COMMENT(14), // "评论" + TRANSACT(15); // "办理" + + private final int value; + + public int getValue() { + return value; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/SubmitStatusEnum.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/SubmitStatusEnum.java new file mode 100644 index 0000000..e382fb4 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/SubmitStatusEnum.java @@ -0,0 +1,40 @@ +package com.ruoyi.common.enums; + +import lombok.Getter; + +import java.util.Objects; + +public enum SubmitStatusEnum { + SUBMITTED(-1, ""), + REJECT(0, "已退回"), + PENDING_REVIEW(1, "待审核"), + ACCEPT(3, "已接收"); + + private final int value; + private final String text; + + public static String getTextByValue(Integer value) { + if (Objects.isNull(value)) { + return ""; + } + for (SubmitStatusEnum v : SubmitStatusEnum.values()) { + if (v.getValue() == (value)) { + return v.getText(); + } + } + return ""; + } + + SubmitStatusEnum(int value, String text) { + this.text = text; + this.value = value; + } + + public int getValue() { + return value; + } + + public String getText() { + return text; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/TaskEventType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/TaskEventType.java new file mode 100644 index 0000000..f96fc9c --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/TaskEventType.java @@ -0,0 +1,146 @@ +/* + * Copyright 2023-2025 Licensed under the Dual Licensing + * website: https://aizuda.com + */ +package com.ruoyi.common.enums; + +/** + * 流程引擎监听类型 + * + * <p> + * <a href="https://aizuda.com">官网</a>尊重知识产权,不允许非法使用,后果自负 + * </p> + * + * @author lizhongyuan + * @since 1.0 + */ +public enum TaskEventType { + /** + * 发起 + */ + start, + /** + * 创建 + */ + create, + /** + * 再创建,仅用于流程回退 + */ + recreate, + /** + * 抄送 + */ + cc, + /** + * 分配 + */ + assignment, + /** + * 委派任务解决 + */ + delegateResolve, + /** + * 任务加签 + */ + addTaskActor, + /** + * 任务减签 + */ + removeTaskActor, + /** + * 驳回至上一步处理 + */ + reject, + /** + * 角色认领 + */ + claimRole, + /** + * 部门认领 + */ + claimDepartment, + /** + * 拿回未执行任务 + */ + reclaim, + /** + * 撤回指定任务 + */ + withdraw, + /** + * 唤醒历史任务 + */ + resume, + /** + * 完成 + */ + complete, + /** + * 撤销 + */ + revoke, + /** + * 终止 + */ + terminate, + /** + * 更新 + */ + update, + /** + * 删除 + */ + delete, + /** + * 调用外部流程任务【办理子流程】 + */ + callProcess, + /** + * 超时 + */ + timeout, + /** + * 跳转 + */ + jump, + /** + * 自动跳转 + */ + autoJump, + /** + * 驳回跳转 + */ + rejectJump, + /** + * 路由跳转 + */ + routeJump, + /** + * 驳回重新审批跳转 + */ + reApproveJump, + /** + * 自动审批完成 + */ + autoComplete, + /** + * 自动审批拒绝 + */ + autoReject, + /** + * 触发器任务 + */ + trigger, + /** + * 结束 + */ + end; + + public boolean eq(TaskEventType eventType) { + return this == eventType; + } + + public boolean ne(TaskEventType eventType) { + return this != eventType; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/UpdateTypeEnum.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/UpdateTypeEnum.java new file mode 100644 index 0000000..56992e8 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/UpdateTypeEnum.java @@ -0,0 +1,58 @@ +package com.ruoyi.common.enums; + + +/** + * @author xiaochen + * @ClassName Disable + * @Description + * @date 2022-06-08 16:55 + */ +public enum UpdateTypeEnum { + /*调整类型 1=经理更换,2=负责人更换,3=人员新增,4=调整计划时间*/ + PROJECT_MANAGER(1, "经理更换"), + WORK_HEADER(2, "负责人更换"), + ADD_USER(3, "人员新增"), + UPDATE_PLAN_TIME(4, "调整计划时间"), + REDUCE_USER(5, "人员减少"); + + private String desc; + private int code; + + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + UpdateTypeEnum(int code, String desc) { + this.code = code; + this.desc = desc; + } + + /** + * 通过code获取枚举 + * + * @param code + * @return + */ + public static UpdateTypeEnum fromCode(Integer code) { + UpdateTypeEnum[] resultTypes = UpdateTypeEnum.values(); + for (UpdateTypeEnum resultType : resultTypes) { + if (code.equals(resultType.getCode())) { + return resultType; + } + } + return null; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/UserStatus.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/UserStatus.java new file mode 100644 index 0000000..d7ff44a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/UserStatus.java @@ -0,0 +1,30 @@ +package com.ruoyi.common.enums; + +/** + * 用户状态 + * + * @author ruoyi + */ +public enum UserStatus +{ + OK("0", "正常"), DISABLE("1", "停用"), DELETED("2", "删除"); + + private final String code; + private final String info; + + UserStatus(String code, String info) + { + this.code = code; + this.info = info; + } + + public String getCode() + { + return code; + } + + public String getInfo() + { + return info; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/DemoModeException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/DemoModeException.java new file mode 100644 index 0000000..f6ad2ab --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/DemoModeException.java @@ -0,0 +1,15 @@ +package com.ruoyi.common.exception; + +/** + * 演示模式异常 + * + * @author ruoyi + */ +public class DemoModeException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public DemoModeException() + { + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/GlobalException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/GlobalException.java new file mode 100644 index 0000000..81a71b5 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/GlobalException.java @@ -0,0 +1,58 @@ +package com.ruoyi.common.exception; + +/** + * 全局异常 + * + * @author ruoyi + */ +public class GlobalException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + * + * 和 {@link CommonResult#getDetailMessage()} 一致的设计 + */ + private String detailMessage; + + /** + * 空构造方法,避免反序列化问题 + */ + public GlobalException() + { + } + + public GlobalException(String message) + { + this.message = message; + } + + public String getDetailMessage() + { + return detailMessage; + } + + public GlobalException setDetailMessage(String detailMessage) + { + this.detailMessage = detailMessage; + return this; + } + + @Override + public String getMessage() + { + return message; + } + + public GlobalException setMessage(String message) + { + this.message = message; + return this; + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/ServiceException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/ServiceException.java new file mode 100644 index 0000000..fcc7ab6 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/ServiceException.java @@ -0,0 +1,74 @@ +package com.ruoyi.common.exception; + +/** + * 业务异常 + * + * @author ruoyi + */ +public final class ServiceException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 错误码 + */ + private Integer code; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + * + * 和 {@link CommonResult#getDetailMessage()} 一致的设计 + */ + private String detailMessage; + + /** + * 空构造方法,避免反序列化问题 + */ + public ServiceException() + { + } + + public ServiceException(String message) + { + this.message = message; + } + + public ServiceException(String message, Integer code) + { + this.message = message; + this.code = code; + } + + public String getDetailMessage() + { + return detailMessage; + } + + @Override + public String getMessage() + { + return message; + } + + public Integer getCode() + { + return code; + } + + public ServiceException setMessage(String message) + { + this.message = message; + return this; + } + + public ServiceException setDetailMessage(String detailMessage) + { + this.detailMessage = detailMessage; + return this; + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/UtilException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/UtilException.java new file mode 100644 index 0000000..980fa46 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/UtilException.java @@ -0,0 +1,26 @@ +package com.ruoyi.common.exception; + +/** + * 工具类异常 + * + * @author ruoyi + */ +public class UtilException extends RuntimeException +{ + private static final long serialVersionUID = 8247610319171014183L; + + public UtilException(Throwable e) + { + super(e.getMessage(), e); + } + + public UtilException(String message) + { + super(message); + } + + public UtilException(String message, Throwable throwable) + { + super(message, throwable); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java new file mode 100644 index 0000000..b55d72e --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java @@ -0,0 +1,97 @@ +package com.ruoyi.common.exception.base; + +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.StringUtils; + +/** + * 基础异常 + * + * @author ruoyi + */ +public class BaseException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 所属模块 + */ + private String module; + + /** + * 错误码 + */ + private String code; + + /** + * 错误码对应的参数 + */ + private Object[] args; + + /** + * 错误消息 + */ + private String defaultMessage; + + public BaseException(String module, String code, Object[] args, String defaultMessage) + { + this.module = module; + this.code = code; + this.args = args; + this.defaultMessage = defaultMessage; + } + + public BaseException(String module, String code, Object[] args) + { + this(module, code, args, null); + } + + public BaseException(String module, String defaultMessage) + { + this(module, null, null, defaultMessage); + } + + public BaseException(String code, Object[] args) + { + this(null, code, args, null); + } + + public BaseException(String defaultMessage) + { + this(null, null, null, defaultMessage); + } + + @Override + public String getMessage() + { + String message = null; + if (!StringUtils.isEmpty(code)) + { + message = MessageUtils.message(code, args); + } + if (message == null) + { + message = defaultMessage; + } + return message; + } + + public String getModule() + { + return module; + } + + public String getCode() + { + return code; + } + + public Object[] getArgs() + { + return args; + } + + public String getDefaultMessage() + { + return defaultMessage; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileException.java new file mode 100644 index 0000000..871f09b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileException.java @@ -0,0 +1,19 @@ +package com.ruoyi.common.exception.file; + +import com.ruoyi.common.exception.base.BaseException; + +/** + * 文件信息异常类 + * + * @author ruoyi + */ +public class FileException extends BaseException +{ + private static final long serialVersionUID = 1L; + + public FileException(String code, Object[] args) + { + super("file", code, args, null); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java new file mode 100644 index 0000000..70e0ec9 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.file; + +/** + * 文件名称超长限制异常类 + * + * @author ruoyi + */ +public class FileNameLengthLimitExceededException extends FileException +{ + private static final long serialVersionUID = 1L; + + public FileNameLengthLimitExceededException(int defaultFileNameLength) + { + super("upload.filename.exceed.length", new Object[] { defaultFileNameLength }); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java new file mode 100644 index 0000000..ec6ab05 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.file; + +/** + * 文件名大小限制异常类 + * + * @author ruoyi + */ +public class FileSizeLimitExceededException extends FileException +{ + private static final long serialVersionUID = 1L; + + public FileSizeLimitExceededException(long defaultMaxSize) + { + super("upload.exceed.maxSize", new Object[] { defaultMaxSize }); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileUploadException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileUploadException.java new file mode 100644 index 0000000..f45e7ef --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileUploadException.java @@ -0,0 +1,61 @@ +package com.ruoyi.common.exception.file; + +import java.io.PrintStream; +import java.io.PrintWriter; + +/** + * 文件上传异常类 + * + * @author ruoyi + */ +public class FileUploadException extends Exception +{ + + private static final long serialVersionUID = 1L; + + private final Throwable cause; + + public FileUploadException() + { + this(null, null); + } + + public FileUploadException(final String msg) + { + this(msg, null); + } + + public FileUploadException(String msg, Throwable cause) + { + super(msg); + this.cause = cause; + } + + @Override + public void printStackTrace(PrintStream stream) + { + super.printStackTrace(stream); + if (cause != null) + { + stream.println("Caused by:"); + cause.printStackTrace(stream); + } + } + + @Override + public void printStackTrace(PrintWriter writer) + { + super.printStackTrace(writer); + if (cause != null) + { + writer.println("Caused by:"); + cause.printStackTrace(writer); + } + } + + @Override + public Throwable getCause() + { + return cause; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java new file mode 100644 index 0000000..011f308 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java @@ -0,0 +1,80 @@ +package com.ruoyi.common.exception.file; + +import java.util.Arrays; + +/** + * 文件上传 误异常类 + * + * @author ruoyi + */ +public class InvalidExtensionException extends FileUploadException +{ + private static final long serialVersionUID = 1L; + + private String[] allowedExtension; + private String extension; + private String filename; + + public InvalidExtensionException(String[] allowedExtension, String extension, String filename) + { + super("文件[" + filename + "]后缀[" + extension + "]不正确,请上传" + Arrays.toString(allowedExtension) + "格式"); + this.allowedExtension = allowedExtension; + this.extension = extension; + this.filename = filename; + } + + public String[] getAllowedExtension() + { + return allowedExtension; + } + + public String getExtension() + { + return extension; + } + + public String getFilename() + { + return filename; + } + + public static class InvalidImageExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidFlashExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidMediaExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidVideoExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidVideoExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/job/TaskException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/job/TaskException.java new file mode 100644 index 0000000..a567b40 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/job/TaskException.java @@ -0,0 +1,34 @@ +package com.ruoyi.common.exception.job; + +/** + * 计划策略异常 + * + * @author ruoyi + */ +public class TaskException extends Exception +{ + private static final long serialVersionUID = 1L; + + private Code code; + + public TaskException(String msg, Code code) + { + this(msg, code, null); + } + + public TaskException(String msg, Code code, Exception nestedEx) + { + super(msg, nestedEx); + this.code = code; + } + + public Code getCode() + { + return code; + } + + public enum Code + { + TASK_EXISTS, NO_TASK_EXISTS, TASK_ALREADY_STARTED, UNKNOWN, CONFIG_ERROR, TASK_NODE_NOT_AVAILABLE + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/state/StateErrorCode.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/state/StateErrorCode.java new file mode 100644 index 0000000..4118ae8 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/state/StateErrorCode.java @@ -0,0 +1,21 @@ +package com.ruoyi.common.exception.state; + + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +public enum StateErrorCode { + + PROCESS_TEMPLATE_KEY_EXISTS("已经存在此名称的模版了!"), + PROCESS_TEMPLATE_NOT_EXISTS("流程模版不存在!"), + PROCESS_NOT_DEPLOY("模版尚未部署成功"), + PROCESS_VERSION_ERROR("流程引擎版本不一致"), + ; + + private final String value; + + public String getValue() { + return value; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/BlackListException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/BlackListException.java new file mode 100644 index 0000000..2bf5038 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/BlackListException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 黑名单IP异常类 + * + * @author ruoyi + */ +public class BlackListException extends UserException +{ + private static final long serialVersionUID = 1L; + + public BlackListException() + { + super("login.blocked", null); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java new file mode 100644 index 0000000..389dbc7 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 验证码错误异常类 + * + * @author ruoyi + */ +public class CaptchaException extends UserException +{ + private static final long serialVersionUID = 1L; + + public CaptchaException() + { + super("user.jcaptcha.error", null); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java new file mode 100644 index 0000000..85f9486 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 验证码失效异常类 + * + * @author ruoyi + */ +public class CaptchaExpireException extends UserException +{ + private static final long serialVersionUID = 1L; + + public CaptchaExpireException() + { + super("user.jcaptcha.expire", null); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserException.java new file mode 100644 index 0000000..c292d70 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserException.java @@ -0,0 +1,18 @@ +package com.ruoyi.common.exception.user; + +import com.ruoyi.common.exception.base.BaseException; + +/** + * 用户信息异常类 + * + * @author ruoyi + */ +public class UserException extends BaseException +{ + private static final long serialVersionUID = 1L; + + public UserException(String code, Object[] args) + { + super("user", code, args, null); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserNotExistsException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserNotExistsException.java new file mode 100644 index 0000000..eff8181 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserNotExistsException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 用户不存在异常类 + * + * @author ruoyi + */ +public class UserNotExistsException extends UserException +{ + private static final long serialVersionUID = 1L; + + public UserNotExistsException() + { + super("user.not.exists", null); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java new file mode 100644 index 0000000..a7f3e5f --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 用户密码不正确或不符合规范异常类 + * + * @author ruoyi + */ +public class UserPasswordNotMatchException extends UserException +{ + private static final long serialVersionUID = 1L; + + public UserPasswordNotMatchException() + { + super("user.password.not.match", null); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java new file mode 100644 index 0000000..c887cf1 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 用户错误最大次数异常类 + * + * @author ruoyi + */ +public class UserPasswordRetryLimitExceedException extends UserException +{ + private static final long serialVersionUID = 1L; + + public UserPasswordRetryLimitExceedException(int retryLimitCount, int lockTime) + { + super("user.password.retry.limit.exceed", new Object[] { retryLimitCount, lockTime }); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java new file mode 100644 index 0000000..e1e431b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java @@ -0,0 +1,24 @@ +package com.ruoyi.common.filter; + +import com.alibaba.fastjson2.filter.SimplePropertyPreFilter; + +/** + * 排除JSON敏感属性 + * + * @author ruoyi + */ +public class PropertyPreExcludeFilter extends SimplePropertyPreFilter +{ + public PropertyPreExcludeFilter() + { + } + + public PropertyPreExcludeFilter addExcludes(String... filters) + { + for (int i = 0; i < filters.length; i++) + { + this.getExcludes().add(filters[i]); + } + return this; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java new file mode 100644 index 0000000..a1bcfe2 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java @@ -0,0 +1,52 @@ +package com.ruoyi.common.filter; + +import java.io.IOException; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import org.springframework.http.MediaType; +import com.ruoyi.common.utils.StringUtils; + +/** + * Repeatable 过滤器 + * + * @author ruoyi + */ +public class RepeatableFilter implements Filter +{ + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + ServletRequest requestWrapper = null; + if (request instanceof HttpServletRequest + && StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) + { + requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response); + } + if (null == requestWrapper) + { + chain.doFilter(request, response); + } + else + { + chain.doFilter(requestWrapper, response); + } + } + + @Override + public void destroy() + { + + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java new file mode 100644 index 0000000..407d1ba --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java @@ -0,0 +1,76 @@ +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; +import com.ruoyi.common.constant.Constants; + +/** + * 构建可重复读取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(Constants.UTF8); + response.setCharacterEncoding(Constants.UTF8); + + body = HttpHelper.getBodyString(request).getBytes(Constants.UTF8); + } + + @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) + { + + } + }; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java new file mode 100644 index 0000000..9052f0d --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java @@ -0,0 +1,75 @@ +package com.ruoyi.common.filter; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.enums.HttpMethod; + +/** + * 防止XSS攻击的过滤器 + * + * @author ruoyi + */ +public class XssFilter implements Filter +{ + /** + * 排除链接 + */ + public List<String> excludes = new ArrayList<>(); + + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + String tempExcludes = filterConfig.getInitParameter("excludes"); + if (StringUtils.isNotEmpty(tempExcludes)) + { + String[] url = tempExcludes.split(","); + for (int i = 0; url != null && i < url.length; i++) + { + excludes.add(url[i]); + } + } + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + HttpServletRequest req = (HttpServletRequest) request; + HttpServletResponse resp = (HttpServletResponse) response; + if (handleExcludeURL(req, resp)) + { + chain.doFilter(request, response); + return; + } + XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request); + chain.doFilter(xssRequest, response); + } + + private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) + { + String url = request.getServletPath(); + String method = request.getMethod(); + // GET DELETE 不过滤 + if (method == null || HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method)) + { + return true; + } + return StringUtils.matches(url, excludes); + } + + @Override + public void destroy() + { + + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java new file mode 100644 index 0000000..05149f0 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java @@ -0,0 +1,111 @@ +package com.ruoyi.common.filter; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import org.apache.commons.io.IOUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.html.EscapeUtil; + +/** + * XSS过滤处理 + * + * @author ruoyi + */ +public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper +{ + /** + * @param request + */ + public XssHttpServletRequestWrapper(HttpServletRequest request) + { + super(request); + } + + @Override + public String[] getParameterValues(String name) + { + String[] values = super.getParameterValues(name); + if (values != null) + { + int length = values.length; + String[] escapesValues = new String[length]; + for (int i = 0; i < length; i++) + { + // 防xss攻击和过滤前后空格 + escapesValues[i] = EscapeUtil.clean(values[i]).trim(); + } + return escapesValues; + } + return super.getParameterValues(name); + } + + @Override + public ServletInputStream getInputStream() throws IOException + { + // 非json类型,直接返回 + if (!isJsonRequest()) + { + return super.getInputStream(); + } + + // 为空,直接返回 + String json = IOUtils.toString(super.getInputStream(), "utf-8"); + if (StringUtils.isEmpty(json)) + { + return super.getInputStream(); + } + + // xss过滤 + json = EscapeUtil.clean(json).trim(); + byte[] jsonBytes = json.getBytes("utf-8"); + final ByteArrayInputStream bis = new ByteArrayInputStream(jsonBytes); + return new ServletInputStream() + { + @Override + public boolean isFinished() + { + return true; + } + + @Override + public boolean isReady() + { + return true; + } + + @Override + public int available() throws IOException + { + return jsonBytes.length; + } + + @Override + public void setReadListener(ReadListener readListener) + { + } + + @Override + public int read() throws IOException + { + return bis.read(); + } + }; + } + + /** + * 是否是Json请求 + * + * @param request + */ + public boolean isJsonRequest() + { + String header = super.getHeader(HttpHeaders.CONTENT_TYPE); + return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE); + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/redis/configure/FastJson2JsonRedisSerializer.java b/ruoyi-common/src/main/java/com/ruoyi/common/redis/configure/FastJson2JsonRedisSerializer.java new file mode 100644 index 0000000..c9f3b66 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/redis/configure/FastJson2JsonRedisSerializer.java @@ -0,0 +1,50 @@ +package com.ruoyi.common.redis.configure; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONReader; +import com.alibaba.fastjson2.JSONWriter; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.SerializationException; + +import java.nio.charset.Charset; + +/** + * Redis使用FastJson序列化 + * + * @author ruoyi + */ +public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T> +{ + public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + + private Class<T> clazz; + + + public FastJson2JsonRedisSerializer(Class<T> clazz) + { + super(); + this.clazz = clazz; + } + + @Override + public byte[] serialize(T t) throws SerializationException + { + if (t == null) + { + return new byte[0]; + } + return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET); + } + + @Override + public T deserialize(byte[] bytes) throws SerializationException + { + if (bytes == null || bytes.length <= 0) + { + return null; + } + String str = new String(bytes, DEFAULT_CHARSET); + + return JSON.parseObject(str, clazz, JSONReader.Feature.SupportAutoType); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/redis/service/RedisService.java b/ruoyi-common/src/main/java/com/ruoyi/common/redis/service/RedisService.java new file mode 100644 index 0000000..1ae72cd --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/redis/service/RedisService.java @@ -0,0 +1,273 @@ +package com.ruoyi.common.redis.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.BoundSetOperations; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * spring redis 工具类 + * + * @author ruoyi + **/ +@SuppressWarnings(value = { "unchecked", "rawtypes" }) +@Component +public class RedisService +{ + @Autowired + public RedisTemplate redisTemplate; + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + */ + public <T> void setCacheObject(final String key, final T value) + { + redisTemplate.opsForValue().set(key, value); + } + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + * @param timeout 时间 + * @param timeUnit 时间颗粒度 + */ + public <T> void setCacheObject(final String key, final T value, final Long timeout, final TimeUnit timeUnit) + { + redisTemplate.opsForValue().set(key, value, timeout, timeUnit); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @return true=设置成功;false=设置失败 + */ + public boolean expire(final String key, final long timeout) + { + return expire(key, timeout, TimeUnit.SECONDS); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @param unit 时间单位 + * @return true=设置成功;false=设置失败 + */ + public boolean expire(final String key, final long timeout, final TimeUnit unit) + { + return redisTemplate.expire(key, timeout, unit); + } + + /** + * 获取有效时间 + * + * @param key Redis键 + * @return 有效时间 + */ + public long getExpire(final String key) + { + return redisTemplate.getExpire(key); + } + + /** + * 判断 key是否存在 + * + * @param key 键 + * @return true 存在 false不存在 + */ + public Boolean hasKey(String key) + { + return redisTemplate.hasKey(key); + } + + /** + * 获得缓存的基本对象。 + * + * @param key 缓存键值 + * @return 缓存键值对应的数据 + */ + public <T> T getCacheObject(final String key) + { + ValueOperations<String, T> operation = redisTemplate.opsForValue(); + return operation.get(key); + } + + /** + * 删除单个对象 + * + * @param key + */ + public boolean deleteObject(final String key) + { + return redisTemplate.delete(key); + } + + /** + * 删除集合对象 + * + * @param collection 多个对象 + * @return + */ + public boolean deleteObject(final Collection collection) + { + return redisTemplate.delete(collection) > 0; + } + + /** + * 缓存List数据 + * + * @param key 缓存的键值 + * @param dataList 待缓存的List数据 + * @return 缓存的对象 + */ + public <T> long setCacheList(final String key, final List<T> dataList) + { + Long count = redisTemplate.opsForList().rightPushAll(key, dataList); + return count == null ? 0 : count; + } + + /** + * 获得缓存的list对象 + * + * @param key 缓存的键值 + * @return 缓存键值对应的数据 + */ + public <T> List<T> getCacheList(final String key) + { + return redisTemplate.opsForList().range(key, 0, -1); + } + + /** + * 缓存Set + * + * @param key 缓存键值 + * @param dataSet 缓存的数据 + * @return 缓存数据的对象 + */ + public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet) + { + BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key); + Iterator<T> it = dataSet.iterator(); + while (it.hasNext()) + { + setOperation.add(it.next()); + } + return setOperation; + } + + /** + * 获得缓存的set + * + * @param key + * @return + */ + public <T> Set<T> getCacheSet(final String key) + { + return redisTemplate.opsForSet().members(key); + } + + /** + * 缓存Map + * + * @param key + * @param dataMap + */ + public <T> void setCacheMap(final String key, final Map<String, T> dataMap) + { + if (dataMap != null) { + redisTemplate.opsForHash().putAll(key, dataMap); + } + } + + public <T> void setCacheMap(final String key, final Map<String, T> dataMap, long timeout) + { + if (dataMap != null) { + redisTemplate.opsForHash().putAll(key, dataMap); + redisTemplate.expire(key, timeout, TimeUnit.SECONDS); + } + } + + /** + * 获得缓存的Map + * + * @param key + * @return + */ + public <T> Map<String, T> getCacheMap(final String key) + { + return redisTemplate.opsForHash().entries(key); + } + + /** + * 往Hash中存入数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @param value 值 + */ + public <T> void setCacheMapValue(final String key, final String hKey, final T value) + { + redisTemplate.opsForHash().put(key, hKey, value); + } + + /** + * 获取Hash中的数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return Hash中的对象 + */ + public <T> T getCacheMapValue(final String key, final String hKey) + { + HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash(); + return opsForHash.get(key, hKey); + } + + /** + * 获取多个Hash中的数据 + * + * @param key Redis键 + * @param hKeys Hash键集合 + * @return Hash对象集合 + */ + public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) + { + return redisTemplate.opsForHash().multiGet(key, hKeys); + } + + /** + * 删除Hash中的某条数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return 是否成功 + */ + public boolean deleteCacheMapValue(final String key, final String hKey) + { + return redisTemplate.opsForHash().delete(key, hKey) > 0; + } + + /** + * 获得缓存的基本对象列表 + * + * @param pattern 字符串前缀 + * @return 对象列表 + */ + public Collection<String> keys(final String pattern) + { + return redisTemplate.keys(pattern); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/resp/AccessTokenRespBody.java b/ruoyi-common/src/main/java/com/ruoyi/common/resp/AccessTokenRespBody.java new file mode 100644 index 0000000..293408d --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/resp/AccessTokenRespBody.java @@ -0,0 +1,28 @@ +package com.ruoyi.common.resp; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * AccessToken 全局唯一 + * + * @author liheng + */ +@Data +public class AccessTokenRespBody implements Serializable { + + /** + * 获取到的凭证 + */ + @JsonProperty("access_token") + private String accessToken; + /** + * 凭证有效时间,单位:秒 + */ + @JsonProperty("expires_in") + private int expiresIn; + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/Arith.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/Arith.java new file mode 100644 index 0000000..b6326c2 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/Arith.java @@ -0,0 +1,114 @@ +package com.ruoyi.common.utils; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +/** + * 精确的浮点数运算 + * + * @author ruoyi + */ +public class Arith +{ + + /** 默认除法运算精度 */ + private static final int DEF_DIV_SCALE = 10; + + /** 这个类不能实例化 */ + private Arith() + { + } + + /** + * 提供精确的加法运算。 + * @param v1 被加数 + * @param v2 加数 + * @return 两个参数的和 + */ + public static double add(double v1, double v2) + { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.add(b2).doubleValue(); + } + + /** + * 提供精确的减法运算。 + * @param v1 被减数 + * @param v2 减数 + * @return 两个参数的差 + */ + public static double sub(double v1, double v2) + { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.subtract(b2).doubleValue(); + } + + /** + * 提供精确的乘法运算。 + * @param v1 被乘数 + * @param v2 乘数 + * @return 两个参数的积 + */ + public static double mul(double v1, double v2) + { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.multiply(b2).doubleValue(); + } + + /** + * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 + * 小数点以后10位,以后的数字四舍五入。 + * @param v1 被除数 + * @param v2 除数 + * @return 两个参数的商 + */ + public static double div(double v1, double v2) + { + return div(v1, v2, DEF_DIV_SCALE); + } + + /** + * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 + * 定精度,以后的数字四舍五入。 + * @param v1 被除数 + * @param v2 除数 + * @param scale 表示表示需要精确到小数点以后几位。 + * @return 两个参数的商 + */ + public static double div(double v1, double v2, int scale) + { + if (scale < 0) + { + throw new IllegalArgumentException( + "The scale must be a positive integer or zero"); + } + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + if (b1.compareTo(BigDecimal.ZERO) == 0) + { + return BigDecimal.ZERO.doubleValue(); + } + return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue(); + } + + /** + * 提供精确的小数位四舍五入处理。 + * @param v 需要四舍五入的数字 + * @param scale 小数点后保留几位 + * @return 四舍五入后的结果 + */ + public static double round(double v, int scale) + { + if (scale < 0) + { + throw new IllegalArgumentException( + "The scale must be a positive integer or zero"); + } + BigDecimal b = new BigDecimal(Double.toString(v)); + BigDecimal one = BigDecimal.ONE; + return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/CodeGenerateUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/CodeGenerateUtils.java new file mode 100644 index 0000000..76492bc --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/CodeGenerateUtils.java @@ -0,0 +1,76 @@ +package com.ruoyi.common.utils; + +import java.math.BigDecimal; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @Description + * @Author xiaochen + * @Date 2021/7/28 10:26 + */ +public class CodeGenerateUtils { + + /** + * @return + * @Description 获取商品编码 + * 商品编码规则:nanoTime(后5位)*5位随机数(10000~99999) + * @Author xiaochen + */ + public static String generateProductCode() { + long nanoPart = System.nanoTime() % 100000L; + if (nanoPart < 10000L) { + nanoPart += 10000L; + } + long randomPart = (long) (Math.random() * (90000) + 10000); + String code = "0" + new BigDecimal(nanoPart).multiply(new BigDecimal(randomPart)); + return code.substring(code.length() - 10); + } + + /** + * @return + * @Description 生成订单编号 10位 + * 订单编号规则:(10位):(年末尾*月,取后2位)+(用户ID%3.33*日取整后2位)+(timestamp*10000以内随机数,取后6位) + * @Author xiaochen + */ + public static String generateOrderSn() { + Calendar calendar = Calendar.getInstance(); + int year = calendar.get(Calendar.YEAR); + year = year % 10; + if (year == 0) year = 10; + int month = calendar.get(Calendar.MONTH) + 1; + int yearMonth = year * month; + String yearMonthPart = "0" + yearMonth; + yearMonthPart = yearMonthPart.substring(yearMonthPart.length() - 2); + + int day = calendar.get(Calendar.DAY_OF_MONTH); + double v = Math.random() * 10000; + int dayNum = (int) ((v % 3.33) * day); + String dayPart = "0" + dayNum; + dayPart = dayPart.substring(dayPart.length() - 2); + + String timestampPart = "" + (Math.random() * 10000) * (System.currentTimeMillis() / 10000); + timestampPart = timestampPart.replace(".", "").replace("E", ""); + timestampPart = timestampPart.substring(0, 6); + return yearMonthPart + dayPart + timestampPart; + } + + /** + * @return + * @Description 生成统一支付单号 规则:年(2)月(2)日(2)时(2)分(2)+timestamp*5位随机整数取后5位 + * @Author xiaochen + */ + public static String generateVolumeSn() { + Calendar calendar = Calendar.getInstance(); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddhhmmss"); + String dateTime = dateFormat.format(calendar.getTime()); + dateTime = dateTime.substring(2); + String timestampPart = "" + (Math.random() * 10000) * (System.currentTimeMillis() / 10000); + timestampPart = timestampPart.replace(".", "").replace("E", ""); + timestampPart = timestampPart.substring(0, 0); + return dateTime + timestampPart; + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java new file mode 100644 index 0000000..3df99a9 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java @@ -0,0 +1,387 @@ +package com.ruoyi.common.utils; + +import java.lang.management.ManagementFactory; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.*; +import java.time.format.DateTimeFormatter; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.lang3.time.DateFormatUtils; + +/** + * 时间工具类 + * + * @author ruoyi + */ +public class DateUtils extends org.apache.commons.lang3.time.DateUtils +{ + public static String YYYY = "yyyy"; + + public static String YYYY_MM = "yyyy-MM"; + + public static String YYYY_MM_DD = "yyyy-MM-dd"; + + public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; + + public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; + + private static String[] parsePatterns = { + "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", + "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", + "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; + + /** + * 获取当前Date型日期 + * + * @return Date() 当前日期 + */ + public static Date getNowDate() + { + return new Date(); + } + + /** + * 获取当前日期, 默认格式为yyyy-MM-dd + * + * @return String + */ + public static String getDate() + { + return dateTimeNow(YYYY_MM_DD); + } + + public static final String getTime() + { + return dateTimeNow(YYYY_MM_DD_HH_MM_SS); + } + + public static final String dateTimeNow() + { + return dateTimeNow(YYYYMMDDHHMMSS); + } + + public static final String dateTimeNow(final String format) + { + return parseDateToStr(format, new Date()); + } + + public static final String dateTime(final Date date) + { + return parseDateToStr(YYYY_MM_DD, date); + } + + public static final String parseDateToStr(final String format, final Date date) + { + return new SimpleDateFormat(format).format(date); + } + + public static final Date dateTime(final String format, final String ts) + { + try + { + return new SimpleDateFormat(format).parse(ts); + } + catch (ParseException e) + { + throw new RuntimeException(e); + } + } + + /** + * 日期路径 即年/月/日 如2018/08/08 + */ + public static final String datePath() + { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyy/MM/dd"); + } + + /** + * 日期路径 即年/月/日 如20180808 + */ + public static final String dateTime() + { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyyMMdd"); + } + + /** + * 日期型字符串转化为日期 格式 + */ + public static Date parseDate(Object str) + { + if (str == null) + { + return null; + } + try + { + return parseDate(str.toString(), parsePatterns); + } + catch (ParseException e) + { + return null; + } + } + + /** + * 获取服务器启动时间 + */ + public static Date getServerStartDate() + { + long time = ManagementFactory.getRuntimeMXBean().getStartTime(); + return new Date(time); + } + + /** + * 计算相差天数 + */ + public static int differentDaysByMillisecond(Date date1, Date date2) + { + return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24))); + } + + /** + * 计算时间差 + * + * @param endDate 最后时间 + * @param startTime 开始时间 + * @return 时间差(天/小时/分钟) + */ + public static String timeDistance(Date endDate, Date startTime) + { + long nd = 1000 * 24 * 60 * 60; + long nh = 1000 * 60 * 60; + long nm = 1000 * 60; + // long ns = 1000; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - startTime.getTime(); + // 计算差多少天 + long day = diff / nd; + // 计算差多少小时 + long hour = diff % nd / nh; + // 计算差多少分钟 + long min = diff % nd % nh / nm; + // 计算差多少秒//输出结果 + // long sec = diff % nd % nh % nm / ns; + return day + "天" + hour + "小时" + min + "分钟"; + } + + /** + * 增加 LocalDateTime ==> Date + */ + public static Date toDate(LocalDateTime temporalAccessor) + { + ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } + + /** + * 增加 LocalDate ==> Date + */ + public static Date toDate(LocalDate temporalAccessor) + { + LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0)); + ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } + + /** + * 指定日期所在周的周一和周日时间 + * + * @return 结果集 + */ + public static Map<String, Date> getWeekDate(Date date) { + Map<String, Date> map = new HashMap<>(2); + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + // 设置一个星期的第一天,按中国的习惯一个星期的第一天是星期一 + cal.setFirstDayOfWeek(Calendar.MONDAY); + // 获得当前日期是一个星期的第几天 + int dayWeek = cal.get(Calendar.DAY_OF_WEEK); + if (dayWeek == 1) { + dayWeek = 8; + } + // 根据日历的规则,给当前日期减去星期几与一个星期第一天的差值 + cal.add(Calendar.DATE, cal.getFirstDayOfWeek() - dayWeek); + Date mondayDate = cal.getTime(); + cal.add(Calendar.DATE, 4 + cal.getFirstDayOfWeek()); + Date sundayDate = cal.getTime(); + map.put("first", mondayDate); + map.put("last", sundayDate); + return map; + } + + /** + * 指定日期所在月的第一天/最后一天时间 + * + * @return 结果集 + */ + public static Map<String, Date> getMonthDate(Date date) { + Map<String, Date> map = new HashMap<>(2); + Calendar cal = Calendar.getInstance(); + //设置指定日期 + cal.setTime(date); + //获取当月第一天日期 + int first = cal.getActualMinimum(Calendar.DAY_OF_MONTH); + cal.set(Calendar.DAY_OF_MONTH, first); + Date firstDay = cal.getTime(); + map.put("first", firstDay); + //获取当月最后一天日期 + int last = cal.getActualMaximum(Calendar.DAY_OF_MONTH); + cal.set(Calendar.DAY_OF_MONTH, last); + Date lastDay = cal.getTime(); + map.put("last", lastDay); + return map; + } + + /** + * 获取日期逻辑中所在的季度数 + * + * @param date 当前日期 + * @return 第几季度 + */ + public static int getQuarterOfYear(Date date) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return calendar.get(Calendar.MONTH) / 3 + 1; + } + /** + * 指定日期所在季度的第一天/最后一天时间 + * + * @return 结果集 + */ + public static Map<String, Date> getQuarterDate(Date date) { + Map<String, Date> map = new HashMap<>(2); + + int quarter = getQuarterOfYear(date); + int year = dateToLocalDateTime(date).getYear(); + + int startMonth = (quarter - 1) * 3; + // 根据月获取开始时间 + Calendar cal = Calendar.getInstance(); + cal.set(Calendar.YEAR, year); + cal.set(Calendar.MONTH, startMonth); + cal.set(Calendar.DAY_OF_MONTH, 1); + cal.set(Calendar.HOUR, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + Date first = cal.getTime(); + map.put("first", first); + + int lastMonth = quarter * 3 - 1; + // 根据月获取结束时间 + cal = Calendar.getInstance(); + cal.set(Calendar.YEAR, year); + cal.set(Calendar.MONTH, lastMonth); + cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH)); + cal.set(Calendar.HOUR, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + Date last = cal.getTime(); + map.put("last", last); + + return map; + } + + /** + * 指定日期所在年的第一天/最后一天时间 + * + * @return 结果集 + */ + public static Map<String, Date> getYearDate(Date date) { + Map<String, Date> map = new HashMap<>(2); + Calendar cal = Calendar.getInstance(); + //设置指定日期 + cal.setTime(date); + //获取本年第一天日期 + int first = cal.getActualMinimum(Calendar.DAY_OF_YEAR); + cal.set(Calendar.DAY_OF_YEAR, first); + Date firstDay = cal.getTime(); + map.put("first", firstDay); + //获取本年最后一天日期 + int last = cal.getActualMaximum(Calendar.DAY_OF_YEAR); + cal.set(Calendar.DAY_OF_YEAR, last); + Date lastDay = cal.getTime(); + map.put("last", lastDay); + return map; + } + + /** + * 获取当天的00:00:00 + * + * @return + */ + public static LocalDateTime getDayStart(LocalDateTime time) { + return time.with(LocalTime.MIN); + } + + + /** + * 获取当天的23:59:59 + * + * @return + */ + public static LocalDateTime getDayEnd(LocalDateTime time) { + return time.with(LocalTime.MAX); + } + + /** + * localdatetime转为字符串 + * + * @param time localdatetime + * @return 字符串 + */ + public static String localDateTimeToString(LocalDateTime time) { + DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + return df.format(time); + } + /** + * localdatetime转为字符串 + * + * @param time localdatetime + * @return 字符串 + */ + public static String localDateTimeToStringYear(LocalDateTime time) { + DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy年MM月dd日"); + return df.format(time); + } + + /** + * Date转为LocalDateTime + * + * @param date 日期 + * @return LocalDateTime + */ + public static LocalDateTime dateToLocalDateTime(Date date) { + Instant instant = date.toInstant(); + ZoneId zoneId = ZoneId.systemDefault(); + return instant.atZone(zoneId).toLocalDateTime(); + } + + /** + * localdate转为字符串 + * + * @param time localdate + * @return 字符串 + */ + public static String localDateToString(LocalDate time) { + DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + return df.format(time); + } + + /** + * 字符串转为localdate + * + * @param time localdate + * @return 字符串 + */ + public static LocalDate stringToLocalDate(String time) { + return LocalDate.parse(time, DateTimeFormatter.ofPattern("yyyy-MM-dd")); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java new file mode 100644 index 0000000..cc5eab2 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java @@ -0,0 +1,186 @@ +package com.ruoyi.common.utils; + +import java.util.Collection; +import java.util.List; +import com.alibaba.fastjson2.JSONArray; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.core.domain.entity.SysDictData; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.spring.SpringUtils; + +/** + * 字典工具类 + * + * @author ruoyi + */ +public class DictUtils +{ + /** + * 分隔符 + */ + public static final String SEPARATOR = ","; + + /** + * 设置字典缓存 + * + * @param key 参数键 + * @param dictDatas 字典数据列表 + */ + public static void setDictCache(String key, List<SysDictData> dictDatas) + { + SpringUtils.getBean(RedisCache.class).setCacheObject(getCacheKey(key), dictDatas); + } + + /** + * 获取字典缓存 + * + * @param key 参数键 + * @return dictDatas 字典数据列表 + */ + public static List<SysDictData> getDictCache(String key) + { + JSONArray arrayCache = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key)); + if (StringUtils.isNotNull(arrayCache)) + { + return arrayCache.toList(SysDictData.class); + } + return null; + } + + /** + * 根据字典类型和字典值获取字典标签 + * + * @param dictType 字典类型 + * @param dictValue 字典值 + * @return 字典标签 + */ + public static String getDictLabel(String dictType, String dictValue) + { + return getDictLabel(dictType, dictValue, SEPARATOR); + } + + /** + * 根据字典类型和字典标签获取字典值 + * + * @param dictType 字典类型 + * @param dictLabel 字典标签 + * @return 字典值 + */ + public static String getDictValue(String dictType, String dictLabel) + { + return getDictValue(dictType, dictLabel, SEPARATOR); + } + + /** + * 根据字典类型和字典值获取字典标签 + * + * @param dictType 字典类型 + * @param dictValue 字典值 + * @param separator 分隔符 + * @return 字典标签 + */ + public static String getDictLabel(String dictType, String dictValue, String separator) + { + StringBuilder propertyString = new StringBuilder(); + List<SysDictData> datas = getDictCache(dictType); + + if (StringUtils.isNotNull(datas)) + { + if (StringUtils.containsAny(separator, dictValue)) + { + for (SysDictData dict : datas) + { + for (String value : dictValue.split(separator)) + { + if (value.equals(dict.getDictValue())) + { + propertyString.append(dict.getDictLabel()).append(separator); + break; + } + } + } + } + else + { + for (SysDictData dict : datas) + { + if (dictValue.equals(dict.getDictValue())) + { + return dict.getDictLabel(); + } + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 根据字典类型和字典标签获取字典值 + * + * @param dictType 字典类型 + * @param dictLabel 字典标签 + * @param separator 分隔符 + * @return 字典值 + */ + public static String getDictValue(String dictType, String dictLabel, String separator) + { + StringBuilder propertyString = new StringBuilder(); + List<SysDictData> datas = getDictCache(dictType); + + if (StringUtils.containsAny(separator, dictLabel) && StringUtils.isNotEmpty(datas)) + { + for (SysDictData dict : datas) + { + for (String label : dictLabel.split(separator)) + { + if (label.equals(dict.getDictLabel())) + { + propertyString.append(dict.getDictValue()).append(separator); + break; + } + } + } + } + else + { + for (SysDictData dict : datas) + { + if (dictLabel.equals(dict.getDictLabel())) + { + return dict.getDictValue(); + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 删除指定字典缓存 + * + * @param key 字典键 + */ + public static void removeDictCache(String key) + { + SpringUtils.getBean(RedisCache.class).deleteObject(getCacheKey(key)); + } + + /** + * 清空字典缓存 + */ + public static void clearDictCache() + { + Collection<String> keys = SpringUtils.getBean(RedisCache.class).keys(CacheConstants.SYS_DICT_KEY + "*"); + SpringUtils.getBean(RedisCache.class).deleteObject(keys); + } + + /** + * 设置cache key + * + * @param configKey 参数键 + * @return 缓存键key + */ + public static String getCacheKey(String configKey) + { + return CacheConstants.SYS_DICT_KEY + configKey; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java new file mode 100644 index 0000000..214e4a0 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java @@ -0,0 +1,39 @@ +package com.ruoyi.common.utils; + +import java.io.PrintWriter; +import java.io.StringWriter; +import org.apache.commons.lang3.exception.ExceptionUtils; + +/** + * 错误信息处理类。 + * + * @author ruoyi + */ +public class ExceptionUtil +{ + /** + * 获取exception的详细错误信息。 + */ + public static String getExceptionMessage(Throwable e) + { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw, true)); + return sw.toString(); + } + + public static String getRootErrorMessage(Exception e) + { + Throwable root = ExceptionUtils.getRootCause(e); + root = (root == null ? e : root); + if (root == null) + { + return ""; + } + String msg = root.getMessage(); + if (msg == null) + { + return "null"; + } + return StringUtils.defaultString(msg); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/IDCardUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/IDCardUtils.java new file mode 100644 index 0000000..dde704d --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/IDCardUtils.java @@ -0,0 +1,18 @@ +package com.ruoyi.common.utils; + +public class IDCardUtils { + + public static void main(String[] args) { + String idCardNumber = "110101199001011234"; + String birthday = getBirthdayFromIdCard(idCardNumber); + System.out.println("出生日期为:" + birthday); + } + + public static String getBirthdayFromIdCard(String idCardNumber) { + String birthday = idCardNumber.substring(6, 14); + String year = birthday.substring(0, 4); + String month = birthday.substring(4, 6); + String day = birthday.substring(6, 8); + return year + "-" + month + "-" + day; + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/LogUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/LogUtils.java new file mode 100644 index 0000000..0de30c6 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/LogUtils.java @@ -0,0 +1,18 @@ +package com.ruoyi.common.utils; + +/** + * 处理并记录日志文件 + * + * @author ruoyi + */ +public class LogUtils +{ + public static String getBlock(Object msg) + { + if (msg == null) + { + msg = ""; + } + return "[" + msg.toString() + "]"; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/MessageUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/MessageUtils.java new file mode 100644 index 0000000..7dac75a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/MessageUtils.java @@ -0,0 +1,26 @@ +package com.ruoyi.common.utils; + +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; +import com.ruoyi.common.utils.spring.SpringUtils; + +/** + * 获取i18n资源文件 + * + * @author ruoyi + */ +public class MessageUtils +{ + /** + * 根据消息键和参数 获取消息 委托给spring messageSource + * + * @param code 消息键 + * @param args 参数 + * @return 获取国际化翻译值 + */ + public static String message(String code, Object... args) + { + MessageSource messageSource = SpringUtils.getBean(MessageSource.class); + return messageSource.getMessage(code, args, LocaleContextHolder.getLocale()); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/MultipartFileUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/MultipartFileUtil.java new file mode 100644 index 0000000..3759d6a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/MultipartFileUtil.java @@ -0,0 +1,222 @@ +//package com.ruoyi.common.utils; +// +//import cn.hutool.core.io.unit.DataSize; +//import cn.hutool.core.util.IdUtil; +//import cn.hutool.http.HttpRequest; +//import cn.hutool.http.HttpResponse; +//import com.google.common.collect.ImmutableList; +//import javafx.util.Duration; +//import org.apache.catalina.Manager; +//import org.apache.commons.io.FilenameUtils; +//import org.apache.commons.lang3.StringUtils; +//import org.apache.http.entity.ContentType; +//import org.springframework.mock.web.MockMultipartFile; +//import org.springframework.web.multipart.MultipartFile; +//import javafx.scene.media.Media; +//import javafx.scene.media.MediaPlayer; +//import ws.schild.jave.EncoderException; +//import ws.schild.jave.MultimediaInfo; +//import ws.schild.jave.MultimediaObject; +// +//import java.io.File; +// +//import java.io.*; +//import java.math.BigDecimal; +//import java.math.RoundingMode; +//import java.net.URL; +//import java.nio.channels.FileChannel; +//import java.util.ArrayList; +//import java.util.List; +// +///** +// * MultipartFile和File互转工具类 +// */ +//public class MultipartFileUtil { +// +// /** +// * 输入流转MultipartFile +// * +// * @param fileName +// * @param inputStream +// * @return +// */ +// public static MultipartFile getMultipartFile(String fileName, InputStream inputStream) { +// MultipartFile multipartFile = null; +// try { +// multipartFile = new MockMultipartFile(fileName, fileName, +// ContentType.APPLICATION_OCTET_STREAM.toString(), inputStream); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// return multipartFile; +// } +// +// /** +// * 读取网络文件 +// * +// * @param url 文件地址 +// * @param fileName 文件名称(需带文件名后缀) +// * @return +// */ +// public static MultipartFile getMultipartFile(String url, String fileName) { +// HttpResponse response = HttpRequest.get(url).execute(); +// InputStream inputStream = response.bodyStream(); +// return MultipartFileUtil.getMultipartFile(fileName, inputStream); +// } +// +// /** +// * File 转MultipartFile +// * +// * @param file +// * @return +// */ +// public static MultipartFile getMultipartFile(File file) { +// FileInputStream fileInputStream = null; +// MultipartFile multipartFile = null; +// try { +// fileInputStream = new FileInputStream(file); +// multipartFile = new MockMultipartFile(file.getName(), file.getName(), +// ContentType.APPLICATION_OCTET_STREAM.toString(), fileInputStream); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// return multipartFile; +// } +// +// /** +// * MultipartFileUtil 转File +// * +// * @param multipartFile +// * @return +// */ +// public static File getFile(MultipartFile multipartFile) { +// // 获取文件名 +// String fileName = multipartFile.getOriginalFilename(); +// // 获取文件后缀 +// String prefix = "." + getExtensionName(fileName); +// File file = null; +// try { +// // 用uuid作为文件名,防止生成的临时文件重复 +// file = File.createTempFile(IdUtil.simpleUUID(), prefix); +// // MultipartFile to File +// multipartFile.transferTo(file); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// return file; +// } +// +// /** +// * 获取文件扩展名,不带 . +// */ +// public static String getExtensionName(String filename) { +// if ((filename != null) && (filename.length() > 0)) { +// int dot = filename.lastIndexOf('.'); +// if ((dot > -1) && (dot < (filename.length() - 1))) { +// return filename.substring(dot + 1); +// } +// } +// return filename; +// } +// +// /** +// 前端上传视频之后,根据上传的视频文件获取视频的大小和时长 +// 1、获取视频时长 +// */ +// public static int readVideoTime(File source) { +// int vedioSecond = Integer.parseInt(parseDuration(source.getAbsolutePath())); +// return vedioSecond; +// } +// +// +// /** +// * 视频时长 +// * +// * @param fileUrl +// * @return String[] 0=秒时长,1=展示时长(格式如 01:00:00) +// */ +// public static String parseDuration(String fileUrl) { +// long ls = 0L; +// String[] length = new String[2]; +// try { +// // +//// URL source = new URL(fileUrl); +// // 构造方法 接受URL对象 +//// MultimediaObject instance = new MultimediaObject(source); +// // 构造方法 接受File对象 +// MultimediaObject instance = new MultimediaObject(new File(fileUrl)); +// MultimediaInfo result = instance.getInfo(); +// ls = result.getDuration() / 1000; +// length[0] = String.valueOf(ls); +// Integer hour = (int) (ls / 3600); +// Integer minute = (int) (ls % 3600) / 60; +// Integer second = (int) (ls - hour * 3600 - minute * 60); +// String hr = hour.toString(); +// String mi = minute.toString(); +// String se = second.toString(); +// if (hr.length() < 2) { +// hr = "0" + hr; +// } +// if (mi.length() < 2) { +// mi = "0" + mi; +// } +// if (se.length() < 2) { +// se = "0" + se; +// } +// +// String noHour = "00"; +// if (noHour.equals(hr)) { +// length[1] = mi + ":" + se; +// } else { +// length[1] = hr + ":" + mi + ":" + se; +// } +// +// } catch (Exception e) { +// e.printStackTrace(); +// } +// System.out.println(length);//{"20","00:20"} +// return String.valueOf(ls); +// } +// +// +// +// +// +// +// /** +// * 2、获取文件大小 +// * @param source +// * @return +// * //***获取视频大小的时候,由于用到了流,使用完之后一定要及时的关闭流,避免无法删除视频文件*** +// * +// */ +// public static BigDecimal readFileSize(File source) { +// //cn.hutool.core.io.unit.DataSize.ofMegabytes() +// FileChannel fc= null; +// //String size = ""; +// BigDecimal size = null ; +// try { +// @SuppressWarnings("resource") +// FileInputStream fis = new FileInputStream(source); +// fc= fis.getChannel(); +// BigDecimal fileSize = new BigDecimal(fc.size()); +// //size = fileSize.divide(new BigDecimal(1048576), 2, RoundingMode.HALF_UP) + "MB"; +// size = fileSize.divide(new BigDecimal(1024*1024), 2, RoundingMode.HALF_UP) ; +// } catch (FileNotFoundException e) { +// e.printStackTrace(); +// } catch (IOException e) { +// e.printStackTrace(); +// } finally { +// if (null!=fc){ +// try{ +// fc.close(); +// }catch(IOException e){ +// e.printStackTrace(); +// } +// } +// } +// return size; +// } +// +// +//} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/NumberUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/NumberUtil.java new file mode 100644 index 0000000..f1ea317 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/NumberUtil.java @@ -0,0 +1,30 @@ +package com.ruoyi.common.utils; + +import java.util.Random; + +public class NumberUtil { + + + public static String getRandomString(int length) { + String base = "abcdefghijklmnopqrstuvwxyz0123456789"; + Random random = new Random(); + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < length; i++) { + int number = random.nextInt(base.length()); + sb.append(base.charAt(number)); + } + return sb.toString(); + } + + public static String getRandomInteger(int length) { + String base = "0123456789"; + Random random = new Random(); + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < length; i++) { + int number = random.nextInt(base.length()); + sb.append(base.charAt(number)); + } + return sb.toString(); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/OrderNos.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/OrderNos.java new file mode 100644 index 0000000..8a80490 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/OrderNos.java @@ -0,0 +1,164 @@ +package com.ruoyi.common.utils; + +import com.ruoyi.common.utils.ip.IpUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.atomic.AtomicLong; + +public class OrderNos { + + public static final char PADCHAR = '0'; + + + + /** + * 长度24位全球唯一标识 + */ + + /** + * 统一不重复内部订单号 + * + * @param systemCode + * 4字节系统编码 + * @return 24字节不重复编码 + */ + public static String oid(String systemCode) { + StringBuilder sb = new StringBuilder(); + sb.append(padding(systemCode, 4)); + sb.append(Did.getInstance().getId(20)); + return sb.toString(); + } + + public static String oid() { + return oid("O001"); + } + + /** + * 获取分布式Id + * + * @return 默认20字符长度的数字不重复编码 + */ + public static String getDid() { + return Did.getInstance().getId(); + } + + public static String getDid(int size) { + return Did.getInstance().getId(size); + } + + public static String getDid(String prefix) { + return prefix + Did.getInstance().getId(); + } + + private static String padding(String text, int size) { + String txt = StringUtils.trimToEmpty(text); + if (StringUtils.length(txt) > size) { + txt = StringUtils.substring(txt, StringUtils.length(txt) - size); + } + return StringUtils.leftPad(txt, size, PADCHAR); + } + + /** + * ID生成器 + * + * 20字符长度 yyMMddHHmmss+3位本机IP末三位+5位随机数字 + * + * @author zhangpu + * + */ + public static class Did { + private static final Logger logger = LoggerFactory.getLogger(OrderNos.class); + private static final int MIN_LENGTH = 19; + private static final int SEQU_MAX = 99999; + private static final int SEQU_19_MAX = 9999; + private volatile static long BASE = 0; + private AtomicLong sequence = new AtomicLong(0); + private String nodeFlag; + private Object nodeFlagLock = new Object(); + private static Did did = new Did(); + + + public static Did getInstance() { + return did; + } + + /** + * 生产新Id + * + * @return + */ + public String getId() { + return getId(20); + } + + public String getId(int size) { + if (size < MIN_LENGTH) { + throw new RuntimeException("did最小长度为" + MIN_LENGTH); + } + StringBuilder sb = new StringBuilder(); + sb.append(DateUtils.dateTimeNow("yyMMddHHmmss")); + sb.append(getNodeFlag()); + sb.append(getSequ(size-15)); + return sb.toString(); + } + + public synchronized String getSequ(int size) { + long timeCount = System.currentTimeMillis() / 1000; + + while (true) { + long now = sequence.get(); + if (timeCount > now) { + // 已经过了本秒,则设置新的值 + if (sequence.compareAndSet(now, timeCount)) { + break; + } + } else { + if (sequence.compareAndSet(now, now + 1)) { + timeCount = now + 1; + break; + } + } + } + int sn = (int) (timeCount % (size>=5?SEQU_MAX:SEQU_19_MAX)); + return StringUtils.leftPad(String.valueOf(sn), size, '0'); + } + + private String getNodeFlag(){ + if (this.nodeFlag == null) { + synchronized (Did.class){ + if (this.nodeFlag==null){ + this.nodeFlag = generateNodeFlag(); + } + } + } + return this.nodeFlag; + } + + /** + * 简单节点编码 + * + * 逻辑:Ip地址后三位,便于快速知道是哪个节点 + * + * @return + */ + private String generateNodeFlag() { + String ipPostfix = null; + try { + String ip = IpUtils.getFirstNoLoopbackIPV4Address(); + ipPostfix = StringUtils.substringAfterLast(ip, "."); + } catch (Exception e) { + logger.warn("生产DID要素本机IP获取失败:" + e.getMessage()); + } + return StringUtils.leftPad(ipPostfix, 3, "0"); + } + + private Did() { + super(); + } + } + + + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java new file mode 100644 index 0000000..8d1445b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java @@ -0,0 +1,45 @@ +//package com.ruoyi.common.utils; +// +//import com.ruoyi.common.core.page.PageDomain; +//import com.ruoyi.common.core.page.TableSupport; +//import com.ruoyi.common.utils.sql.SqlUtil; +// +///** +// * 分页工具类 +// * +// * @author ruoyi +// */ +//public class PageUtils extends PageHelper +//{ +// /** +// * 设置请求分页数据 +// */ +// public static void startPage() +// { +// PageDomain pageDomain = TableSupport.buildPageRequest(); +// Integer pageNum = pageDomain.getPageNum(); +// Integer pageSize = pageDomain.getPageSize(); +// String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); +// Boolean reasonable = pageDomain.getReasonable(); +// PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable); +// } +// +// /** +// * 设置请求分页数据 +// */ +// public static void startPage(Integer pageNum,Integer pageSize) +// { +// PageDomain pageDomain = TableSupport.buildPageRequest(); +// String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); +// Boolean reasonable = pageDomain.getReasonable(); +// PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable); +// } +// +// /** +// * 清理分页的线程变量 +// */ +// public static void clearPage() +// { +// PageHelper.clearPage(); +// } +//} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java new file mode 100644 index 0000000..adea656 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java @@ -0,0 +1,146 @@ +package com.ruoyi.common.utils; + +import com.ruoyi.common.core.domain.model.LoginUserApplet; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.exception.ServiceException; + +/** + * 安全服务工具类 + * + * @author ruoyi + */ +public class SecurityUtils +{ + /** + * 用户ID + **/ + public static Long getUserId() + { + try + { + return getLoginUser().getUserId(); + } + catch (Exception e) + { + throw new ServiceException("获取用户ID异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取部门ID + **/ + public static Long getDeptId() + { + try + { + return getLoginUser().getDeptId(); + } + catch (Exception e) + { + throw new ServiceException("获取部门ID异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取用户账户 + **/ + public static String getUsername() + { + try + { + return getLoginUser().getUsername(); + } + catch (Exception e) + { + throw new ServiceException("获取用户账户异常", HttpStatus.UNAUTHORIZED); + } + } + /** + * 获取用户账户小程序 + **/ + public static String getUsernameApplet() + { + try + { + return getLoginUserApplet().getUsername(); + } + catch (Exception e) + { + throw new ServiceException("获取用户账户异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取用户 + **/ + public static LoginUser getLoginUser() + { + try + { + return (LoginUser) getAuthentication().getPrincipal(); + } + catch (Exception e) + { + throw new ServiceException("获取用户信息异常", HttpStatus.UNAUTHORIZED); + } + } + public static LoginUserApplet getLoginUserApplet() + { + try + { + return (LoginUserApplet) getAuthentication().getPrincipal(); + } + catch (Exception e) + { + throw new ServiceException("获取用户信息异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取Authentication + */ + public static Authentication getAuthentication() + { + return SecurityContextHolder.getContext().getAuthentication(); + } + + /** + * 生成BCryptPasswordEncoder密码 + * + * @param password 密码 + * @return 加密字符串 + */ + public static String encryptPassword(String password) + { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.encode(password); + } + + /** + * 判断密码是否相同 + * + * @param rawPassword 真实密码 + * @param encodedPassword 加密后字符 + * @return 结果 + */ + public static boolean matchesPassword(String rawPassword, String encodedPassword) + { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.matches(rawPassword, encodedPassword); + } + + /** + * 是否为管理员 + * + * @param userId 用户ID + * @return 结果 + */ + public static boolean isAdmin(Long userId) + { + return userId != null && 1L == userId; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java new file mode 100644 index 0000000..febb603 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java @@ -0,0 +1,218 @@ +package com.ruoyi.common.utils; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.text.Convert; + +/** + * 客户端工具类 + * + * @author ruoyi + */ +public class ServletUtils +{ + /** + * 获取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); + } + + /** + * 获取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); + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool(String name) + { + return Convert.toBool(getRequest().getParameter(name)); + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool(String name, Boolean defaultValue) + { + return Convert.toBool(getRequest().getParameter(name), defaultValue); + } + + /** + * 获得所有请求参数 + * + * @param request 请求对象{@link ServletRequest} + * @return Map + */ + public static Map<String, String[]> getParams(ServletRequest request) + { + final Map<String, String[]> map = request.getParameterMap(); + return Collections.unmodifiableMap(map); + } + + /** + * 获得所有请求参数 + * + * @param request 请求对象{@link ServletRequest} + * @return Map + */ + public static Map<String, String> getParamMap(ServletRequest request) + { + Map<String, String> params = new HashMap<>(); + for (Map.Entry<String, String[]> entry : getParams(request).entrySet()) + { + params.put(entry.getKey(), StringUtils.join(entry.getValue(), ",")); + } + return params; + } + + /** + * 获取request + */ + public static HttpServletRequest getRequest() + { + return getRequestAttributes().getRequest(); + } + + /** + * 获取response + */ + public static HttpServletResponse getResponse() + { + return getRequestAttributes().getResponse(); + } + + /** + * 获取session + */ + public static HttpSession getSession() + { + return getRequest().getSession(); + } + + public static ServletRequestAttributes getRequestAttributes() + { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + return (ServletRequestAttributes) attributes; + } + + /** + * 将字符串渲染到客户端 + * + * @param response 渲染对象 + * @param string 待渲染的字符串 + */ + public static void 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(); + } + } + + /** + * 是否是Ajax异步请求 + * + * @param request + */ + public static boolean isAjaxRequest(HttpServletRequest request) + { + String accept = request.getHeader("accept"); + if (accept != null && accept.contains("application/json")) + { + return true; + } + + String xRequestedWith = request.getHeader("X-Requested-With"); + if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) + { + return true; + } + + String uri = request.getRequestURI(); + if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) + { + return true; + } + + String ajax = request.getParameter("__ajax"); + return StringUtils.inStringIgnoreCase(ajax, "json", "xml"); + } + + /** + * 内容编码 + * + * @param str 内容 + * @return 编码后的内容 + */ + public static String urlEncode(String str) + { + try + { + return URLEncoder.encode(str, Constants.UTF8); + } + catch (UnsupportedEncodingException e) + { + return StringUtils.EMPTY; + } + } + + /** + * 内容解码 + * + * @param str 内容 + * @return 解码后的内容 + */ + public static String urlDecode(String str) + { + try + { + return URLDecoder.decode(str, Constants.UTF8); + } + catch (UnsupportedEncodingException e) + { + return StringUtils.EMPTY; + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/SmsUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/SmsUtil.java new file mode 100644 index 0000000..c558969 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/SmsUtil.java @@ -0,0 +1,79 @@ +package com.ruoyi.common.utils; + +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.config.SmsProperties; +import com.ruoyi.common.exception.ServiceException; +import com.tencentcloudapi.common.Credential; +import com.tencentcloudapi.common.exception.TencentCloudSDKException; +import com.tencentcloudapi.common.profile.ClientProfile; +import com.tencentcloudapi.common.profile.HttpProfile; +import com.tencentcloudapi.sms.v20190711.SmsClient; +import com.tencentcloudapi.sms.v20190711.models.SendSmsRequest; +import com.tencentcloudapi.sms.v20190711.models.SendSmsResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.util.Arrays; +import java.util.List; + +@Component +@Slf4j +public class SmsUtil { + + @Resource + SmsProperties smsProperties; + static SmsClient smsClient; + + + public SmsProperties getPro() { + return smsProperties; + } + + @PostConstruct + public void createSmsClient() { + // 实例化一个认证对象,入参需要传入腾讯云账户 SecretId,SecretKey。 + // 为了保护密钥安全,建议将密钥设置在环境变量中或者配置文件中,请参考凭证管理 https://github.com/TencentCloud/tencentcloud-sdk-java?tab=readme-ov-file#%E5%87%AD%E8%AF%81%E7%AE%A1%E7%90%86。 + // 硬编码密钥到代码中有可能随代码泄露而暴露,有安全隐患,并不推荐。 + // SecretId、SecretKey 查询: https://console.cloud.tencent.com/cam/capi + // Credential cred = new Credential("SecretId", "SecretKey"); + + Credential cred = new Credential(smsProperties.getSecretid(), smsProperties.getSecretkey()); + // 实例化一个http选项,可选的,没有特殊需求可以跳过 + HttpProfile httpProfile = new HttpProfile(); + // 指定接入地域域名,默认就近地域接入域名为 sms.tencentcloudapi.com ,也支持指定地域域名访问,例如广州地域的域名为 sms.ap-guangzhou.tencentcloudapi.com + httpProfile.setEndpoint("sms.tencentcloudapi.com"); + // 实例化一个客户端配置对象 + ClientProfile clientProfile = new ClientProfile(); + clientProfile.setHttpProfile(httpProfile); + // 实例化要请求产品(sms)的client对象,第二个参数是地域信息,可以直接填写字符串ap-guangzhou,支持的地域列表参考 https://cloud.tencent.com/document/api/382/52071#.E5.9C.B0.E5.9F.9F.E5.88.97.E8.A1.A8 + smsClient = new SmsClient(cred, "ap-guangzhou", clientProfile); + } + + public boolean sendSms(String phone,String templateId,String[] param){ + phone = phone.startsWith("+86")?phone:("+86"+phone); + SendSmsRequest req = new SendSmsRequest(); + req.setSmsSdkAppid(smsProperties.getAppId()); + req.setPhoneNumberSet(new String[]{phone}); + req.setTemplateID(templateId); + req.setSign(smsProperties.getSign()); + req.setTemplateParamSet(param); + req.setSenderId(""); + req.setSessionContext(""); + req.setExtendCode(""); + try { + SendSmsResponse sendSmsResponse = smsClient.SendSms(req); + System.out.println(JSON.toJSONString(sendSmsResponse)); + return true; + } catch (TencentCloudSDKException e) { + log.error("发送短信失败,{},{}",phone,param,e); + throw new ServiceException("发送短信失败"); + } catch (Exception e){ + log.error("发送短信失败1,{},{}",phone,param,e); + throw new ServiceException("发送短信失败1"); + } + } + + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java new file mode 100644 index 0000000..e683d54 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java @@ -0,0 +1,614 @@ +package com.ruoyi.common.utils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.springframework.util.AntPathMatcher; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.text.StrFormatter; + +/** + * 字符串工具类 + * + * @author ruoyi + */ +public class StringUtils extends org.apache.commons.lang3.StringUtils +{ + /** 空字符串 */ + private static final String NULLSTR = ""; + + /** 下划线 */ + private static final char SEPARATOR = '_'; + + /** + * 获取参数不为空值 + * + * @param value defaultValue 要判断的value + * @return value 返回值 + */ + public static <T> T nvl(T value, T defaultValue) + { + return value != null ? value : defaultValue; + } + + /** + * * 判断一个Collection是否为空, 包含List,Set,Queue + * + * @param coll 要判断的Collection + * @return true:为空 false:非空 + */ + public static boolean isEmpty(Collection<?> coll) + { + return isNull(coll) || coll.isEmpty(); + } + + /** + * * 判断一个Collection是否非空,包含List,Set,Queue + * + * @param coll 要判断的Collection + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Collection<?> coll) + { + return !isEmpty(coll); + } + + /** + * * 判断一个对象数组是否为空 + * + * @param objects 要判断的对象数组 + ** @return true:为空 false:非空 + */ + public static boolean isEmpty(Object[] objects) + { + return isNull(objects) || (objects.length == 0); + } + + /** + * * 判断一个对象数组是否非空 + * + * @param objects 要判断的对象数组 + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Object[] objects) + { + return !isEmpty(objects); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * @return true:为空 false:非空 + */ + public static boolean isEmpty(Map<?, ?> map) + { + return isNull(map) || map.isEmpty(); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Map<?, ?> map) + { + return !isEmpty(map); + } + + /** + * * 判断一个字符串是否为空串 + * + * @param str String + * @return true:为空 false:非空 + */ + public static boolean isEmpty(String str) + { + return isNull(str) || NULLSTR.equals(str.trim()); + } + + /** + * * 判断一个字符串是否为非空串 + * + * @param str String + * @return true:非空串 false:空串 + */ + public static boolean isNotEmpty(String str) + { + return !isEmpty(str); + } + + /** + * * 判断一个对象是否为空 + * + * @param object Object + * @return true:为空 false:非空 + */ + public static boolean isNull(Object object) + { + return object == null; + } + + /** + * * 判断一个对象是否非空 + * + * @param object Object + * @return true:非空 false:空 + */ + public static boolean isNotNull(Object object) + { + return !isNull(object); + } + + /** + * * 判断一个对象是否是数组类型(Java基本型别的数组) + * + * @param object 对象 + * @return true:是数组 false:不是数组 + */ + public static boolean isArray(Object object) + { + return isNotNull(object) && object.getClass().isArray(); + } + + /** + * 去空格 + */ + public static String trim(String str) + { + return (str == null ? "" : str.trim()); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @return 结果 + */ + public static String substring(final String str, int start) + { + if (str == null) + { + return NULLSTR; + } + + if (start < 0) + { + start = str.length() + start; + } + + if (start < 0) + { + start = 0; + } + if (start > str.length()) + { + return NULLSTR; + } + + return str.substring(start); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @param end 结束 + * @return 结果 + */ + public static String substring(final String str, int start, int end) + { + if (str == null) + { + return NULLSTR; + } + + if (end < 0) + { + end = str.length() + end; + } + if (start < 0) + { + start = str.length() + start; + } + + if (end > str.length()) + { + end = str.length(); + } + + if (start > end) + { + return NULLSTR; + } + + if (start < 0) + { + start = 0; + } + if (end < 0) + { + end = 0; + } + + return str.substring(start, end); + } + + /** + * 格式化文本, {} 表示占位符<br> + * 此方法只是简单将占位符 {} 按照顺序替换为参数<br> + * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br> + * 例:<br> + * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br> + * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br> + * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br> + * + * @param template 文本模板,被替换的部分用 {} 表示 + * @param params 参数值 + * @return 格式化后的文本 + */ + public static String format(String template, Object... params) + { + if (isEmpty(params) || isEmpty(template)) + { + return template; + } + return StrFormatter.format(template, params); + } + + /** + * 是否为http(s)://开头 + * + * @param link 链接 + * @return 结果 + */ + public static boolean ishttp(String link) + { + return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS); + } + + /** + * 字符串转set + * + * @param str 字符串 + * @param sep 分隔符 + * @return set集合 + */ + public static final Set<String> str2Set(String str, String sep) + { + return new HashSet<String>(str2List(str, sep, true, false)); + } + + /** + * 字符串转list + * + * @param str 字符串 + * @param sep 分隔符 + * @param filterBlank 过滤纯空白 + * @param trim 去掉首尾空白 + * @return list集合 + */ + public static final List<String> str2List(String str, String sep, boolean filterBlank, boolean trim) + { + List<String> list = new ArrayList<String>(); + if (StringUtils.isEmpty(str)) + { + return list; + } + + // 过滤空白字符串 + if (filterBlank && StringUtils.isBlank(str)) + { + return list; + } + String[] split = str.split(sep); + for (String string : split) + { + if (filterBlank && StringUtils.isBlank(string)) + { + continue; + } + if (trim) + { + string = string.trim(); + } + list.add(string); + } + + return list; + } + + /** + * 判断给定的collection列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value + * + * @param collection 给定的集合 + * @param array 给定的数组 + * @return boolean 结果 + */ + public static boolean containsAny(Collection<String> collection, String... array) + { + if (isEmpty(collection) || isEmpty(array)) + { + return false; + } + else + { + for (String str : array) + { + if (collection.contains(str)) + { + return true; + } + } + return false; + } + } + + /** + * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写 + * + * @param cs 指定字符串 + * @param searchCharSequences 需要检查的字符串数组 + * @return 是否包含任意一个字符串 + */ + public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) + { + if (isEmpty(cs) || isEmpty(searchCharSequences)) + { + return false; + } + for (CharSequence testStr : searchCharSequences) + { + if (containsIgnoreCase(cs, testStr)) + { + return true; + } + } + return false; + } + + /** + * 驼峰转下划线命名 + */ + public static String toUnderScoreCase(String str) + { + if (str == null) + { + return null; + } + StringBuilder sb = new StringBuilder(); + // 前置字符是否大写 + boolean preCharIsUpperCase = true; + // 当前字符是否大写 + boolean curreCharIsUpperCase = true; + // 下一字符是否大写 + boolean nexteCharIsUpperCase = true; + for (int i = 0; i < str.length(); i++) + { + char c = str.charAt(i); + if (i > 0) + { + preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1)); + } + else + { + preCharIsUpperCase = false; + } + + curreCharIsUpperCase = Character.isUpperCase(c); + + if (i < (str.length() - 1)) + { + nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1)); + } + + if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) + { + sb.append(SEPARATOR); + } + else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) + { + sb.append(SEPARATOR); + } + sb.append(Character.toLowerCase(c)); + } + + return sb.toString(); + } + + /** + * 是否包含字符串 + * + * @param str 验证字符串 + * @param strs 字符串组 + * @return 包含返回true + */ + public static boolean inStringIgnoreCase(String str, String... strs) + { + if (str != null && strs != null) + { + for (String s : strs) + { + if (str.equalsIgnoreCase(trim(s))) + { + return true; + } + } + } + return false; + } + + /** + * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld + * + * @param name 转换前的下划线大写方式命名的字符串 + * @return 转换后的驼峰式命名的字符串 + */ + public static String convertToCamelCase(String name) + { + StringBuilder result = new StringBuilder(); + // 快速检查 + if (name == null || name.isEmpty()) + { + // 没必要转换 + return ""; + } + else if (!name.contains("_")) + { + // 不含下划线,仅将首字母大写 + return name.substring(0, 1).toUpperCase() + name.substring(1); + } + // 用下划线将原始字符串分割 + String[] camels = name.split("_"); + for (String camel : camels) + { + // 跳过原始字符串中开头、结尾的下换线或双重下划线 + if (camel.isEmpty()) + { + continue; + } + // 首字母大写 + result.append(camel.substring(0, 1).toUpperCase()); + result.append(camel.substring(1).toLowerCase()); + } + return result.toString(); + } + + /** + * 驼峰式命名法 + * 例如:user_name->userName + */ + public static String toCamelCase(String s) + { + if (s == null) + { + return null; + } + if (s.indexOf(SEPARATOR) == -1) + { + return s; + } + s = s.toLowerCase(); + StringBuilder sb = new StringBuilder(s.length()); + boolean upperCase = false; + for (int i = 0; i < s.length(); i++) + { + char c = s.charAt(i); + + if (c == SEPARATOR) + { + upperCase = true; + } + else if (upperCase) + { + sb.append(Character.toUpperCase(c)); + upperCase = false; + } + else + { + sb.append(c); + } + } + return sb.toString(); + } + + /** + * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串 + * + * @param str 指定字符串 + * @param strs 需要检查的字符串数组 + * @return 是否匹配 + */ + public static boolean matches(String str, List<String> strs) + { + if (isEmpty(str) || isEmpty(strs)) + { + return false; + } + for (String pattern : strs) + { + if (isMatch(pattern, str)) + { + return true; + } + } + return false; + } + + /** + * 判断url是否与规则配置: + * ? 表示单个字符; + * * 表示一层路径内的任意字符串,不可跨层级; + * ** 表示任意层路径; + * + * @param pattern 匹配规则 + * @param url 需要匹配的url + * @return + */ + public static boolean isMatch(String pattern, String url) + { + AntPathMatcher matcher = new AntPathMatcher(); + return matcher.match(pattern, url); + } + + @SuppressWarnings("unchecked") + public static <T> T cast(Object obj) + { + return (T) obj; + } + + /** + * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。 + * + * @param num 数字对象 + * @param size 字符串指定长度 + * @return 返回数字的字符串格式,该字符串为指定长度。 + */ + public static final String padl(final Number num, final int size) + { + return padl(num.toString(), size, '0'); + } + + /** + * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。 + * + * @param s 原始字符串 + * @param size 字符串指定长度 + * @param c 用于补齐的字符 + * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。 + */ + public static final String padl(final String s, final int size, final char c) + { + final StringBuilder sb = new StringBuilder(size); + if (s != null) + { + final int len = s.length(); + if (s.length() <= size) + { + for (int i = size - len; i > 0; i--) + { + sb.append(c); + } + sb.append(s); + } + else + { + return s.substring(len - size, len); + } + } + else + { + for (int i = size; i > 0; i--) + { + sb.append(c); + } + } + return sb.toString(); + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/TencentMailUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/TencentMailUtil.java new file mode 100644 index 0000000..a6e40ba --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/TencentMailUtil.java @@ -0,0 +1,203 @@ +package com.ruoyi.common.utils; + +import com.ruoyi.common.config.MailProperties; +import com.ruoyi.common.exception.ServiceException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.activation.DataHandler; +import javax.activation.FileDataSource; +import java.net.URLEncoder; +import javax.activation.URLDataSource; +import javax.mail.*; +import javax.mail.internet.*; +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Properties; +import java.util.concurrent.CompletableFuture; + +@Component +@Slf4j +public class TencentMailUtil { + + @Autowired + MailProperties properties; + + public MailProperties getPro() { + return properties; + } + + /** + * + * @param emailAddress 邮件接收者email地址 + * @param param 用户房屋地址参数 + */ + public void send(String emailAddress,String param){ + // 配置发送邮件的环境属性 + final Properties props = new Properties(); + // 表示SMTP发送邮件,需要进行身份验证 + props.put("mail.smtp.auth", "true"); + props.put("mail.smtp.host", properties.getSmtpHost()); + // 如果使用ssl,则去掉使用25端口的配置,进行如下配置, + props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); + props.put("mail.smtp.socketFactory.port", properties.getSmtpPort()); + props.put("mail.smtp.port", properties.getSmtpPort()); + // 发件人的账号,填写控制台配置的发信地址,比如xxx@xxx.com + props.put("mail.user", properties.getUserAddr()); + // 访问SMTP服务时需要提供的密码(在控制台选择发信地址进行设置) + props.put("mail.password", properties.getPassword()); + props.setProperty("mail.smtp.socketFactory.fallback", "false"); + props.put("mail.smtp.ssl.enable", "true"); + props.put("mail.smtp.ssl.protocols", "TLSv1.2"); + // 构建授权信息,用于进行SMTP进行身份验证 + Authenticator authenticator = new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + // 用户名、密码 + String userName = props.getProperty("mail.user"); + String password = props.getProperty("mail.password"); + return new PasswordAuthentication(userName, password); + } + }; + // 使用环境属性和授权信息,创建邮件会话 + Session mailSession = Session.getInstance(props, authenticator); +// mailSession.setDebug(true); + //UUID uuid = UUID.randomUUID(); + //final String messageIDValue = "<" + uuid.toString() + ">"; + // 创建邮件消息 + MimeMessage message = new MimeMessage(mailSession) { + //@Override + //protected void updateMessageID() throws MessagingException { + //设置自定义Message-ID值 + //setHeader("Message-ID", messageIDValue); + //} + }; + try { + // 设置发件人邮件地址和名称。填写控制台配置的发信地址,比如xxx@xxx.com。和上面的mail.user保持一致。名称用户可以自定义填写。 + InternetAddress from = new InternetAddress(properties.getUserAddr(), properties.getUserName()); + message.setFrom(from); + //可选。设置回信地址 +// Address[] a = new Address[1]; +// a[0] = new InternetAddress("***"); +// message.setReplyTo(a); + // 设置收件人邮件地址,比如yyy@yyy.com + InternetAddress to = new InternetAddress(emailAddress); + message.setRecipient(MimeMessage.RecipientType.TO, to); + //如果同时发给多人,才将上面两行替换为如下(因为部分收信系统的一些限制,尽量每次投递给一个人;同时我们限制单次允许发送的人数是50人): + // 设置邮件标题 + message.setSubject("您的"+param+"账单提醒"); + message.setHeader("Content-Transfer-Encoding", "base64"); + // 设置邮件的内容体 type: text/plain(纯文本)text/html(HTML 文档) + message.setContent("邻居您好!您"+param+",提醒您有账单需要处理,如已处理请忽略此短信,感谢您的支持!如有疑问请详询:4008888888。", "text/html;charset=UTF-8"); + //发送邮件 + Transport.send(message); + } catch (MessagingException | UnsupportedEncodingException e) { + e.printStackTrace(); + log.error("发送邮件发生异常",e); + throw new ServiceException("发送邮件失败,请检查"); + }catch (Exception e){ + e.printStackTrace(); + log.error("发送邮件发生异常",e); + } + } + + public void sendInvoice(String emailAddress, List<Map<String, String>> list) { + // 异步发送邮件 + CompletableFuture.runAsync(() -> { + try { + sendEmail(emailAddress, list); + } catch (ServiceException e) { + log.error("邮件发送失败", e); + } + }); + } + + private void sendEmail(String emailAddress, List<Map<String, String>> list) throws ServiceException { + try { + // 创建邮件会话 + Session mailSession = Session.getInstance(getMailProperties(), getAuthenticator()); + // 创建邮件消息 + MimeMessage message = new MimeMessage(mailSession); + // 设置发件人 + InternetAddress from = new InternetAddress(properties.getUserAddr(), properties.getUserName()); + message.setFrom(from); + // 设置收件人 + InternetAddress to = new InternetAddress(emailAddress); + message.setRecipient(MimeMessage.RecipientType.TO, to); + // 设置邮件标题 + message.setSubject("发票"); + // 创建邮件内容 + Multipart multipart = createMultipart(list); + // 设置邮件内容 + message.setContent(multipart); + // 发送邮件 + Transport.send(message); + } catch (MessagingException | UnsupportedEncodingException | MalformedURLException e) { + log.error("发送邮件发生异常", e); + throw new ServiceException("发送邮件失败, 请检查"); + } + } + + private Properties getMailProperties() { + Properties props = new Properties(); + props.put("mail.smtp.auth", "true"); + props.put("mail.smtp.host", properties.getSmtpHost()); + props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); + props.put("mail.smtp.socketFactory.port", properties.getSmtpPort()); + props.put("mail.smtp.port", properties.getSmtpPort()); + props.put("mail.user", properties.getUserAddr()); + props.put("mail.password", properties.getPassword()); + props.setProperty("mail.smtp.socketFactory.fallback", "false"); + props.put("mail.smtp.ssl.enable", "true"); + props.put("mail.smtp.ssl.protocols", "TLSv1.2"); + return props; + } + + private Authenticator getAuthenticator() { + return new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + String userName = properties.getUserAddr(); + String password = properties.getPassword(); + return new PasswordAuthentication(userName, password); + } + }; + } + + private Multipart createMultipart(List<Map<String, String>> list) throws MessagingException, UnsupportedEncodingException, MalformedURLException { + Multipart multipart = new MimeMultipart(); + // 添加文本消息部分 + BodyPart messageBodyPart = new MimeBodyPart(); + messageBodyPart.setHeader("Content-Type", "text/plain;charset=utf-8"); + messageBodyPart.setContent("您在小程序提交的开票申请已开票成功,请查看附件内容","text/html;charset=UTF-8"); + multipart.addBodyPart(messageBodyPart); + // 添加附件部分 + for (Map<String, String> map : list) { + messageBodyPart = new MimeBodyPart(); + String url = map.get("url"); + String fileName = map.get("fileName"); + URLDataSource source = new URLDataSource(new URL(url)); + messageBodyPart.setDataHandler(new DataHandler(source)); + String filenameEncode = MimeUtility.encodeText(fileName, "UTF-8", "base64"); + messageBodyPart.setFileName(filenameEncode); + messageBodyPart.setHeader("Content-Transfer-Encoding", "base64"); + messageBodyPart.setHeader("Content-Disposition", "attachment"); + messageBodyPart.setHeader("Content-Type", "application/octet-stream;name=\"" + filenameEncode + "\""); + multipart.addBodyPart(messageBodyPart); + } + + return multipart; + } + + // public static void main(String[] args) throws UnsupportedEncodingException { + // TencentMailUtil tencentMailUtil = new TencentMailUtil(); + // MailProperties properties = new MailProperties(); + // tencentMailUtil.properties = properties; + // tencentMailUtil.send("645025773@qq.com","大学城揽院24栋"); + // } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/Threads.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/Threads.java new file mode 100644 index 0000000..71fe6d5 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/Threads.java @@ -0,0 +1,99 @@ +package com.ruoyi.common.utils; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 线程相关工具类. + * + * @author ruoyi + */ +public class Threads +{ + private static final Logger logger = LoggerFactory.getLogger(Threads.class); + + /** + * sleep等待,单位为毫秒 + */ + public static void sleep(long milliseconds) + { + try + { + Thread.sleep(milliseconds); + } + catch (InterruptedException e) + { + return; + } + } + + /** + * 停止线程池 + * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务. + * 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数. + * 如果仍然超時,則強制退出. + * 另对在shutdown时线程本身被调用中断做了处理. + */ + public static void shutdownAndAwaitTermination(ExecutorService pool) + { + if (pool != null && !pool.isShutdown()) + { + pool.shutdown(); + try + { + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) + { + pool.shutdownNow(); + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) + { + logger.info("Pool did not terminate"); + } + } + } + catch (InterruptedException ie) + { + pool.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + } + + /** + * 打印线程异常信息 + */ + public static void printException(Runnable r, Throwable t) + { + if (t == null && r instanceof Future<?>) + { + try + { + Future<?> future = (Future<?>) r; + if (future.isDone()) + { + future.get(); + } + } + catch (CancellationException ce) + { + t = ce; + } + catch (ExecutionException ee) + { + t = ee.getCause(); + } + catch (InterruptedException ie) + { + Thread.currentThread().interrupt(); + } + } + if (t != null) + { + logger.error(t.getMessage(), t); + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/TimeConverter.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/TimeConverter.java new file mode 100644 index 0000000..6715f60 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/TimeConverter.java @@ -0,0 +1,19 @@ +package com.ruoyi.common.utils; + +import java.util.concurrent.TimeUnit; + +public class TimeConverter { + + public static String convertSecondsToHMS(Long seconds) { + long hours = TimeUnit.SECONDS.toHours(seconds); + long minutes = TimeUnit.SECONDS.toMinutes(seconds) % 60; + long remainingSeconds = seconds % 60; + return String.format("%02d",hours) + ":" + String.format("%02d",minutes) + ":" + String.format("%02d",remainingSeconds); + } + + public static void main(String[] args) { + Long totalSeconds = 44871L; + String hms = convertSecondsToHMS(totalSeconds); + System.out.println(hms); + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/VideoUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/VideoUtil.java new file mode 100644 index 0000000..4b6b739 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/VideoUtil.java @@ -0,0 +1,69 @@ +package com.ruoyi.common.utils; + + +import lombok.extern.slf4j.Slf4j; +import ws.schild.jave.MultimediaInfo; +import ws.schild.jave.MultimediaObject; + +import java.io.File; +import java.net.URL; +import java.util.Arrays; + +@Slf4j +public class VideoUtil { + + + /** + * 视频时长 + * + * @param fileUrl + * @return String[] 0=秒时长,1=展示时长(格式如 01:00:00) + */ + public static String[] parseDuration(String fileUrl) { + String[] length = new String[2]; + try { + // +// URL source = new URL(fileUrl); + // 构造方法 接受URL对象 +// MultimediaObject instance = new MultimediaObject(source); + // 构造方法 接受File对象 + MultimediaObject instance = new MultimediaObject(new File(fileUrl)); + MultimediaInfo result = instance.getInfo(); + Long ls = result.getDuration() / 1000; + length[0] = String.valueOf(ls); + Integer hour = (int) (ls / 3600); + Integer minute = (int) (ls % 3600) / 60; + Integer second = (int) (ls - hour * 3600 - minute * 60); + String hr = hour.toString(); + String mi = minute.toString(); + String se = second.toString(); + if (hr.length() < 2) { + hr = "0" + hr; + } + if (mi.length() < 2) { + mi = "0" + mi; + } + if (se.length() < 2) { + se = "0" + se; + } + + String noHour = "00"; + if (noHour.equals(hr)) { + length[1] = mi + ":" + se; + } else { + length[1] = hr + ":" + mi + ":" + se; + } + + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return length; + } + + public static void main(String[] args) { + String url = "C:/Users/Admin/Desktop/qrcode/2023-10-25/video.mp4"; + String[] strings = parseDuration(url); + System.err.println(Arrays.toString(strings)); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/WebUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/WebUtils.java new file mode 100644 index 0000000..7397e87 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/WebUtils.java @@ -0,0 +1,170 @@ +package com.ruoyi.common.utils; + + +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.util.HashMap; +import java.util.Map; + + +/** + * web工具类 + * + * @author liheng + */ +public final class WebUtils extends org.springframework.web.util.WebUtils { + + /** + * 当前请求 + */ + public static HttpServletRequest request() { + return contextHolder() == null ? null : contextHolder().getRequest(); + } + + /** + * 当前响应 + */ + public static HttpServletResponse response() { + return contextHolder() == null ? null : contextHolder().getResponse(); + } + + /** + * 当前session + */ + public static HttpSession session() { + return request() == null ? null : request().getSession(); + } + + /** + * 当前ServletRequest + */ + public static ServletRequestAttributes contextHolder() { + return (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + } + + /** + * 判断请求是否为 AJAX + * + * @param request 当前请求 + */ + public static boolean isAjax(HttpServletRequest request) { + return "XMLHttpRequest".equals(request.getHeader("X-Requested-With")); + } + + /** + * 获取操作系统,浏览器及浏览器版本信息 + * + * @param request + * @return + */ + public static Map<String, String> getOsAndBrowserInfo(HttpServletRequest request) { + Map<String, String> map = new HashMap<>(2); + String browserDetails = request.getHeader("User-Agent"); + String userAgent = browserDetails; + String user = userAgent.toLowerCase(); + + String os = ""; + String browser = ""; + //=================OS Info======================= + if (userAgent.toLowerCase().contains("windows")) { + os = "Windows"; + } else if (userAgent.toLowerCase().contains("mac")) { + os = "Mac"; + } else if (userAgent.toLowerCase().contains("x11")) { + os = "Unix"; + } else if (userAgent.toLowerCase().contains("android")) { + os = "Android"; + } else if (userAgent.toLowerCase().contains("iphone")) { + os = "IPhone"; + } else { + os = "UnKnown, More-Info: " + userAgent; + } + //===============Browser=========================== + if (user.contains("edge")) { + browser = (userAgent.substring(userAgent.indexOf("Edge")).split(" ")[0]).replace("/", "-"); + } else if (user.contains("msie")) { + String substring = userAgent.substring(userAgent.indexOf("MSIE")).split(";")[0]; + browser = substring.split(" ")[0].replace("MSIE", "IE") + "-" + substring.split(" ")[1]; + } else if (user.contains("safari") && user.contains("version")) { + browser = (userAgent.substring(userAgent.indexOf("Safari")).split(" ")[0]).split("/")[0] + + "-" + (userAgent.substring(userAgent.indexOf("Version")).split(" ")[0]).split("/")[1]; + } else if (user.contains("opr") || user.contains("opera")) { + if (user.contains("opera")) { + browser = (userAgent.substring(userAgent.indexOf("Opera")).split(" ")[0]).split("/")[0] + + "-" + (userAgent.substring(userAgent.indexOf("Version")).split(" ")[0]).split("/")[1]; + } else if (user.contains("opr")) { + browser = ((userAgent.substring(userAgent.indexOf("OPR")).split(" ")[0]).replace("/", "-")) + .replace("OPR", "Opera"); + } + } else if (user.contains("chrome")) { + browser = (userAgent.substring(userAgent.indexOf("Chrome")).split(" ")[0]).replace("/", "-"); + } else if ((user.contains("mozilla/7.0")) || (user.contains("netscape6")) || + (user.contains("mozilla/4.7")) || (user.contains("mozilla/4.78")) || + (user.contains("mozilla/4.08")) || (user.contains("mozilla/3"))) { + browser = "Netscape-?"; + } else if (user.contains("firefox")) { + browser = (userAgent.substring(userAgent.indexOf("Firefox")).split(" ")[0]).replace("/", "-"); + } else if (user.contains("rv")) { + String IEVersion = (userAgent.substring(userAgent.indexOf("rv")).split(" ")[0]).replace("rv:", "-"); + browser = "IE" + IEVersion.substring(0, IEVersion.length() - 1); + } else { + browser = "UnKnown, More-Info: " + userAgent; + } + map.put("os", os); + map.put("browser", browser); + return map; + } + + /** + * 读取cookie + * + * @param name cookie name + * @return cookie value + */ + public String getCookieVal(String name) { + return getCookieVal(request(), name); + } + + /** + * 读取cookie + * + * @param request HttpServletRequest + * @param name cookie name + * @return cookie value + */ + public String getCookieVal(HttpServletRequest request, String name) { + Cookie cookie = getCookie(request, name); + return cookie != null ? cookie.getValue() : null; + } + + /** + * 清除 某个指定的cookie + * + * @param response HttpServletResponse + * @param key cookie key + */ + public void removeCookie(HttpServletResponse response, String key) { + setCookie(response, key, null, 0); + } + + /** + * 设置cookie + * + * @param response HttpServletResponse + * @param name cookie name + * @param value cookie value + * @param maxAgeInSeconds maxage + */ + public void setCookie(HttpServletResponse response, String name, String value, int maxAgeInSeconds) { + Cookie cookie = new Cookie(name, value); + cookie.setPath("/"); + cookie.setMaxAge(maxAgeInSeconds); + //cookie.setHttpOnly(true); + response.addCookie(cookie); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/WxAppletTools.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/WxAppletTools.java new file mode 100644 index 0000000..7c6ee85 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/WxAppletTools.java @@ -0,0 +1,77 @@ +package com.ruoyi.common.utils; + +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.common.config.WxConfig; +import com.ruoyi.common.core.redis.RedisCache; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import org.springframework.web.client.RestTemplate; + +import java.text.MessageFormat; + +/** + * @author liheng + * @ClassName WxAppletTools + * @Description + * @date 2020-12-04 13:55 + */ +@Slf4j +@Component +public class WxAppletTools { + @Autowired + private RedisCache redisCache; + @Autowired + private RestTemplate restTemplate; + @Autowired + private WxConfig wxConfig; + private final static String ACCESSTOKEN_CACHE_KEY = "accessToken"; + /** + * 请求参数 + * 属性 类型 默认值 必填 说明 + * appid string 是 小程序 appId + * secret string 是 小程序 appSecret + * js_code string 是 登录时获取的 code + * grant_type string 是 授权类型,此处只需填写 authorization_cod + * <p> + * 返回值: + * <p> + * 属性 类型 说明 + * openid string 用户唯一标识 + * session_key string 会话密钥 + * unionid string 用户在开放平台的唯一标识符,若当前小程序已绑定到微信开放平台帐号下会返回,详见 UnionID 机制说明。 + * errcode number 错误码 + * errmsg string 错误信息 + */ + private static final String JSCODE_2_SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code"; + + + /** + * 请求参数 + * 属性 类型 默认值 必填 说明 + * grant_type string 是 填写 client_credential + * appid string 是 小程序唯一凭证,即 AppID,可在「微信公众平台 - 设置 - 开发设置」页中获得。(需要已经成为开发者,且帐号没有异常状态) + * secret string 是 小程序唯一凭证密钥,即 AppSecret,获取方式同 appid + * 返回值 + * Object + * 返回的 JSON 数据包 + * <p> + * 属性 类型 说明 + * access_token string 获取到的凭证 + * expires_in number 凭证有效时间,单位:秒。目前是7200秒之内的值。 + * errcode number 错误码 + * errmsg string 错误信息 + */ + private static String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}"; + + /** + * @return + */ + public String getAccessToken() { + String requestUrl = MessageFormat.format(ACCESS_TOKEN_URL, wxConfig.getAppId(), wxConfig.getSecret()); + String respBody = restTemplate.getForEntity(requestUrl, String.class).getBody(); + JSONObject jsonObject = JSONObject.parseObject(respBody); + return jsonObject.getString("access_token"); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java new file mode 100644 index 0000000..4463662 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java @@ -0,0 +1,110 @@ +package com.ruoyi.common.utils.bean; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Bean 工具类 + * + * @author ruoyi + */ +public class BeanUtils extends org.springframework.beans.BeanUtils +{ + /** Bean方法名中属性名开始的下标 */ + private static final int BEAN_METHOD_PROP_INDEX = 3; + + /** * 匹配getter方法的正则表达式 */ + private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)"); + + /** * 匹配setter方法的正则表达式 */ + private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)"); + + /** + * Bean属性复制工具方法。 + * + * @param dest 目标对象 + * @param src 源对象 + */ + public static void copyBeanProp(Object dest, Object src) + { + try + { + copyProperties(src, dest); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + /** + * 获取对象的setter方法。 + * + * @param obj 对象 + * @return 对象的setter方法列表 + */ + public static List<Method> getSetterMethods(Object obj) + { + // setter方法列表 + List<Method> setterMethods = new ArrayList<Method>(); + + // 获取所有方法 + Method[] methods = obj.getClass().getMethods(); + + // 查找setter方法 + + for (Method method : methods) + { + Matcher m = SET_PATTERN.matcher(method.getName()); + if (m.matches() && (method.getParameterTypes().length == 1)) + { + setterMethods.add(method); + } + } + // 返回setter方法列表 + return setterMethods; + } + + /** + * 获取对象的getter方法。 + * + * @param obj 对象 + * @return 对象的getter方法列表 + */ + + public static List<Method> getGetterMethods(Object obj) + { + // getter方法列表 + List<Method> getterMethods = new ArrayList<Method>(); + // 获取所有方法 + Method[] methods = obj.getClass().getMethods(); + // 查找getter方法 + for (Method method : methods) + { + Matcher m = GET_PATTERN.matcher(method.getName()); + if (m.matches() && (method.getParameterTypes().length == 0)) + { + getterMethods.add(method); + } + } + // 返回getter方法列表 + return getterMethods; + } + + /** + * 检查Bean方法名中的属性名是否相等。<br> + * 如getName()和setName()属性名一样,getName()和setAge()属性名不一样。 + * + * @param m1 方法名1 + * @param m2 方法名2 + * @return 属性名一样返回true,否则返回false + */ + + public static boolean isMethodPropEquals(String m1, String m2) + { + return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX)); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java new file mode 100644 index 0000000..80bfed7 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java @@ -0,0 +1,24 @@ +package com.ruoyi.common.utils.bean; + +import java.util.Set; +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.validation.Validator; + +/** + * bean对象属性验证 + * + * @author ruoyi + */ +public class BeanValidators +{ + public static void validateWithException(Validator validator, Object object, Class<?>... groups) + throws ConstraintViolationException + { + Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups); + if (!constraintViolations.isEmpty()) + { + throw new ConstraintViolationException(constraintViolations); + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java new file mode 100644 index 0000000..68130b9 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java @@ -0,0 +1,76 @@ +package com.ruoyi.common.utils.file; + +import java.io.File; +import org.apache.commons.lang3.StringUtils; + +/** + * 文件类型工具类 + * + * @author ruoyi + */ +public class FileTypeUtils +{ + /** + * 获取文件类型 + * <p> + * 例如: ruoyi.txt, 返回: txt + * + * @param file 文件名 + * @return 后缀(不含".") + */ + public static String getFileType(File file) + { + if (null == file) + { + return StringUtils.EMPTY; + } + return getFileType(file.getName()); + } + + /** + * 获取文件类型 + * <p> + * 例如: ruoyi.txt, 返回: txt + * + * @param fileName 文件名 + * @return 后缀(不含".") + */ + public static String getFileType(String fileName) + { + int separatorIndex = fileName.lastIndexOf("."); + if (separatorIndex < 0) + { + return ""; + } + return fileName.substring(separatorIndex + 1).toLowerCase(); + } + + /** + * 获取文件类型 + * + * @param photoByte 文件字节码 + * @return 后缀(不含".") + */ + public static String getFileExtendName(byte[] photoByte) + { + String strFileExtendName = "JPG"; + if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) + && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) + { + strFileExtendName = "GIF"; + } + else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) + { + strFileExtendName = "JPG"; + } + else if ((photoByte[0] == 66) && (photoByte[1] == 77)) + { + strFileExtendName = "BMP"; + } + else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) + { + strFileExtendName = "PNG"; + } + return strFileExtendName; + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java new file mode 100644 index 0000000..d9f2b13 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java @@ -0,0 +1,232 @@ +package com.ruoyi.common.utils.file; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.Objects; +import org.apache.commons.io.FilenameUtils; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.exception.file.FileNameLengthLimitExceededException; +import com.ruoyi.common.exception.file.FileSizeLimitExceededException; +import com.ruoyi.common.exception.file.InvalidExtensionException; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.uuid.Seq; + +/** + * 文件上传工具类 + * + * @author ruoyi + */ +public class FileUploadUtils +{ + /** + * 默认大小 50M + */ + public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024; + + /** + * 默认的文件名最大长度 100 + */ + public static final int DEFAULT_FILE_NAME_LENGTH = 100; + + /** + * 默认上传的地址 + */ + private static String defaultBaseDir = RuoYiConfig.getProfile(); + + public static void setDefaultBaseDir(String defaultBaseDir) + { + FileUploadUtils.defaultBaseDir = defaultBaseDir; + } + + public static String getDefaultBaseDir() + { + return defaultBaseDir; + } + + /** + * 以默认配置进行文件上传 + * + * @param file 上传的文件 + * @return 文件名称 + * @throws Exception + */ + public static final String upload(MultipartFile file) throws IOException + { + try + { + return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); + } + catch (Exception e) + { + throw new IOException(e.getMessage(), e); + } + } + + /** + * 根据文件路径上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @return 文件名称 + * @throws IOException + */ + public static final String upload(String baseDir, MultipartFile file) throws IOException + { + try + { + return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); + } + catch (Exception e) + { + throw new IOException(e.getMessage(), e); + } + } + + /** + * 文件上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @param allowedExtension 上传文件类型 + * @return 返回上传成功的文件名 + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws FileNameLengthLimitExceededException 文件名太长 + * @throws IOException 比如读写文件出错时 + * @throws InvalidExtensionException 文件校验异常 + */ + public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, + InvalidExtensionException + { + int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length(); + if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) + { + throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH); + } + + assertAllowed(file, allowedExtension); + + String fileName = extractFilename(file); + + String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath(); + file.transferTo(Paths.get(absPath)); + return getPathFileName(baseDir, fileName); + } + + /** + * 编码文件名 + */ + public static final String extractFilename(MultipartFile file) + { + return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(), + FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file)); + } + + public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException + { + File desc = new File(uploadDir + File.separator + fileName); + + if (!desc.exists()) + { + if (!desc.getParentFile().exists()) + { + desc.getParentFile().mkdirs(); + } + } + return desc; + } + + public static final String getPathFileName(String uploadDir, String fileName) throws IOException + { + int dirLastIndex = RuoYiConfig.getProfile().length() + 1; + String currentDir = StringUtils.substring(uploadDir, dirLastIndex); + return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName; + } + + /** + * 文件大小校验 + * + * @param file 上传的文件 + * @return + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws InvalidExtensionException + */ + public static final void assertAllowed(MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, InvalidExtensionException + { + long size = file.getSize(); + if (size > DEFAULT_MAX_SIZE) + { + throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024); + } + + String fileName = file.getOriginalFilename(); + String extension = getExtension(file); + if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) + { + if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) + { + throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) + { + throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) + { + throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) + { + throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension, + fileName); + } + else + { + throw new InvalidExtensionException(allowedExtension, extension, fileName); + } + } + } + + /** + * 判断MIME类型是否是允许的MIME类型 + * + * @param extension + * @param allowedExtension + * @return + */ + public static final boolean isAllowedExtension(String extension, String[] allowedExtension) + { + for (String str : allowedExtension) + { + if (str.equalsIgnoreCase(extension)) + { + return true; + } + } + return false; + } + + /** + * 获取文件名的后缀 + * + * @param file 表单文件 + * @return 后缀名 + */ + public static final String getExtension(MultipartFile file) + { + String extension = FilenameUtils.getExtension(file.getOriginalFilename()); + if (StringUtils.isEmpty(extension)) + { + extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType())); + } + return extension; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java new file mode 100644 index 0000000..ed4cbc9 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java @@ -0,0 +1,291 @@ +package com.ruoyi.common.utils.file; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.ArrayUtils; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.uuid.IdUtils; +import org.apache.commons.io.FilenameUtils; + +/** + * 文件处理工具类 + * + * @author ruoyi + */ +public class FileUtils +{ + 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 + { + IOUtils.close(os); + IOUtils.close(fis); + } + } + + /** + * 写数据到文件中 + * + * @param data 数据 + * @return 目标文件 + * @throws IOException IO异常 + */ + public static String writeImportBytes(byte[] data) throws IOException + { + return writeBytes(data, RuoYiConfig.getImportPath()); + } + + /** + * 写数据到文件中 + * + * @param data 数据 + * @param uploadDir 目标文件 + * @return 目标文件 + * @throws IOException IO异常 + */ + public static String writeBytes(byte[] data, String uploadDir) throws IOException + { + FileOutputStream fos = null; + String pathName = ""; + try + { + String extension = getFileExtendName(data); + pathName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension; + File file = FileUploadUtils.getAbsoluteFile(uploadDir, pathName); + fos = new FileOutputStream(file); + fos.write(data); + } + finally + { + IOUtils.close(fos); + } + return FileUploadUtils.getPathFileName(uploadDir, pathName); + } + + /** + * 删除文件 + * + * @param filePath 文件 + * @return + */ + public static boolean deleteFile(String filePath) + { + boolean flag = false; + File file = new File(filePath); + // 路径为文件且不为空则进行删除 + if (file.isFile() && file.exists()) + { + flag = file.delete(); + } + return flag; + } + + /** + * 文件名称验证 + * + * @param filename 文件名称 + * @return true 正常 false 非法 + */ + public static boolean isValidFilename(String filename) + { + return filename.matches(FILENAME_PATTERN); + } + + /** + * 检查文件是否可下载 + * + * @param resource 需要下载的文件 + * @return true 正常 false 非法 + */ + public static boolean checkAllowDownload(String resource) + { + // 禁止目录上跳级别 + if (StringUtils.contains(resource, "..")) + { + return false; + } + + // 检查允许下载的文件规则 + if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource))) + { + return true; + } + + // 不在允许下载的文件规则 + return false; + } + + /** + * 下载文件名重新编码 + * + * @param request 请求对象 + * @param fileName 文件名 + * @return 编码后的文件名 + */ + public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException + { + final String agent = request.getHeader("USER-AGENT"); + String filename = fileName; + if (agent.contains("MSIE")) + { + // IE浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + filename = filename.replace("+", " "); + } + else if (agent.contains("Firefox")) + { + // 火狐浏览器 + filename = new String(fileName.getBytes(), "ISO8859-1"); + } + else if (agent.contains("Chrome")) + { + // google浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + } + else + { + // 其它浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + } + return filename; + } + + /** + * 下载文件名重新编码 + * + * @param response 响应对象 + * @param realFileName 真实文件名 + */ + public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException + { + String percentEncodedFileName = percentEncode(realFileName); + + StringBuilder contentDispositionValue = new StringBuilder(); + contentDispositionValue.append("attachment; filename=") + .append(percentEncodedFileName) + .append(";") + .append("filename*=") + .append("utf-8''") + .append(percentEncodedFileName); + + response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename"); + response.setHeader("Content-disposition", contentDispositionValue.toString()); + response.setHeader("download-filename", percentEncodedFileName); + } + + /** + * 百分号编码工具方法 + * + * @param s 需要百分号编码的字符串 + * @return 百分号编码后的字符串 + */ + public static String percentEncode(String s) throws UnsupportedEncodingException + { + String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); + return encode.replaceAll("\\+", "%20"); + } + + /** + * 获取图像后缀 + * + * @param photoByte 图像数据 + * @return 后缀名 + */ + public static String getFileExtendName(byte[] photoByte) + { + String strFileExtendName = "jpg"; + if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) + && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) + { + strFileExtendName = "gif"; + } + else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) + { + strFileExtendName = "jpg"; + } + else if ((photoByte[0] == 66) && (photoByte[1] == 77)) + { + strFileExtendName = "bmp"; + } + else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) + { + strFileExtendName = "png"; + } + return strFileExtendName; + } + + /** + * 获取文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi.png + * + * @param fileName 路径名称 + * @return 没有文件路径的名称 + */ + public static String getName(String fileName) + { + if (fileName == null) + { + return null; + } + int lastUnixPos = fileName.lastIndexOf('/'); + int lastWindowsPos = fileName.lastIndexOf('\\'); + int index = Math.max(lastUnixPos, lastWindowsPos); + return fileName.substring(index + 1); + } + + /** + * 获取不带后缀文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi + * + * @param fileName 路径名称 + * @return 没有文件路径和后缀的名称 + */ + public static String getNameNotSuffix(String fileName) + { + if (fileName == null) + { + return null; + } + String baseName = FilenameUtils.getBaseName(fileName); + return baseName; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java new file mode 100644 index 0000000..facaf74 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java @@ -0,0 +1,93 @@ +package com.ruoyi.common.utils.file; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.Arrays; + +/** + * 图片处理工具类 + * + * @author ruoyi + */ +public class ImageUtils +{ + private static final Logger log = LoggerFactory.getLogger(ImageUtils.class); + +// public static byte[] getImage(String imagePath) +// { +// InputStream is = getFile(imagePath); +// try +// { +// return IOUtils.toByteArray(is); +// } +// catch (Exception e) +// { +// log.error("图片加载异常 {}", e); +// return null; +// } +// finally +// { +// IOUtils.closeQuietly(is); +// } +// } + + public static InputStream getFile(String imagePath) + { + try + { + byte[] result = readFile(imagePath); + result = Arrays.copyOf(result, result.length); + return new ByteArrayInputStream(result); + } + catch (Exception e) + { + log.error("获取图片异常 {}", e); + } + return null; + } + + /** + * 读取文件为字节数据 + * + * @param url 地址 + * @return 字节数据 + */ + public static byte[] readFile(String url) + { +// InputStream in = null; +// try +// { +// if (url.startsWith("http")) +// { +// // 网络地址 +// URL urlObj = new URL(url); +// URLConnection urlConnection = urlObj.openConnection(); +// urlConnection.setConnectTimeout(30 * 1000); +// urlConnection.setReadTimeout(60 * 1000); +// urlConnection.setDoInput(true); +// in = urlConnection.getInputStream(); +// } +// else +// { +// // 本机地址 +// String localPath = RuoYiConfig.getProfile(); +// String downloadPath = localPath + StringUtils.substringAfter(url, Constants.RESOURCE_PREFIX); +// in = new FileInputStream(downloadPath); +// } +// return IOUtils.toByteArray(in); +// } +// catch (Exception e) +// { +// log.error("获取文件路径异常 {}", e); +// return null; +// } +// finally +// { +// IOUtils.closeQuietly(in); +// } + return null; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java new file mode 100644 index 0000000..f968f1a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java @@ -0,0 +1,59 @@ +package com.ruoyi.common.utils.file; + +/** + * 媒体类型工具类 + * + * @author ruoyi + */ +public class MimeTypeUtils +{ + public static final String IMAGE_PNG = "image/png"; + + public static final String IMAGE_JPG = "image/jpg"; + + public static final String IMAGE_JPEG = "image/jpeg"; + + public static final String IMAGE_BMP = "image/bmp"; + + public static final String IMAGE_GIF = "image/gif"; + + public static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" }; + + public static final String[] FLASH_EXTENSION = { "swf", "flv" }; + + public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg", + "asf", "rm", "rmvb" }; + + public static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb" }; + + public static final String[] DEFAULT_ALLOWED_EXTENSION = { + // 图片 + "bmp", "gif", "jpg", "jpeg", "png", + // word excel powerpoint + "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt", + // 压缩文件 + "rar", "zip", "gz", "bz2", + // 视频格式 + "mp4", "avi", "rmvb", + // pdf + "pdf" }; + + public static String getExtension(String prefix) + { + switch (prefix) + { + case IMAGE_PNG: + return "png"; + case IMAGE_JPG: + return "jpg"; + case IMAGE_JPEG: + return "jpeg"; + case IMAGE_BMP: + return "bmp"; + case IMAGE_GIF: + return "gif"; + default: + return ""; + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java new file mode 100644 index 0000000..f52e83e --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java @@ -0,0 +1,167 @@ +package com.ruoyi.common.utils.html; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 转义和反转义工具类 + * + * @author ruoyi + */ +public class EscapeUtil +{ + public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)"; + + private static final char[][] TEXT = new char[64][]; + + static + { + for (int i = 0; i < 64; i++) + { + TEXT[i] = new char[] { (char) i }; + } + + // special HTML characters + TEXT['\''] = "'".toCharArray(); // 单引号 + TEXT['"'] = """.toCharArray(); // 双引号 + TEXT['&'] = "&".toCharArray(); // &符 + TEXT['<'] = "<".toCharArray(); // 小于号 + TEXT['>'] = ">".toCharArray(); // 大于号 + } + + /** + * 转义文本中的HTML字符为安全的字符 + * + * @param text 被转义的文本 + * @return 转义后的文本 + */ + public static String escape(String text) + { + return encode(text); + } + + /** + * 还原被转义的HTML特殊字符 + * + * @param content 包含转义符的HTML内容 + * @return 转换后的字符串 + */ + public static String unescape(String content) + { + return decode(content); + } + + /** + * 清除所有HTML标签,但是不删除标签内的内容 + * + * @param content 文本 + * @return 清除标签后的文本 + */ + public static String clean(String content) + { + return new HTMLFilter().filter(content); + } + + /** + * Escape编码 + * + * @param text 被编码的文本 + * @return 编码后的字符 + */ + private static String encode(String text) + { + if (StringUtils.isEmpty(text)) + { + return StringUtils.EMPTY; + } + + final StringBuilder tmp = new StringBuilder(text.length() * 6); + char c; + for (int i = 0; i < text.length(); i++) + { + c = text.charAt(i); + if (c < 256) + { + tmp.append("%"); + if (c < 16) + { + tmp.append("0"); + } + tmp.append(Integer.toString(c, 16)); + } + else + { + tmp.append("%u"); + if (c <= 0xfff) + { + // issue#I49JU8@Gitee + tmp.append("0"); + } + tmp.append(Integer.toString(c, 16)); + } + } + return tmp.toString(); + } + + /** + * Escape解码 + * + * @param content 被转义的内容 + * @return 解码后的字符串 + */ + public static String decode(String content) + { + if (StringUtils.isEmpty(content)) + { + return content; + } + + StringBuilder tmp = new StringBuilder(content.length()); + int lastPos = 0, pos = 0; + char ch; + while (lastPos < content.length()) + { + pos = content.indexOf("%", lastPos); + if (pos == lastPos) + { + if (content.charAt(pos + 1) == 'u') + { + ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16); + tmp.append(ch); + lastPos = pos + 6; + } + else + { + ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16); + tmp.append(ch); + lastPos = pos + 3; + } + } + else + { + if (pos == -1) + { + tmp.append(content.substring(lastPos)); + lastPos = content.length(); + } + else + { + tmp.append(content.substring(lastPos, pos)); + lastPos = pos; + } + } + } + return tmp.toString(); + } + + public static void main(String[] args) + { + String html = "<script>alert(1);</script>"; + String escape = EscapeUtil.escape(html); + // String html = "<scr<script>ipt>alert(\"XSS\")</scr<script>ipt>"; + // String html = "<123"; + // String html = "123>"; + System.out.println("clean: " + EscapeUtil.clean(html)); + System.out.println("escape: " + escape); + System.out.println("unescape: " + EscapeUtil.unescape(escape)); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java new file mode 100644 index 0000000..ebff3fd --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java @@ -0,0 +1,570 @@ +package com.ruoyi.common.utils.html; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * HTML过滤器,用于去除XSS漏洞隐患。 + * + * @author ruoyi + */ +public final class HTMLFilter +{ + /** + * regex flag union representing /si modifiers in php + **/ + private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL; + private static final Pattern P_COMMENTS = Pattern.compile("<!--(.*?)-->", Pattern.DOTALL); + private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI); + private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL); + private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI); + private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI); + private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI); + private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI); + private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI); + private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?"); + private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?"); + private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?"); + private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))"); + private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL); + private static final Pattern P_END_ARROW = Pattern.compile("^>"); + private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_AMP = Pattern.compile("&"); + private static final Pattern P_QUOTE = Pattern.compile("\""); + private static final Pattern P_LEFT_ARROW = Pattern.compile("<"); + private static final Pattern P_RIGHT_ARROW = Pattern.compile(">"); + private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>"); + + // @xxx could grow large... maybe use sesat's ReferenceMap + private static final ConcurrentMap<String, Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>(); + private static final ConcurrentMap<String, Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>(); + + /** + * set of allowed html elements, along with allowed attributes for each element + **/ + private final Map<String, List<String>> vAllowed; + /** + * counts of open tags for each (allowable) html element + **/ + private final Map<String, Integer> vTagCounts = new HashMap<>(); + + /** + * html elements which must always be self-closing (e.g. "<img />") + **/ + private final String[] vSelfClosingTags; + /** + * html elements which must always have separate opening and closing tags (e.g. "<b></b>") + **/ + private final String[] vNeedClosingTags; + /** + * set of disallowed html elements + **/ + private final String[] vDisallowed; + /** + * attributes which should be checked for valid protocols + **/ + private final String[] vProtocolAtts; + /** + * allowed protocols + **/ + private final String[] vAllowedProtocols; + /** + * tags which should be removed if they contain no content (e.g. "<b></b>" or "<b />") + **/ + private final String[] vRemoveBlanks; + /** + * entities allowed within html markup + **/ + private final String[] vAllowedEntities; + /** + * flag determining whether comments are allowed in input String. + */ + private final boolean stripComment; + private final boolean encodeQuotes; + /** + * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "<b text </b>" + * becomes "<b> text </b>"). If set to false, unbalanced angle brackets will be html escaped. + */ + private final boolean alwaysMakeTags; + + /** + * Default constructor. + */ + public HTMLFilter() + { + vAllowed = new HashMap<>(); + + final ArrayList<String> a_atts = new ArrayList<>(); + a_atts.add("href"); + a_atts.add("target"); + vAllowed.put("a", a_atts); + + final ArrayList<String> img_atts = new ArrayList<>(); + img_atts.add("src"); + img_atts.add("width"); + img_atts.add("height"); + img_atts.add("alt"); + vAllowed.put("img", img_atts); + + final ArrayList<String> no_atts = new ArrayList<>(); + vAllowed.put("b", no_atts); + vAllowed.put("strong", no_atts); + vAllowed.put("i", no_atts); + vAllowed.put("em", no_atts); + + vSelfClosingTags = new String[] { "img" }; + vNeedClosingTags = new String[] { "a", "b", "strong", "i", "em" }; + vDisallowed = new String[] {}; + vAllowedProtocols = new String[] { "http", "mailto", "https" }; // no ftp. + vProtocolAtts = new String[] { "src", "href" }; + vRemoveBlanks = new String[] { "a", "b", "strong", "i", "em" }; + vAllowedEntities = new String[] { "amp", "gt", "lt", "quot" }; + stripComment = true; + encodeQuotes = true; + alwaysMakeTags = false; + } + + /** + * Map-parameter configurable constructor. + * + * @param conf map containing configuration. keys match field names. + */ + @SuppressWarnings("unchecked") + public HTMLFilter(final Map<String, Object> conf) + { + + assert conf.containsKey("vAllowed") : "configuration requires vAllowed"; + assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags"; + assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags"; + assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed"; + assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols"; + assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts"; + assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks"; + assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities"; + + vAllowed = Collections.unmodifiableMap((HashMap<String, List<String>>) conf.get("vAllowed")); + vSelfClosingTags = (String[]) conf.get("vSelfClosingTags"); + vNeedClosingTags = (String[]) conf.get("vNeedClosingTags"); + vDisallowed = (String[]) conf.get("vDisallowed"); + vAllowedProtocols = (String[]) conf.get("vAllowedProtocols"); + vProtocolAtts = (String[]) conf.get("vProtocolAtts"); + vRemoveBlanks = (String[]) conf.get("vRemoveBlanks"); + vAllowedEntities = (String[]) conf.get("vAllowedEntities"); + stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true; + encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true; + alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true; + } + + private void reset() + { + vTagCounts.clear(); + } + + // --------------------------------------------------------------- + // my versions of some PHP library functions + public static String chr(final int decimal) + { + return String.valueOf((char) decimal); + } + + public static String htmlSpecialChars(final String s) + { + String result = s; + result = regexReplace(P_AMP, "&", result); + result = regexReplace(P_QUOTE, """, result); + result = regexReplace(P_LEFT_ARROW, "<", result); + result = regexReplace(P_RIGHT_ARROW, ">", result); + return result; + } + + // --------------------------------------------------------------- + + /** + * given a user submitted input String, filter out any invalid or restricted html. + * + * @param input text (i.e. submitted by a user) than may contain html + * @return "clean" version of input, with only valid, whitelisted html elements allowed + */ + public String filter(final String input) + { + reset(); + String s = input; + + s = escapeComments(s); + + s = balanceHTML(s); + + s = checkTags(s); + + s = processRemoveBlanks(s); + + // s = validateEntities(s); + + return s; + } + + public boolean isAlwaysMakeTags() + { + return alwaysMakeTags; + } + + public boolean isStripComments() + { + return stripComment; + } + + private String escapeComments(final String s) + { + final Matcher m = P_COMMENTS.matcher(s); + final StringBuffer buf = new StringBuffer(); + if (m.find()) + { + final String match = m.group(1); // (.*?) + m.appendReplacement(buf, Matcher.quoteReplacement("<!--" + htmlSpecialChars(match) + "-->")); + } + m.appendTail(buf); + + return buf.toString(); + } + + private String balanceHTML(String s) + { + if (alwaysMakeTags) + { + // + // try and form html + // + s = regexReplace(P_END_ARROW, "", s); + // 不追加结束标签 + s = regexReplace(P_BODY_TO_END, "<$1>", s); + s = regexReplace(P_XML_CONTENT, "$1<$2", s); + + } + else + { + // + // escape stray brackets + // + s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s); + s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s); + + // + // the last regexp causes '<>' entities to appear + // (we need to do a lookahead assertion so that the last bracket can + // be used in the next pass of the regexp) + // + s = regexReplace(P_BOTH_ARROWS, "", s); + } + + return s; + } + + private String checkTags(String s) + { + Matcher m = P_TAGS.matcher(s); + + final StringBuffer buf = new StringBuffer(); + while (m.find()) + { + String replaceStr = m.group(1); + replaceStr = processTag(replaceStr); + m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr)); + } + m.appendTail(buf); + + // these get tallied in processTag + // (remember to reset before subsequent calls to filter method) + final StringBuilder sBuilder = new StringBuilder(buf.toString()); + for (String key : vTagCounts.keySet()) + { + for (int ii = 0; ii < vTagCounts.get(key); ii++) + { + sBuilder.append("</").append(key).append(">"); + } + } + s = sBuilder.toString(); + + return s; + } + + private String processRemoveBlanks(final String s) + { + String result = s; + for (String tag : vRemoveBlanks) + { + if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) + { + P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?></" + tag + ">")); + } + result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result); + if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) + { + P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>")); + } + result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result); + } + + return result; + } + + private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) + { + Matcher m = regex_pattern.matcher(s); + return m.replaceAll(replacement); + } + + private String processTag(final String s) + { + // ending tags + Matcher m = P_END_TAG.matcher(s); + if (m.find()) + { + final String name = m.group(1).toLowerCase(); + if (allowed(name)) + { + if (!inArray(name, vSelfClosingTags)) + { + if (vTagCounts.containsKey(name)) + { + vTagCounts.put(name, vTagCounts.get(name) - 1); + return "</" + name + ">"; + } + } + } + } + + // starting tags + m = P_START_TAG.matcher(s); + if (m.find()) + { + final String name = m.group(1).toLowerCase(); + final String body = m.group(2); + String ending = m.group(3); + + // debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" ); + if (allowed(name)) + { + final StringBuilder params = new StringBuilder(); + + final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body); + final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body); + final List<String> paramNames = new ArrayList<>(); + final List<String> paramValues = new ArrayList<>(); + while (m2.find()) + { + paramNames.add(m2.group(1)); // ([a-z0-9]+) + paramValues.add(m2.group(3)); // (.*?) + } + while (m3.find()) + { + paramNames.add(m3.group(1)); // ([a-z0-9]+) + paramValues.add(m3.group(3)); // ([^\"\\s']+) + } + + String paramName, paramValue; + for (int ii = 0; ii < paramNames.size(); ii++) + { + paramName = paramNames.get(ii).toLowerCase(); + paramValue = paramValues.get(ii); + + // debug( "paramName='" + paramName + "'" ); + // debug( "paramValue='" + paramValue + "'" ); + // debug( "allowed? " + vAllowed.get( name ).contains( paramName ) ); + + if (allowedAttribute(name, paramName)) + { + if (inArray(paramName, vProtocolAtts)) + { + paramValue = processParamProtocol(paramValue); + } + params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\\\""); + } + } + + if (inArray(name, vSelfClosingTags)) + { + ending = " /"; + } + + if (inArray(name, vNeedClosingTags)) + { + ending = ""; + } + + if (ending == null || ending.length() < 1) + { + if (vTagCounts.containsKey(name)) + { + vTagCounts.put(name, vTagCounts.get(name) + 1); + } + else + { + vTagCounts.put(name, 1); + } + } + else + { + ending = " /"; + } + return "<" + name + params + ending + ">"; + } + else + { + return ""; + } + } + + // comments + m = P_COMMENT.matcher(s); + if (!stripComment && m.find()) + { + return "<" + m.group() + ">"; + } + + return ""; + } + + private String processParamProtocol(String s) + { + s = decodeEntities(s); + final Matcher m = P_PROTOCOL.matcher(s); + if (m.find()) + { + final String protocol = m.group(1); + if (!inArray(protocol, vAllowedProtocols)) + { + // bad protocol, turn into local anchor link instead + s = "#" + s.substring(protocol.length() + 1); + if (s.startsWith("#//")) + { + s = "#" + s.substring(3); + } + } + } + + return s; + } + + private String decodeEntities(String s) + { + StringBuffer buf = new StringBuffer(); + + Matcher m = P_ENTITY.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.decode(match).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENTITY_UNICODE.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENCODE.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + s = validateEntities(s); + return s; + } + + private String validateEntities(final String s) + { + StringBuffer buf = new StringBuffer(); + + // validate entities throughout the string + Matcher m = P_VALID_ENTITIES.matcher(s); + while (m.find()) + { + final String one = m.group(1); // ([^&;]*) + final String two = m.group(2); // (?=(;|&|$)) + m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two))); + } + m.appendTail(buf); + + return encodeQuotes(buf.toString()); + } + + private String encodeQuotes(final String s) + { + if (encodeQuotes) + { + StringBuffer buf = new StringBuffer(); + Matcher m = P_VALID_QUOTES.matcher(s); + while (m.find()) + { + final String one = m.group(1); // (>|^) + final String two = m.group(2); // ([^<]+?) + final String three = m.group(3); // (<|$) + // 不替换双引号为",防止json格式无效 regexReplace(P_QUOTE, """, two) + m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three)); + } + m.appendTail(buf); + return buf.toString(); + } + else + { + return s; + } + } + + private String checkEntity(final String preamble, final String term) + { + + return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&" + preamble; + } + + private boolean isValidEntity(final String entity) + { + return inArray(entity, vAllowedEntities); + } + + private static boolean inArray(final String s, final String[] array) + { + for (String item : array) + { + if (item != null && item.equals(s)) + { + return true; + } + } + return false; + } + + private boolean allowed(final String name) + { + return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed); + } + + private boolean allowedAttribute(final String name, final String paramName) + { + return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName)); + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java new file mode 100644 index 0000000..589d123 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java @@ -0,0 +1,55 @@ +package com.ruoyi.common.utils.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import javax.servlet.ServletRequest; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 通用http工具封装 + * + * @author ruoyi + */ +public class HttpHelper +{ + private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class); + + public static String getBodyString(ServletRequest request) + { + StringBuilder sb = new StringBuilder(); + BufferedReader reader = null; + try (InputStream inputStream = request.getInputStream()) + { + reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + String line = ""; + while ((line = reader.readLine()) != null) + { + sb.append(line); + } + } + catch (IOException e) + { + LOGGER.warn("getBodyString出现问题!"); + } + finally + { + if (reader != null) + { + try + { + reader.close(); + } + catch (IOException e) + { + LOGGER.error(ExceptionUtils.getMessage(e)); + } + } + } + return sb.toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java new file mode 100644 index 0000000..f85c82c --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java @@ -0,0 +1,274 @@ +package com.ruoyi.common.utils.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.ConnectException; +import java.net.SocketTimeoutException; +import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.StandardCharsets; +import java.security.cert.X509Certificate; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.StringUtils; + +/** + * 通用http发送方法 + * + * @author ruoyi + */ +public class HttpUtils +{ + private static final Logger log = LoggerFactory.getLogger(HttpUtils.class); + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url) + { + return sendGet(url, StringUtils.EMPTY); + } + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url, String param) + { + return sendGet(url, param, Constants.UTF8); + } + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @param contentType 编码类型 + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url, String param, String contentType) + { + StringBuilder result = new StringBuilder(); + BufferedReader in = null; + try + { + String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url; + log.info("sendGet - {}", urlNameString); + URL realUrl = new URL(urlNameString); + URLConnection connection = realUrl.openConnection(); + connection.setRequestProperty("accept", "*/*"); + connection.setRequestProperty("connection", "Keep-Alive"); + connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + connection.connect(); + in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType)); + String line; + while ((line = in.readLine()) != null) + { + result.append(line); + } + log.info("recv - {}", result); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e); + } + finally + { + try + { + if (in != null) + { + in.close(); + } + } + catch (Exception ex) + { + log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); + } + } + return result.toString(); + } + + /** + * 向指定 URL 发送POST方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendPost(String url, String param) + { + PrintWriter out = null; + BufferedReader in = null; + StringBuilder result = new StringBuilder(); + try + { + log.info("sendPost - {}", url); + URL realUrl = new URL(url); + URLConnection conn = realUrl.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("contentType", "utf-8"); + conn.setDoOutput(true); + conn.setDoInput(true); + out = new PrintWriter(conn.getOutputStream()); + out.print(param); + out.flush(); + in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); + String line; + while ((line = in.readLine()) != null) + { + result.append(line); + } + log.info("recv - {}", result); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e); + } + finally + { + try + { + if (out != null) + { + out.close(); + } + if (in != null) + { + in.close(); + } + } + catch (IOException ex) + { + log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); + } + } + return result.toString(); + } + + public static String sendSSLPost(String url, String param) + { + StringBuilder result = new StringBuilder(); + String urlNameString = url + "?" + param; + try + { + log.info("sendSSLPost - {}", urlNameString); + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom()); + URL console = new URL(urlNameString); + HttpsURLConnection conn = (HttpsURLConnection) console.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("contentType", "utf-8"); + conn.setDoOutput(true); + conn.setDoInput(true); + + conn.setSSLSocketFactory(sc.getSocketFactory()); + conn.setHostnameVerifier(new TrustAnyHostnameVerifier()); + conn.connect(); + InputStream is = conn.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + String ret = ""; + while ((ret = br.readLine()) != null) + { + if (ret != null && !"".equals(ret.trim())) + { + result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8)); + } + } + log.info("recv - {}", result); + conn.disconnect(); + br.close(); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e); + } + return result.toString(); + } + + private static class TrustAnyTrustManager implements X509TrustManager + { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) + { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) + { + } + + @Override + public X509Certificate[] getAcceptedIssuers() + { + return new X509Certificate[] {}; + } + } + + private static class TrustAnyHostnameVerifier implements HostnameVerifier + { + @Override + public boolean verify(String hostname, SSLSession session) + { + return true; + } + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java new file mode 100644 index 0000000..edfe419 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java @@ -0,0 +1,56 @@ +package com.ruoyi.common.utils.ip; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.http.HttpUtils; + +/** + * 获取地址类 + * + * @author ruoyi + */ +public class AddressUtils +{ + private static final Logger log = LoggerFactory.getLogger(AddressUtils.class); + + // IP地址查询 + public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp"; + + // 未知地址 + public static final String UNKNOWN = "XX XX"; + + public static String getRealAddressByIP(String ip) + { + // 内网不查询 + if (IpUtils.internalIp(ip)) + { + return "内网IP"; + } + if (RuoYiConfig.isAddressEnabled()) + { + try + { + String rspStr = HttpUtils.sendGet(IP_URL, "ip=" + ip + "&json=true", Constants.GBK); + if (StringUtils.isEmpty(rspStr)) + { + log.error("获取地理位置异常 {}", ip); + return UNKNOWN; + } + JSONObject obj = JSON.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 UNKNOWN; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java new file mode 100644 index 0000000..8874697 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java @@ -0,0 +1,454 @@ +package com.ruoyi.common.utils.ip; + +import java.net.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import javax.servlet.http.HttpServletRequest; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import lombok.extern.slf4j.Slf4j; + +/** + * 获取IP方法 + * + * @author ruoyi + */ +@Slf4j +public class IpUtils +{ + public final static String REGX_0_255 = "(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)"; + // 匹配 ip + public final static String REGX_IP = "((" + REGX_0_255 + "\\.){3}" + REGX_0_255 + ")"; + public final static String REGX_IP_WILDCARD = "(((\\*\\.){3}\\*)|(" + REGX_0_255 + "(\\.\\*){3})|(" + REGX_0_255 + "\\." + REGX_0_255 + ")(\\.\\*){2}" + "|((" + REGX_0_255 + "\\.){3}\\*))"; + // 匹配网段 + public final static String REGX_IP_SEG = "(" + REGX_IP + "\\-" + REGX_IP + ")"; + + /** + * 获取客户端IP + * + * @return IP地址 + */ + public static String getIpAddr() + { + return getIpAddr(ServletUtils.getRequest()); + } + + /** + * 获取客户端IP + * + * @param request 请求对象 + * @return IP地址 + */ + public static String getIpAddr(HttpServletRequest request) + { + if (request == null) + { + return "unknown"; + } + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("X-Forwarded-For"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("X-Real-IP"); + } + + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getRemoteAddr(); + } + + return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip); + } + + /** + * 检查是否为内部IP地址 + * + * @param ip IP地址 + * @return 结果 + */ + public static boolean internalIp(String ip) + { + byte[] addr = textToNumericFormatV4(ip); + return internalIp(addr) || "127.0.0.1".equals(ip); + } + + /** + * 检查是否为内部IP地址 + * + * @param addr byte地址 + * @return 结果 + */ + private static boolean internalIp(byte[] addr) + { + if (StringUtils.isNull(addr) || addr.length < 2) + { + return true; + } + final byte b0 = addr[0]; + final byte b1 = addr[1]; + // 10.x.x.x/8 + final byte SECTION_1 = 0x0A; + // 172.16.x.x/12 + final byte SECTION_2 = (byte) 0xAC; + final byte SECTION_3 = (byte) 0x10; + final byte SECTION_4 = (byte) 0x1F; + // 192.168.x.x/16 + final byte SECTION_5 = (byte) 0xC0; + final byte SECTION_6 = (byte) 0xA8; + switch (b0) + { + case SECTION_1: + return true; + case SECTION_2: + if (b1 >= SECTION_3 && b1 <= SECTION_4) + { + return true; + } + case SECTION_5: + switch (b1) + { + case SECTION_6: + return true; + } + default: + return false; + } + } + + /** + * 将IPv4地址转换成字节 + * + * @param text IPv4地址 + * @return byte 字节 + */ + public static byte[] textToNumericFormatV4(String text) + { + if (text.length() == 0) + { + return null; + } + + byte[] bytes = new byte[4]; + String[] elements = text.split("\\.", -1); + try + { + long l; + int i; + switch (elements.length) + { + case 1: + l = Long.parseLong(elements[0]); + if ((l < 0L) || (l > 4294967295L)) + { + return null; + } + bytes[0] = (byte) (int) (l >> 24 & 0xFF); + bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 2: + l = Integer.parseInt(elements[0]); + if ((l < 0L) || (l > 255L)) + { + return null; + } + bytes[0] = (byte) (int) (l & 0xFF); + l = Integer.parseInt(elements[1]); + if ((l < 0L) || (l > 16777215L)) + { + return null; + } + bytes[1] = (byte) (int) (l >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 3: + for (i = 0; i < 2; ++i) + { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) + { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + l = Integer.parseInt(elements[2]); + if ((l < 0L) || (l > 65535L)) + { + return null; + } + bytes[2] = (byte) (int) (l >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 4: + for (i = 0; i < 4; ++i) + { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) + { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + break; + default: + return null; + } + } + catch (NumberFormatException e) + { + return null; + } + return bytes; + } + + /** + * 获取IP地址 + * + * @return 本地IP地址 + */ + public static String getHostIp() + { + try + { + return InetAddress.getLocalHost().getHostAddress(); + } + catch (UnknownHostException e) + { + } + return "127.0.0.1"; + } + + /** + * 获取主机名 + * + * @return 本地主机名 + */ + public static String getHostName() + { + try + { + return InetAddress.getLocalHost().getHostName(); + } + catch (UnknownHostException e) + { + } + return "未知"; + } + + /** + * 从多级反向代理中获得第一个非unknown IP地址 + * + * @param ip 获得的IP地址 + * @return 第一个非unknown IP地址 + */ + public static String getMultistageReverseProxyIp(String ip) + { + // 多级反向代理检测 + if (ip != null && ip.indexOf(",") > 0) + { + final String[] ips = ip.trim().split(","); + for (String subIp : ips) + { + if (false == isUnknown(subIp)) + { + ip = subIp; + break; + } + } + } + return StringUtils.substring(ip, 0, 255); + } + + /** + * 检测给定字符串是否为未知,多用于检测HTTP请求相关 + * + * @param checkString 被检测的字符串 + * @return 是否未知 + */ + public static boolean isUnknown(String checkString) + { + return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString); + } + + /** + * 是否为IP + */ + public static boolean isIP(String ip) + { + return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP); + } + + /** + * 是否为IP,或 *为间隔的通配符地址 + */ + public static boolean isIpWildCard(String ip) + { + return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP_WILDCARD); + } + + /** + * 检测参数是否在ip通配符里 + */ + public static boolean ipIsInWildCardNoCheck(String ipWildCard, String ip) + { + String[] s1 = ipWildCard.split("\\."); + String[] s2 = ip.split("\\."); + boolean isMatchedSeg = true; + for (int i = 0; i < s1.length && !s1[i].equals("*"); i++) + { + if (!s1[i].equals(s2[i])) + { + isMatchedSeg = false; + break; + } + } + return isMatchedSeg; + } + + /** + * 是否为特定格式如:“10.10.10.1-10.10.10.99”的ip段字符串 + */ + public static boolean isIPSegment(String ipSeg) + { + return StringUtils.isNotBlank(ipSeg) && ipSeg.matches(REGX_IP_SEG); + } + + /** + * 判断ip是否在指定网段中 + */ + public static boolean ipIsInNetNoCheck(String iparea, String ip) + { + int idx = iparea.indexOf('-'); + String[] sips = iparea.substring(0, idx).split("\\."); + String[] sipe = iparea.substring(idx + 1).split("\\."); + String[] sipt = ip.split("\\."); + long ips = 0L, ipe = 0L, ipt = 0L; + for (int i = 0; i < 4; ++i) + { + ips = ips << 8 | Integer.parseInt(sips[i]); + ipe = ipe << 8 | Integer.parseInt(sipe[i]); + ipt = ipt << 8 | Integer.parseInt(sipt[i]); + } + if (ips > ipe) + { + long t = ips; + ips = ipe; + ipe = t; + } + return ips <= ipt && ipt <= ipe; + } + + /** + * 校验ip是否符合过滤串规则 + * + * @param filter 过滤IP列表,支持后缀'*'通配,支持网段如:`10.10.10.1-10.10.10.99` + * @param ip 校验IP地址 + * @return boolean 结果 + */ + public static boolean isMatchedIp(String filter, String ip) + { + if (StringUtils.isEmpty(filter) || StringUtils.isEmpty(ip)) + { + return false; + } + String[] ips = filter.split(";"); + for (String iStr : ips) + { + if (isIP(iStr) && iStr.equals(ip)) + { + return true; + } + else if (isIpWildCard(iStr) && ipIsInWildCardNoCheck(iStr, ip)) + { + return true; + } + else if (isIPSegment(iStr) && ipIsInNetNoCheck(iStr, ip)) + { + return true; + } + } + return false; + } + + + private static final String LOOP_BACK = "127.0.0.1"; + private static String firstNoLoopbackIPV4Address = null; + + private static Collection<InetAddress> allHostIPV4Address = null; + /** + * 获取ipv4地址,如果有多个网卡的情况,获取第一个非loopback ip地址 + * + * @return + */ + public static String getFirstNoLoopbackIPV4Address() { + if (firstNoLoopbackIPV4Address != null) { + return firstNoLoopbackIPV4Address; + } + Collection<String> allNoLoopbackAddresses = null; + try { + allNoLoopbackAddresses = getAllNoLoopbackIPV4Addresses(); + } catch (Exception e) { + log.error("获取ip失败", e); + return LOOP_BACK; + } + if (allNoLoopbackAddresses.isEmpty()) { + return LOOP_BACK; + } + + return firstNoLoopbackIPV4Address = allNoLoopbackAddresses.iterator().next(); + } + + + public static Collection<String> getAllNoLoopbackIPV4Addresses() { + Collection<String> noLoopbackAddresses = new ArrayList<String>(); + Collection<InetAddress> allInetAddresses = getAllHostIPV4Address(); + + for (InetAddress address : allInetAddresses) { + if (!address.isLoopbackAddress()) { + noLoopbackAddresses.add(address.getHostAddress()); + } + } + + return noLoopbackAddresses; + } + + public static Collection<InetAddress> getAllHostIPV4Address() { + if (allHostIPV4Address == null) { + try { + Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces(); + Collection<InetAddress> addresses = new ArrayList<InetAddress>(); + + while (networkInterfaces.hasMoreElements()) { + NetworkInterface networkInterface = networkInterfaces.nextElement(); + Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses(); + while (inetAddresses.hasMoreElements()) { + InetAddress inetAddress = inetAddresses.nextElement(); + if (inetAddress instanceof Inet4Address) { + addresses.add(inetAddress); + } + } + } + allHostIPV4Address = addresses; + } catch (SocketException e) { + log.error("获取ip地址失败", e); + throw new RuntimeException(e.getMessage(), e); + } + + } + return allHostIPV4Address; + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.java new file mode 100644 index 0000000..e6cbd5a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.java @@ -0,0 +1,23 @@ +package com.ruoyi.common.utils.poi; + + + +/** + * Excel数据格式处理适配器 + * + * @author ruoyi + */ +public interface ExcelHandlerAdapter +{ + /** + * 格式化 + * + * @param value 单元格数据值 + * @param args excel注解args参数组 + * @param cell 单元格对象 + * @param wb 工作簿对象 + * + * @return 处理后的值 + */ +// Object format(Object value, String[] args, Cell cell, Workbook wb); +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java new file mode 100644 index 0000000..0c89771 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java @@ -0,0 +1,1679 @@ +package com.ruoyi.common.utils.poi; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Map; + +/** + * Excel相关处理 + * + * @author ruoyi + */ +public class ExcelUtil<T> +{ + private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class); + + public static final String FORMULA_REGEX_STR = "=|-|\\+|@"; + + public static final String[] FORMULA_STR = { "=", "-", "+", "@" }; + + /** + * 用于dictType属性数据存储,避免重复查缓存 + */ + public Map<String, String> sysDictMap = new HashMap<String, String>(); + + /** + * Excel sheet最大行数,默认65536 + */ + public static final int sheetSize = 65536; + + /** + * 工作表名称 + */ + private String sheetName; + + /** + * 导出类型(EXPORT:导出数据;IMPORT:导入模板) + */ +// private Type type; +// +// /** +// * 工作薄对象 +// */ +// private Workbook wb; +// +// /** +// * 工作表对象 +// */ +// private Sheet sheet; +// +// /** +// * 样式列表 +// */ +// private Map<String, CellStyle> styles; +// +// /** +// * 导入导出数据列表 +// */ +// private List<T> list; +// +// /** +// * 注解列表 +// */ +// private List<Object[]> fields; +// +// /** +// * 当前行号 +// */ +// private int rownum; +// +// /** +// * 标题 +// */ +// private String title; +// +// /** +// * 最大高度 +// */ +// private short maxHeight; +// +// /** +// * 合并后最后行数 +// */ +// private int subMergedLastRowNum = 0; +// +// /** +// * 合并后开始行数 +// */ +// private int subMergedFirstRowNum = 1; +// +// /** +// * 对象的子列表方法 +// */ +// private Method subMethod; +// +// /** +// * 对象的子列表属性 +// */ +// private List<Field> subFields; +// +// /** +// * 统计列表 +// */ +// private Map<Integer, Double> statistics = new HashMap<Integer, Double>(); +// +// /** +// * 数字格式 +// */ +// private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00"); +// +// /** +// * 实体对象 +// */ +// public Class<T> clazz; +// +// /** +// * 需要排除列属性 +// */ +// public String[] excludeFields; +// +// public ExcelUtil(Class<T> clazz) +// { +// this.clazz = clazz; +// } +// +// /** +// * 隐藏Excel中列属性 +// * +// * @param fields 列属性名 示例[单个"name"/多个"id","name"] +// * @throws Exception +// */ +// public void hideColumn(String... fields) +// { +// this.excludeFields = fields; +// } +// +// public void init(List<T> list, String sheetName, String title, Type type) +// { +// if (list == null) +// { +// list = new ArrayList<T>(); +// } +// this.list = list; +// this.sheetName = sheetName; +// this.type = type; +// this.title = title; +// createExcelField(); +// createWorkbook(); +// createTitle(); +// createSubHead(); +// } +// +// /** +// * 创建excel第一行标题 +// */ +// public void createTitle() +// { +// if (StringUtils.isNotEmpty(title)) +// { +// subMergedFirstRowNum++; +// subMergedLastRowNum++; +// int titleLastCol = this.fields.size() - 1; +// if (isSubList()) +// { +// titleLastCol = titleLastCol + subFields.size() - 1; +// } +// Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0); +// titleRow.setHeightInPoints(30); +// Cell titleCell = titleRow.createCell(0); +// titleCell.setCellStyle(styles.get("title")); +// titleCell.setCellValue(title); +// sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(), titleLastCol)); +// } +// } +// +// /** +// * 创建对象的子列表名称 +// */ +// public void createSubHead() +// { +// if (isSubList()) +// { +// subMergedFirstRowNum++; +// subMergedLastRowNum++; +// Row subRow = sheet.createRow(rownum); +// int excelNum = 0; +// for (Object[] objects : fields) +// { +// Excel attr = (Excel) objects[1]; +// Cell headCell1 = subRow.createCell(excelNum); +// headCell1.setCellValue(attr.name()); +// headCell1.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); +// excelNum++; +// } +// int headFirstRow = excelNum - 1; +// int headLastRow = headFirstRow + subFields.size() - 1; +// if (headLastRow > headFirstRow) +// { +// sheet.addMergedRegion(new CellRangeAddress(rownum, rownum, headFirstRow, headLastRow)); +// } +// rownum++; +// } +// } +// +// /** +// * 对excel表单默认第一个索引名转换成list +// * +// * @param is 输入流 +// * @return 转换后集合 +// */ +// public List<T> importExcel(InputStream is) +// { +// List<T> list = null; +// try +// { +// list = importExcel(is, 0); +// } +// catch (Exception e) +// { +// log.error("导入Excel异常{}", e.getMessage()); +// throw new UtilException(e.getMessage()); +// } +// finally +// { +// IOUtils.closeQuietly(is); +// } +// return list; +// } +// +// /** +// * 对excel表单默认第一个索引名转换成list +// * +// * @param is 输入流 +// * @param titleNum 标题占用行数 +// * @return 转换后集合 +// */ +// public List<T> importExcel(InputStream is, int titleNum) throws Exception +// { +// return importExcel(StringUtils.EMPTY, is, titleNum); +// } +// +// /** +// * 对excel表单指定表格索引名转换成list +// * +// * @param sheetName 表格索引名 +// * @param titleNum 标题占用行数 +// * @param is 输入流 +// * @return 转换后集合 +// */ +// public List<T> importExcel(String sheetName, InputStream is, int titleNum) throws Exception +// { +// this.type = Type.IMPORT; +// this.wb = WorkbookFactory.create(is); +// List<T> list = new ArrayList<T>(); +// // 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet +// Sheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0); +// if (sheet == null) +// { +// throw new IOException("文件sheet不存在"); +// } +// boolean isXSSFWorkbook = !(wb instanceof HSSFWorkbook); +// Map<String, PictureData> pictures; +// if (isXSSFWorkbook) +// { +// pictures = getSheetPictures07((XSSFSheet) sheet, (XSSFWorkbook) wb); +// } +// else +// { +// pictures = getSheetPictures03((HSSFSheet) sheet, (HSSFWorkbook) wb); +// } +// // 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1 +// int rows = sheet.getLastRowNum(); +// if (rows > 0) +// { +// // 定义一个map用于存放excel列的序号和field. +// Map<String, Integer> cellMap = new HashMap<String, Integer>(); +// // 获取表头 +// Row heard = sheet.getRow(titleNum); +// for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++) +// { +// Cell cell = heard.getCell(i); +// if (StringUtils.isNotNull(cell)) +// { +// String value = this.getCellValue(heard, i).toString(); +// cellMap.put(value, i); +// } +// else +// { +// cellMap.put(null, i); +// } +// } +// // 有数据时才处理 得到类的所有field. +// List<Object[]> fields = this.getFields(); +// Map<Integer, Object[]> fieldsMap = new HashMap<Integer, Object[]>(); +// for (Object[] objects : fields) +// { +// Excel attr = (Excel) objects[1]; +// Integer column = cellMap.get(attr.name()); +// if (column != null) +// { +// fieldsMap.put(column, objects); +// } +// } +// for (int i = titleNum + 1; i <= rows; i++) +// { +// // 从第2行开始取数据,默认第一行是表头. +// Row row = sheet.getRow(i); +// // 判断当前行是否是空行 +// if (isRowEmpty(row)) +// { +// continue; +// } +// T entity = null; +// for (Map.Entry<Integer, Object[]> entry : fieldsMap.entrySet()) +// { +// Object val = this.getCellValue(row, entry.getKey()); +// +// // 如果不存在实例则新建. +// entity = (entity == null ? clazz.newInstance() : entity); +// // 从map中得到对应列的field. +// Field field = (Field) entry.getValue()[0]; +// Excel attr = (Excel) entry.getValue()[1]; +// // 取得类型,并根据对象类型设置值. +// Class<?> fieldType = field.getType(); +// if (String.class == fieldType) +// { +// String s = Convert.toStr(val); +// if (StringUtils.endsWith(s, ".0")) +// { +// val = StringUtils.substringBefore(s, ".0"); +// } +// else +// { +// String dateFormat = field.getAnnotation(Excel.class).dateFormat(); +// if (StringUtils.isNotEmpty(dateFormat)) +// { +// val = parseDateToStr(dateFormat, val); +// } +// else +// { +// val = Convert.toStr(val); +// } +// } +// } +// else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) +// { +// val = Convert.toInt(val); +// } +// else if ((Long.TYPE == fieldType || Long.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) +// { +// val = Convert.toLong(val); +// } +// else if (Double.TYPE == fieldType || Double.class == fieldType) +// { +// val = Convert.toDouble(val); +// } +// else if (Float.TYPE == fieldType || Float.class == fieldType) +// { +// val = Convert.toFloat(val); +// } +// else if (BigDecimal.class == fieldType) +// { +// val = Convert.toBigDecimal(val); +// } +// else if (Date.class == fieldType) +// { +// if (val instanceof String) +// { +// val = DateUtils.parseDate(val); +// } +// else if (val instanceof Double) +// { +// val = DateUtil.getJavaDate((Double) val); +// } +// } +// else if (Boolean.TYPE == fieldType || Boolean.class == fieldType) +// { +// val = Convert.toBool(val, false); +// } +// if (StringUtils.isNotNull(fieldType)) +// { +// String propertyName = field.getName(); +// if (StringUtils.isNotEmpty(attr.targetAttr())) +// { +// propertyName = field.getName() + "." + attr.targetAttr(); +// } +// if (StringUtils.isNotEmpty(attr.readConverterExp())) +// { +// val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator()); +// } +// else if (StringUtils.isNotEmpty(attr.dictType())) +// { +// val = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator()); +// } +// else if (!attr.handler().equals(ExcelHandlerAdapter.class)) +// { +// val = dataFormatHandlerAdapter(val, attr, null); +// } +// else if (ColumnType.IMAGE == attr.cellType() && StringUtils.isNotEmpty(pictures)) +// { +// PictureData image = pictures.get(row.getRowNum() + "_" + entry.getKey()); +// if (image == null) +// { +// val = ""; +// } +// else +// { +// byte[] data = image.getData(); +// val = FileUtils.writeImportBytes(data); +// } +// } +// ReflectUtils.invokeSetter(entity, propertyName, val); +// } +// } +// list.add(entity); +// } +// } +// return list; +// } +// +// /** +// * 对list数据源将其里面的数据导入到excel表单 +// * +// * @param list 导出数据集合 +// * @param sheetName 工作表的名称 +// * @return 结果 +// */ +// public AjaxResult exportExcel(List<T> list, String sheetName) +// { +// return exportExcel(list, sheetName, StringUtils.EMPTY); +// } +// +// /** +// * 对list数据源将其里面的数据导入到excel表单 +// * +// * @param list 导出数据集合 +// * @param sheetName 工作表的名称 +// * @param title 标题 +// * @return 结果 +// */ +// public AjaxResult exportExcel(List<T> list, String sheetName, String title) +// { +// this.init(list, sheetName, title, Type.EXPORT); +// return exportExcel(); +// } +// +// /** +// * 对list数据源将其里面的数据导入到excel表单 +// * +// * @param response 返回数据 +// * @param list 导出数据集合 +// * @param sheetName 工作表的名称 +// * @return 结果 +// */ +// public void exportExcel(HttpServletResponse response, List<T> list, String sheetName) +// { +// exportExcel(response, list, sheetName, StringUtils.EMPTY); +// } +// +// /** +// * 对list数据源将其里面的数据导入到excel表单 +// * +// * @param response 返回数据 +// * @param list 导出数据集合 +// * @param sheetName 工作表的名称 +// * @param title 标题 +// * @return 结果 +// */ +// public void exportExcel(HttpServletResponse response, List<T> list, String sheetName, String title) +// { +// response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); +// response.setCharacterEncoding("utf-8"); +// this.init(list, sheetName, title, Type.EXPORT); +// exportExcel(response); +// } +// +// /** +// * 对list数据源将其里面的数据导入到excel表单 +// * +// * @param sheetName 工作表的名称 +// * @return 结果 +// */ +// public AjaxResult importTemplateExcel(String sheetName) +// { +// return importTemplateExcel(sheetName, StringUtils.EMPTY); +// } +// +// /** +// * 对list数据源将其里面的数据导入到excel表单 +// * +// * @param sheetName 工作表的名称 +// * @param title 标题 +// * @return 结果 +// */ +// public AjaxResult importTemplateExcel(String sheetName, String title) +// { +// this.init(null, sheetName, title, Type.IMPORT); +// return exportExcel(); +// } +// +// /** +// * 对list数据源将其里面的数据导入到excel表单 +// * +// * @param sheetName 工作表的名称 +// * @return 结果 +// */ +// public void importTemplateExcel(HttpServletResponse response, String sheetName) +// { +// importTemplateExcel(response, sheetName, StringUtils.EMPTY); +// } +// +// /** +// * 对list数据源将其里面的数据导入到excel表单 +// * +// * @param sheetName 工作表的名称 +// * @param title 标题 +// * @return 结果 +// */ +// public void importTemplateExcel(HttpServletResponse response, String sheetName, String title) +// { +// response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); +// response.setCharacterEncoding("utf-8"); +// this.init(null, sheetName, title, Type.IMPORT); +// exportExcel(response); +// } +// +// /** +// * 对list数据源将其里面的数据导入到excel表单 +// * +// * @return 结果 +// */ +// public void exportExcel(HttpServletResponse response) +// { +// try +// { +// writeSheet(); +// wb.write(response.getOutputStream()); +// } +// catch (Exception e) +// { +// log.error("导出Excel异常{}", e.getMessage()); +// } +// finally +// { +// IOUtils.closeQuietly(wb); +// } +// } +// +// /** +// * 对list数据源将其里面的数据导入到excel表单 +// * +// * @return 结果 +// */ +// public AjaxResult exportExcel() +// { +// OutputStream out = null; +// try +// { +// writeSheet(); +// String filename = encodingFilename(sheetName); +// out = new FileOutputStream(getAbsoluteFile(filename)); +// wb.write(out); +// return AjaxResult.success(filename); +// } +// catch (Exception e) +// { +// log.error("导出Excel异常{}", e.getMessage()); +// throw new UtilException("导出Excel失败,请联系网站管理员!"); +// } +// finally +// { +// IOUtils.closeQuietly(wb); +// IOUtils.closeQuietly(out); +// } +// } +// +// /** +// * 创建写入数据到Sheet +// */ +// public void writeSheet() +// { +// // 取出一共有多少个sheet. +// int sheetNo = Math.max(1, (int) Math.ceil(list.size() * 1.0 / sheetSize)); +// for (int index = 0; index < sheetNo; index++) +// { +// createSheet(sheetNo, index); +// +// // 产生一行 +// Row row = sheet.createRow(rownum); +// int column = 0; +// // 写入各个字段的列头名称 +// for (Object[] os : fields) +// { +// Field field = (Field) os[0]; +// Excel excel = (Excel) os[1]; +// if (Collection.class.isAssignableFrom(field.getType())) +// { +// for (Field subField : subFields) +// { +// Excel subExcel = subField.getAnnotation(Excel.class); +// this.createHeadCell(subExcel, row, column++); +// } +// } +// else +// { +// this.createHeadCell(excel, row, column++); +// } +// } +// if (Type.EXPORT.equals(type)) +// { +// fillExcelData(index, row); +// addStatisticsRow(); +// } +// } +// } +// +// /** +// * 填充excel数据 +// * +// * @param index 序号 +// * @param row 单元格行 +// */ +// @SuppressWarnings("unchecked") +// public void fillExcelData(int index, Row row) +// { +// int startNo = index * sheetSize; +// int endNo = Math.min(startNo + sheetSize, list.size()); +// int rowNo = (1 + rownum) - startNo; +// for (int i = startNo; i < endNo; i++) +// { +// rowNo = isSubList() ? (i > 1 ? rowNo + 1 : rowNo + i) : i + 1 + rownum - startNo; +// row = sheet.createRow(rowNo); +// // 得到导出对象. +// T vo = (T) list.get(i); +// Collection<?> subList = null; +// if (isSubList()) +// { +// if (isSubListValue(vo)) +// { +// subList = getListCellValue(vo); +// subMergedLastRowNum = subMergedLastRowNum + subList.size(); +// } +// else +// { +// subMergedFirstRowNum++; +// subMergedLastRowNum++; +// } +// } +// int column = 0; +// for (Object[] os : fields) +// { +// Field field = (Field) os[0]; +// Excel excel = (Excel) os[1]; +// if (Collection.class.isAssignableFrom(field.getType()) && StringUtils.isNotNull(subList)) +// { +// boolean subFirst = false; +// for (Object obj : subList) +// { +// if (subFirst) +// { +// rowNo++; +// row = sheet.createRow(rowNo); +// } +// List<Field> subFields = FieldUtils.getFieldsListWithAnnotation(obj.getClass(), Excel.class); +// int subIndex = 0; +// for (Field subField : subFields) +// { +// if (subField.isAnnotationPresent(Excel.class)) +// { +// subField.setAccessible(true); +// Excel attr = subField.getAnnotation(Excel.class); +// this.addCell(attr, row, (T) obj, subField, column + subIndex); +// } +// subIndex++; +// } +// subFirst = true; +// } +// this.subMergedFirstRowNum = this.subMergedFirstRowNum + subList.size(); +// } +// else +// { +// this.addCell(excel, row, vo, field, column++); +// } +// } +// } +// } +// +// /** +// * 创建表格样式 +// * +// * @param wb 工作薄对象 +// * @return 样式列表 +// */ +// private Map<String, CellStyle> createStyles(Workbook wb) +// { +// // 写入各条记录,每条记录对应excel表中的一行 +// Map<String, CellStyle> styles = new HashMap<String, CellStyle>(); +// CellStyle style = wb.createCellStyle(); +// style.setAlignment(HorizontalAlignment.CENTER); +// style.setVerticalAlignment(VerticalAlignment.CENTER); +// Font titleFont = wb.createFont(); +// titleFont.setFontName("Arial"); +// titleFont.setFontHeightInPoints((short) 16); +// titleFont.setBold(true); +// style.setFont(titleFont); +// styles.put("title", style); +// +// style = wb.createCellStyle(); +// style.setAlignment(HorizontalAlignment.CENTER); +// style.setVerticalAlignment(VerticalAlignment.CENTER); +// style.setBorderRight(BorderStyle.THIN); +// style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); +// style.setBorderLeft(BorderStyle.THIN); +// style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); +// style.setBorderTop(BorderStyle.THIN); +// style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); +// style.setBorderBottom(BorderStyle.THIN); +// style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); +// Font dataFont = wb.createFont(); +// dataFont.setFontName("Arial"); +// dataFont.setFontHeightInPoints((short) 10); +// style.setFont(dataFont); +// styles.put("data", style); +// +// style = wb.createCellStyle(); +// style.setAlignment(HorizontalAlignment.CENTER); +// style.setVerticalAlignment(VerticalAlignment.CENTER); +// Font totalFont = wb.createFont(); +// totalFont.setFontName("Arial"); +// totalFont.setFontHeightInPoints((short) 10); +// style.setFont(totalFont); +// styles.put("total", style); +// +// styles.putAll(annotationHeaderStyles(wb, styles)); +// +// styles.putAll(annotationDataStyles(wb)); +// +// return styles; +// } +// +// /** +// * 根据Excel注解创建表格头样式 +// * +// * @param wb 工作薄对象 +// * @return 自定义样式列表 +// */ +// private Map<String, CellStyle> annotationHeaderStyles(Workbook wb, Map<String, CellStyle> styles) +// { +// Map<String, CellStyle> headerStyles = new HashMap<String, CellStyle>(); +// for (Object[] os : fields) +// { +// Excel excel = (Excel) os[1]; +// String key = StringUtils.format("header_{}_{}", excel.headerColor(), excel.headerBackgroundColor()); +// if (!headerStyles.containsKey(key)) +// { +// CellStyle style = wb.createCellStyle(); +// style.cloneStyleFrom(styles.get("data")); +// style.setAlignment(HorizontalAlignment.CENTER); +// style.setVerticalAlignment(VerticalAlignment.CENTER); +// style.setFillForegroundColor(excel.headerBackgroundColor().index); +// style.setFillPattern(FillPatternType.SOLID_FOREGROUND); +// Font headerFont = wb.createFont(); +// headerFont.setFontName("Arial"); +// headerFont.setFontHeightInPoints((short) 10); +// headerFont.setBold(true); +// headerFont.setColor(excel.headerColor().index); +// style.setFont(headerFont); +// headerStyles.put(key, style); +// } +// } +// return headerStyles; +// } +// +// /** +// * 根据Excel注解创建表格列样式 +// * +// * @param wb 工作薄对象 +// * @return 自定义样式列表 +// */ +// private Map<String, CellStyle> annotationDataStyles(Workbook wb) +// { +// Map<String, CellStyle> styles = new HashMap<String, CellStyle>(); +// for (Object[] os : fields) +// { +// Excel excel = (Excel) os[1]; +// String key = StringUtils.format("data_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor()); +// if (!styles.containsKey(key)) +// { +// CellStyle style = wb.createCellStyle(); +// style.setAlignment(excel.align()); +// style.setVerticalAlignment(VerticalAlignment.CENTER); +// style.setBorderRight(BorderStyle.THIN); +// style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); +// style.setBorderLeft(BorderStyle.THIN); +// style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); +// style.setBorderTop(BorderStyle.THIN); +// style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); +// style.setBorderBottom(BorderStyle.THIN); +// style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); +// style.setFillPattern(FillPatternType.SOLID_FOREGROUND); +// style.setFillForegroundColor(excel.backgroundColor().getIndex()); +// Font dataFont = wb.createFont(); +// dataFont.setFontName("Arial"); +// dataFont.setFontHeightInPoints((short) 10); +// dataFont.setColor(excel.color().index); +// style.setFont(dataFont); +// styles.put(key, style); +// } +// } +// return styles; +// } +// +// /** +// * 创建单元格 +// */ +// public Cell createHeadCell(Excel attr, Row row, int column) +// { +// // 创建列 +// Cell cell = row.createCell(column); +// // 写入列信息 +// cell.setCellValue(attr.name()); +// setDataValidation(attr, row, column); +// cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); +// if (isSubList()) +// { +// // 填充默认样式,防止合并单元格样式失效 +// sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor()))); +// if (attr.needMerge()) +// { +// sheet.addMergedRegion(new CellRangeAddress(rownum - 1, rownum, column, column)); +// } +// } +// return cell; +// } +// +// /** +// * 设置单元格信息 +// * +// * @param value 单元格值 +// * @param attr 注解相关 +// * @param cell 单元格信息 +// */ +// public void setCellVo(Object value, Excel attr, Cell cell) +// { +// if (ColumnType.STRING == attr.cellType()) +// { +// String cellValue = Convert.toStr(value); +// // 对于任何以表达式触发字符 =-+@开头的单元格,直接使用tab字符作为前缀,防止CSV注入。 +// if (StringUtils.startsWithAny(cellValue, FORMULA_STR)) +// { +// cellValue = RegExUtils.replaceFirst(cellValue, FORMULA_REGEX_STR, "\t$0"); +// } +// if (value instanceof Collection && StringUtils.equals("[]", cellValue)) +// { +// cellValue = StringUtils.EMPTY; +// } +// cell.setCellValue(StringUtils.isNull(cellValue) ? attr.defaultValue() : cellValue + attr.suffix()); +// } +// else if (ColumnType.NUMERIC == attr.cellType()) +// { +// if (StringUtils.isNotNull(value)) +// { +// cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value)); +// } +// } +// else if (ColumnType.IMAGE == attr.cellType()) +// { +// ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), cell.getRow().getRowNum() + 1); +// String imagePath = Convert.toStr(value); +// if (StringUtils.isNotEmpty(imagePath)) +// { +// byte[] data = ImageUtils.getImage(imagePath); +// getDrawingPatriarch(cell.getSheet()).createPicture(anchor, +// cell.getSheet().getWorkbook().addPicture(data, getImageType(data))); +// } +// } +// } +// +// /** +// * 获取画布 +// */ +// public static Drawing<?> getDrawingPatriarch(Sheet sheet) +// { +// if (sheet.getDrawingPatriarch() == null) +// { +// sheet.createDrawingPatriarch(); +// } +// return sheet.getDrawingPatriarch(); +// } +// +// /** +// * 获取图片类型,设置图片插入类型 +// */ +// public int getImageType(byte[] value) +// { +// String type = FileTypeUtils.getFileExtendName(value); +// if ("JPG".equalsIgnoreCase(type)) +// { +// return Workbook.PICTURE_TYPE_JPEG; +// } +// else if ("PNG".equalsIgnoreCase(type)) +// { +// return Workbook.PICTURE_TYPE_PNG; +// } +// return Workbook.PICTURE_TYPE_JPEG; +// } +// +// /** +// * 创建表格样式 +// */ +// public void setDataValidation(Excel attr, Row row, int column) +// { +// if (attr.name().indexOf("注:") >= 0) +// { +// sheet.setColumnWidth(column, 6000); +// } +// else +// { +// // 设置列宽 +// sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256)); +// } +// if (StringUtils.isNotEmpty(attr.prompt()) || attr.combo().length > 0) +// { +// if (attr.combo().length > 15 || StringUtils.join(attr.combo()).length() > 255) +// { +// // 如果下拉数大于15或字符串长度大于255,则使用一个新sheet存储,避免生成的模板下拉值获取不到 +// setXSSFValidationWithHidden(sheet, attr.combo(), attr.prompt(), 1, 100, column, column); +// } +// else +// { +// // 提示信息或只能选择不能输入的列内容. +// setPromptOrValidation(sheet, attr.combo(), attr.prompt(), 1, 100, column, column); +// } +// } +// } +// +// /** +// * 添加单元格 +// */ +// public Cell addCell(Excel attr, Row row, T vo, Field field, int column) +// { +// Cell cell = null; +// try +// { +// // 设置行高 +// row.setHeight(maxHeight); +// // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列. +// if (attr.isExport()) +// { +// // 创建cell +// cell = row.createCell(column); +// if (isSubListValue(vo) && getListCellValue(vo).size() > 1 && attr.needMerge()) +// { +// CellRangeAddress cellAddress = new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column); +// sheet.addMergedRegion(cellAddress); +// } +// cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor()))); +// +// // 用于读取对象中的属性 +// Object value = getTargetValue(vo, field, attr); +// String dateFormat = attr.dateFormat(); +// String readConverterExp = attr.readConverterExp(); +// String separator = attr.separator(); +// String dictType = attr.dictType(); +// if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value)) +// { +// cell.setCellValue(parseDateToStr(dateFormat, value)); +// } +// else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value)) +// { +// cell.setCellValue(convertByExp(Convert.toStr(value), readConverterExp, separator)); +// } +// else if (StringUtils.isNotEmpty(dictType) && StringUtils.isNotNull(value)) +// { +// if (!sysDictMap.containsKey(dictType + value)) +// { +// String lable = convertDictByExp(Convert.toStr(value), dictType, separator); +// sysDictMap.put(dictType + value, lable); +// } +// cell.setCellValue(sysDictMap.get(dictType + value)); +// } +// else if (value instanceof BigDecimal && -1 != attr.scale()) +// { +// cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).doubleValue()); +// } +// else if (!attr.handler().equals(ExcelHandlerAdapter.class)) +// { +// cell.setCellValue(dataFormatHandlerAdapter(value, attr, cell)); +// } +// else +// { +// // 设置列类型 +// setCellVo(value, attr, cell); +// } +// addStatisticsData(column, Convert.toStr(value), attr); +// } +// } +// catch (Exception e) +// { +// log.error("导出Excel失败{}", e); +// } +// return cell; +// } +// +// /** +// * 设置 POI XSSFSheet 单元格提示或选择框 +// * +// * @param sheet 表单 +// * @param textlist 下拉框显示的内容 +// * @param promptContent 提示内容 +// * @param firstRow 开始行 +// * @param endRow 结束行 +// * @param firstCol 开始列 +// * @param endCol 结束列 +// */ +// public void setPromptOrValidation(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, +// int firstCol, int endCol) +// { +// DataValidationHelper helper = sheet.getDataValidationHelper(); +// DataValidationConstraint constraint = textlist.length > 0 ? helper.createExplicitListConstraint(textlist) : helper.createCustomConstraint("DD1"); +// CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); +// DataValidation dataValidation = helper.createValidation(constraint, regions); +// if (StringUtils.isNotEmpty(promptContent)) +// { +// // 如果设置了提示信息则鼠标放上去提示 +// dataValidation.createPromptBox("", promptContent); +// dataValidation.setShowPromptBox(true); +// } +// // 处理Excel兼容性问题 +// if (dataValidation instanceof XSSFDataValidation) +// { +// dataValidation.setSuppressDropDownArrow(true); +// dataValidation.setShowErrorBox(true); +// } +// else +// { +// dataValidation.setSuppressDropDownArrow(false); +// } +// sheet.addValidationData(dataValidation); +// } +// +// /** +// * 设置某些列的值只能输入预制的数据,显示下拉框(兼容超出一定数量的下拉框). +// * +// * @param sheet 要设置的sheet. +// * @param textlist 下拉框显示的内容 +// * @param promptContent 提示内容 +// * @param firstRow 开始行 +// * @param endRow 结束行 +// * @param firstCol 开始列 +// * @param endCol 结束列 +// */ +// public void setXSSFValidationWithHidden(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, int firstCol, int endCol) +// { +// String hideSheetName = "combo_" + firstCol + "_" + endCol; +// Sheet hideSheet = wb.createSheet(hideSheetName); // 用于存储 下拉菜单数据 +// for (int i = 0; i < textlist.length; i++) +// { +// hideSheet.createRow(i).createCell(0).setCellValue(textlist[i]); +// } +// // 创建名称,可被其他单元格引用 +// Name name = wb.createName(); +// name.setNameName(hideSheetName + "_data"); +// name.setRefersToFormula(hideSheetName + "!$A$1:$A$" + textlist.length); +// DataValidationHelper helper = sheet.getDataValidationHelper(); +// // 加载下拉列表内容 +// DataValidationConstraint constraint = helper.createFormulaListConstraint(hideSheetName + "_data"); +// // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列 +// CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); +// // 数据有效性对象 +// DataValidation dataValidation = helper.createValidation(constraint, regions); +// if (StringUtils.isNotEmpty(promptContent)) +// { +// // 如果设置了提示信息则鼠标放上去提示 +// dataValidation.createPromptBox("", promptContent); +// dataValidation.setShowPromptBox(true); +// } +// // 处理Excel兼容性问题 +// if (dataValidation instanceof XSSFDataValidation) +// { +// dataValidation.setSuppressDropDownArrow(true); +// dataValidation.setShowErrorBox(true); +// } +// else +// { +// dataValidation.setSuppressDropDownArrow(false); +// } +// +// sheet.addValidationData(dataValidation); +// // 设置hiddenSheet隐藏 +// wb.setSheetHidden(wb.getSheetIndex(hideSheet), true); +// } +// +// /** +// * 解析导出值 0=男,1=女,2=未知 +// * +// * @param propertyValue 参数值 +// * @param converterExp 翻译注解 +// * @param separator 分隔符 +// * @return 解析后值 +// */ +// public static String convertByExp(String propertyValue, String converterExp, String separator) +// { +// StringBuilder propertyString = new StringBuilder(); +// String[] convertSource = converterExp.split(","); +// for (String item : convertSource) +// { +// String[] itemArray = item.split("="); +// if (StringUtils.containsAny(propertyValue, separator)) +// { +// for (String value : propertyValue.split(separator)) +// { +// if (itemArray[0].equals(value)) +// { +// propertyString.append(itemArray[1] + separator); +// break; +// } +// } +// } +// else +// { +// if (itemArray[0].equals(propertyValue)) +// { +// return itemArray[1]; +// } +// } +// } +// return StringUtils.stripEnd(propertyString.toString(), separator); +// } +// +// /** +// * 反向解析值 男=0,女=1,未知=2 +// * +// * @param propertyValue 参数值 +// * @param converterExp 翻译注解 +// * @param separator 分隔符 +// * @return 解析后值 +// */ +// public static String reverseByExp(String propertyValue, String converterExp, String separator) +// { +// StringBuilder propertyString = new StringBuilder(); +// String[] convertSource = converterExp.split(","); +// for (String item : convertSource) +// { +// String[] itemArray = item.split("="); +// if (StringUtils.containsAny(propertyValue, separator)) +// { +// for (String value : propertyValue.split(separator)) +// { +// if (itemArray[1].equals(value)) +// { +// propertyString.append(itemArray[0] + separator); +// break; +// } +// } +// } +// else +// { +// if (itemArray[1].equals(propertyValue)) +// { +// return itemArray[0]; +// } +// } +// } +// return StringUtils.stripEnd(propertyString.toString(), separator); +// } +// +// /** +// * 解析字典值 +// * +// * @param dictValue 字典值 +// * @param dictType 字典类型 +// * @param separator 分隔符 +// * @return 字典标签 +// */ +// public static String convertDictByExp(String dictValue, String dictType, String separator) +// { +// return DictUtils.getDictLabel(dictType, dictValue, separator); +// } +// +// /** +// * 反向解析值字典值 +// * +// * @param dictLabel 字典标签 +// * @param dictType 字典类型 +// * @param separator 分隔符 +// * @return 字典值 +// */ +// public static String reverseDictByExp(String dictLabel, String dictType, String separator) +// { +// return DictUtils.getDictValue(dictType, dictLabel, separator); +// } +// +// /** +// * 数据处理器 +// * +// * @param value 数据值 +// * @param excel 数据注解 +// * @return +// */ +// public String dataFormatHandlerAdapter(Object value, Excel excel, Cell cell) +// { +// try +// { +// Object instance = excel.handler().newInstance(); +// Method formatMethod = excel.handler().getMethod("format", new Class[] { Object.class, String[].class, Cell.class, Workbook.class }); +// value = formatMethod.invoke(instance, value, excel.args(), cell, this.wb); +// } +// catch (Exception e) +// { +// log.error("不能格式化数据 " + excel.handler(), e.getMessage()); +// } +// return Convert.toStr(value); +// } +// +// /** +// * 合计统计信息 +// */ +// private void addStatisticsData(Integer index, String text, Excel entity) +// { +// if (entity != null && entity.isStatistics()) +// { +// Double temp = 0D; +// if (!statistics.containsKey(index)) +// { +// statistics.put(index, temp); +// } +// try +// { +// temp = Double.valueOf(text); +// } +// catch (NumberFormatException e) +// { +// } +// statistics.put(index, statistics.get(index) + temp); +// } +// } +// +// /** +// * 创建统计行 +// */ +// public void addStatisticsRow() +// { +// if (statistics.size() > 0) +// { +// Row row = sheet.createRow(sheet.getLastRowNum() + 1); +// Set<Integer> keys = statistics.keySet(); +// Cell cell = row.createCell(0); +// cell.setCellStyle(styles.get("total")); +// cell.setCellValue("合计"); +// +// for (Integer key : keys) +// { +// cell = row.createCell(key); +// cell.setCellStyle(styles.get("total")); +// cell.setCellValue(DOUBLE_FORMAT.format(statistics.get(key))); +// } +// statistics.clear(); +// } +// } +// +// /** +// * 编码文件名 +// */ +// public String encodingFilename(String filename) +// { +// filename = UUID.randomUUID() + "_" + filename + ".xlsx"; +// return filename; +// } +// +// /** +// * 获取下载路径 +// * +// * @param filename 文件名称 +// */ +// public String getAbsoluteFile(String filename) +// { +// String downloadPath = RuoYiConfig.getDownloadPath() + filename; +// File desc = new File(downloadPath); +// if (!desc.getParentFile().exists()) +// { +// desc.getParentFile().mkdirs(); +// } +// return downloadPath; +// } +// +// /** +// * 获取bean中的属性值 +// * +// * @param vo 实体对象 +// * @param field 字段 +// * @param excel 注解 +// * @return 最终的属性值 +// * @throws Exception +// */ +// private Object getTargetValue(T vo, Field field, Excel excel) throws Exception +// { +// Object o = field.get(vo); +// if (StringUtils.isNotEmpty(excel.targetAttr())) +// { +// String target = excel.targetAttr(); +// if (target.contains(".")) +// { +// String[] targets = target.split("[.]"); +// for (String name : targets) +// { +// o = getValue(o, name); +// } +// } +// else +// { +// o = getValue(o, target); +// } +// } +// return o; +// } +// +// /** +// * 以类的属性的get方法方法形式获取值 +// * +// * @param o +// * @param name +// * @return value +// * @throws Exception +// */ +// private Object getValue(Object o, String name) throws Exception +// { +// if (StringUtils.isNotNull(o) && StringUtils.isNotEmpty(name)) +// { +// Class<?> clazz = o.getClass(); +// Field field = clazz.getDeclaredField(name); +// field.setAccessible(true); +// o = field.get(o); +// } +// return o; +// } +// +// /** +// * 得到所有定义字段 +// */ +// private void createExcelField() +// { +// this.fields = getFields(); +// this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList()); +// this.maxHeight = getRowHeight(); +// } +// +// /** +// * 获取字段注解信息 +// */ +// public List<Object[]> getFields() +// { +// List<Object[]> fields = new ArrayList<Object[]>(); +// List<Field> tempFields = new ArrayList<>(); +// tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields())); +// tempFields.addAll(Arrays.asList(clazz.getDeclaredFields())); +// for (Field field : tempFields) +// { +// if (!ArrayUtils.contains(this.excludeFields, field.getName())) +// { +// // 单注解 +// if (field.isAnnotationPresent(Excel.class)) +// { +// Excel attr = field.getAnnotation(Excel.class); +// if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) +// { +// field.setAccessible(true); +// fields.add(new Object[] { field, attr }); +// } +// if (Collection.class.isAssignableFrom(field.getType())) +// { +// subMethod = getSubMethod(field.getName(), clazz); +// ParameterizedType pt = (ParameterizedType) field.getGenericType(); +// Class<?> subClass = (Class<?>) pt.getActualTypeArguments()[0]; +// this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class); +// } +// } +// +// // 多注解 +// if (field.isAnnotationPresent(Excels.class)) +// { +// Excels attrs = field.getAnnotation(Excels.class); +// Excel[] excels = attrs.value(); +// for (Excel attr : excels) +// { +// if (!ArrayUtils.contains(this.excludeFields, field.getName() + "." + attr.targetAttr()) +// && (attr != null && (attr.type() == Type.ALL || attr.type() == type))) +// { +// field.setAccessible(true); +// fields.add(new Object[] { field, attr }); +// } +// } +// } +// } +// } +// return fields; +// } +// +// /** +// * 根据注解获取最大行高 +// */ +// public short getRowHeight() +// { +// double maxHeight = 0; +// for (Object[] os : this.fields) +// { +// Excel excel = (Excel) os[1]; +// maxHeight = Math.max(maxHeight, excel.height()); +// } +// return (short) (maxHeight * 20); +// } +// +// /** +// * 创建一个工作簿 +// */ +// public void createWorkbook() +// { +// this.wb = new SXSSFWorkbook(500); +// this.sheet = wb.createSheet(); +// wb.setSheetName(0, sheetName); +// this.styles = createStyles(wb); +// } +// +// /** +// * 创建工作表 +// * +// * @param sheetNo sheet数量 +// * @param index 序号 +// */ +// public void createSheet(int sheetNo, int index) +// { +// // 设置工作表的名称. +// if (sheetNo > 1 && index > 0) +// { +// this.sheet = wb.createSheet(); +// this.createTitle(); +// wb.setSheetName(index, sheetName + index); +// } +// } +// +// /** +// * 获取单元格值 +// * +// * @param row 获取的行 +// * @param column 获取单元格列号 +// * @return 单元格值 +// */ +// public Object getCellValue(Row row, int column) +// { +// if (row == null) +// { +// return row; +// } +// Object val = ""; +// try +// { +// Cell cell = row.getCell(column); +// if (StringUtils.isNotNull(cell)) +// { +// if (cell.getCellType() == CellType.NUMERIC || cell.getCellType() == CellType.FORMULA) +// { +// val = cell.getNumericCellValue(); +// if (DateUtil.isCellDateFormatted(cell)) +// { +// val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换 +// } +// else +// { +// if ((Double) val % 1 != 0) +// { +// val = new BigDecimal(val.toString()); +// } +// else +// { +// val = new DecimalFormat("0").format(val); +// } +// } +// } +// else if (cell.getCellType() == CellType.STRING) +// { +// val = cell.getStringCellValue(); +// } +// else if (cell.getCellType() == CellType.BOOLEAN) +// { +// val = cell.getBooleanCellValue(); +// } +// else if (cell.getCellType() == CellType.ERROR) +// { +// val = cell.getErrorCellValue(); +// } +// +// } +// } +// catch (Exception e) +// { +// return val; +// } +// return val; +// } +// +// /** +// * 判断是否是空行 +// * +// * @param row 判断的行 +// * @return +// */ +// private boolean isRowEmpty(Row row) +// { +// if (row == null) +// { +// return true; +// } +// for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) +// { +// Cell cell = row.getCell(i); +// if (cell != null && cell.getCellType() != CellType.BLANK) +// { +// return false; +// } +// } +// return true; +// } +// +// /** +// * 获取Excel2003图片 +// * +// * @param sheet 当前sheet对象 +// * @param workbook 工作簿对象 +// * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData +// */ +// public static Map<String, PictureData> getSheetPictures03(HSSFSheet sheet, HSSFWorkbook workbook) +// { +// Map<String, PictureData> sheetIndexPicMap = new HashMap<String, PictureData>(); +// List<HSSFPictureData> pictures = workbook.getAllPictures(); +// if (!pictures.isEmpty()) +// { +// for (HSSFShape shape : sheet.getDrawingPatriarch().getChildren()) +// { +// HSSFClientAnchor anchor = (HSSFClientAnchor) shape.getAnchor(); +// if (shape instanceof HSSFPicture) +// { +// HSSFPicture pic = (HSSFPicture) shape; +// int pictureIndex = pic.getPictureIndex() - 1; +// HSSFPictureData picData = pictures.get(pictureIndex); +// String picIndex = anchor.getRow1() + "_" + anchor.getCol1(); +// sheetIndexPicMap.put(picIndex, picData); +// } +// } +// return sheetIndexPicMap; +// } +// else +// { +// return sheetIndexPicMap; +// } +// } +// +// /** +// * 获取Excel2007图片 +// * +// * @param sheet 当前sheet对象 +// * @param workbook 工作簿对象 +// * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData +// */ +// public static Map<String, PictureData> getSheetPictures07(XSSFSheet sheet, XSSFWorkbook workbook) +// { +// Map<String, PictureData> sheetIndexPicMap = new HashMap<String, PictureData>(); +// for (POIXMLDocumentPart dr : sheet.getRelations()) +// { +// if (dr instanceof XSSFDrawing) +// { +// XSSFDrawing drawing = (XSSFDrawing) dr; +// List<XSSFShape> shapes = drawing.getShapes(); +// for (XSSFShape shape : shapes) +// { +// if (shape instanceof XSSFPicture) +// { +// XSSFPicture pic = (XSSFPicture) shape; +// XSSFClientAnchor anchor = pic.getPreferredSize(); +// CTMarker ctMarker = anchor.getFrom(); +// String picIndex = ctMarker.getRow() + "_" + ctMarker.getCol(); +// sheetIndexPicMap.put(picIndex, pic.getPictureData()); +// } +// } +// } +// } +// return sheetIndexPicMap; +// } +// +// /** +// * 格式化不同类型的日期对象 +// * +// * @param dateFormat 日期格式 +// * @param val 被格式化的日期对象 +// * @return 格式化后的日期字符 +// */ +// public String parseDateToStr(String dateFormat, Object val) +// { +// if (val == null) +// { +// return ""; +// } +// String str; +// if (val instanceof Date) +// { +// str = DateUtils.parseDateToStr(dateFormat, (Date) val); +// } +// else if (val instanceof LocalDateTime) +// { +// str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDateTime) val)); +// } +// else if (val instanceof LocalDate) +// { +// str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDate) val)); +// } +// else +// { +// str = val.toString(); +// } +// return str; +// } +// +// /** +// * 是否有对象的子列表 +// */ +// public boolean isSubList() +// { +// return StringUtils.isNotNull(subFields) && subFields.size() > 0; +// } +// +// /** +// * 是否有对象的子列表,集合不为空 +// */ +// public boolean isSubListValue(T vo) +// { +// return StringUtils.isNotNull(subFields) && subFields.size() > 0 && StringUtils.isNotNull(getListCellValue(vo)) && getListCellValue(vo).size() > 0; +// } +// +// /** +// * 获取集合的值 +// */ +// public Collection<?> getListCellValue(Object obj) +// { +// Object value; +// try +// { +// value = subMethod.invoke(obj, new Object[] {}); +// } +// catch (Exception e) +// { +// return new ArrayList<Object>(); +// } +// return (Collection<?>) value; +// } +// +// /** +// * 获取对象的子列表方法 +// * +// * @param name 名称 +// * @param pojoClass 类对象 +// * @return 子列表方法 +// */ +// public Method getSubMethod(String name, Class<?> pojoClass) +// { +// StringBuffer getMethodName = new StringBuffer("get"); +// getMethodName.append(name.substring(0, 1).toUpperCase()); +// getMethodName.append(name.substring(1)); +// Method method = null; +// try +// { +// method = pojoClass.getMethod(getMethodName.toString(), new Class[] {}); +// } +// catch (Exception e) +// { +// log.error("获取对象异常{}", e.getMessage()); +// } +// return method; +// } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java new file mode 100644 index 0000000..64d47ca --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java @@ -0,0 +1,409 @@ +package com.ruoyi.common.utils.reflect; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Date; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.utils.DateUtils; + +/** + * 反射工具类. 提供调用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); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Base64.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Base64.java new file mode 100644 index 0000000..ca1cd92 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Base64.java @@ -0,0 +1,291 @@ +package com.ruoyi.common.utils.sign; + +/** + * Base64工具类 + * + * @author ruoyi + */ +public final class Base64 +{ + static private final int BASELENGTH = 128; + static private final int LOOKUPLENGTH = 64; + static private final int TWENTYFOURBITGROUP = 24; + static private final int EIGHTBIT = 8; + static private final int SIXTEENBIT = 16; + static private final int FOURBYTE = 4; + static private final int SIGN = -128; + static private final char PAD = '='; + static final private byte[] base64Alphabet = new byte[BASELENGTH]; + static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH]; + + static + { + for (int i = 0; i < BASELENGTH; ++i) + { + base64Alphabet[i] = -1; + } + for (int i = 'Z'; i >= 'A'; i--) + { + base64Alphabet[i] = (byte) (i - 'A'); + } + for (int i = 'z'; i >= 'a'; i--) + { + base64Alphabet[i] = (byte) (i - 'a' + 26); + } + + for (int i = '9'; i >= '0'; i--) + { + base64Alphabet[i] = (byte) (i - '0' + 52); + } + + base64Alphabet['+'] = 62; + base64Alphabet['/'] = 63; + + for (int i = 0; i <= 25; i++) + { + lookUpBase64Alphabet[i] = (char) ('A' + i); + } + + for (int i = 26, j = 0; i <= 51; i++, j++) + { + lookUpBase64Alphabet[i] = (char) ('a' + j); + } + + for (int i = 52, j = 0; i <= 61; i++, j++) + { + lookUpBase64Alphabet[i] = (char) ('0' + j); + } + lookUpBase64Alphabet[62] = (char) '+'; + lookUpBase64Alphabet[63] = (char) '/'; + } + + private static boolean isWhiteSpace(char octect) + { + return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9); + } + + private static boolean isPad(char octect) + { + return (octect == PAD); + } + + private static boolean isData(char octect) + { + return (octect < BASELENGTH && base64Alphabet[octect] != -1); + } + + /** + * Encodes hex octects into Base64 + * + * @param binaryData Array containing binaryData + * @return Encoded Base64 array + */ + public static String encode(byte[] binaryData) + { + if (binaryData == null) + { + return null; + } + + int lengthDataBits = binaryData.length * EIGHTBIT; + if (lengthDataBits == 0) + { + return ""; + } + + int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP; + int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP; + int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets; + char encodedData[] = null; + + encodedData = new char[numberQuartet * 4]; + + byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0; + + int encodedIndex = 0; + int dataIndex = 0; + + for (int i = 0; i < numberTriplets; i++) + { + b1 = binaryData[dataIndex++]; + b2 = binaryData[dataIndex++]; + b3 = binaryData[dataIndex++]; + + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f]; + } + + // form integral number of 6-bit groups + if (fewerThan24bits == EIGHTBIT) + { + b1 = binaryData[dataIndex]; + k = (byte) (b1 & 0x03); + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4]; + encodedData[encodedIndex++] = PAD; + encodedData[encodedIndex++] = PAD; + } + else if (fewerThan24bits == SIXTEENBIT) + { + b1 = binaryData[dataIndex]; + b2 = binaryData[dataIndex + 1]; + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2]; + encodedData[encodedIndex++] = PAD; + } + return new String(encodedData); + } + + /** + * Decodes Base64 data into octects + * + * @param encoded string containing Base64 data + * @return Array containind decoded data. + */ + public static byte[] decode(String encoded) + { + if (encoded == null) + { + return null; + } + + char[] base64Data = encoded.toCharArray(); + // remove white spaces + int len = removeWhiteSpace(base64Data); + + if (len % FOURBYTE != 0) + { + return null;// should be divisible by four + } + + int numberQuadruple = (len / FOURBYTE); + + if (numberQuadruple == 0) + { + return new byte[0]; + } + + byte decodedData[] = null; + byte b1 = 0, b2 = 0, b3 = 0, b4 = 0; + char d1 = 0, d2 = 0, d3 = 0, d4 = 0; + + int i = 0; + int encodedIndex = 0; + int dataIndex = 0; + decodedData = new byte[(numberQuadruple) * 3]; + + for (; i < numberQuadruple - 1; i++) + { + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++])) + || !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++]))) + { + return null; + } // if found "no data" just return null + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + } + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) + { + return null;// if found "no data" just return null + } + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + + d3 = base64Data[dataIndex++]; + d4 = base64Data[dataIndex++]; + if (!isData((d3)) || !isData((d4))) + {// Check if they are PAD characters + if (isPad(d3) && isPad(d4)) + { + if ((b2 & 0xf) != 0)// last 4 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 1]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); + return tmp; + } + else if (!isPad(d3) && isPad(d4)) + { + b3 = base64Alphabet[d3]; + if ((b3 & 0x3) != 0)// last 2 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 2]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + return tmp; + } + else + { + return null; + } + } + else + { // No PAD e.g 3cQl + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + + } + return decodedData; + } + + /** + * remove WhiteSpace from MIME containing encoded Base64 data. + * + * @param data the byte array of base64 data (with WS) + * @return the new length + */ + private static int removeWhiteSpace(char[] data) + { + if (data == null) + { + return 0; + } + + // count characters that's not whitespace + int newSize = 0; + int len = data.length; + for (int i = 0; i < len; i++) + { + if (!isWhiteSpace(data[i])) + { + data[newSize++] = data[i]; + } + } + return newSize; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java new file mode 100644 index 0000000..c1c58db --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java @@ -0,0 +1,67 @@ +package com.ruoyi.common.utils.sign; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Md5加密方法 + * + * @author ruoyi + */ +public class Md5Utils +{ + private static final Logger log = LoggerFactory.getLogger(Md5Utils.class); + + private static byte[] md5(String s) + { + MessageDigest algorithm; + try + { + algorithm = MessageDigest.getInstance("MD5"); + algorithm.reset(); + algorithm.update(s.getBytes("UTF-8")); + byte[] messageDigest = algorithm.digest(); + return messageDigest; + } + catch (Exception e) + { + log.error("MD5 Error...", e); + } + return null; + } + + private static final String toHex(byte hash[]) + { + if (hash == null) + { + return null; + } + StringBuffer buf = new StringBuffer(hash.length * 2); + int i; + + for (i = 0; i < hash.length; i++) + { + if ((hash[i] & 0xff) < 0x10) + { + buf.append("0"); + } + buf.append(Long.toString(hash[i] & 0xff, 16)); + } + return buf.toString(); + } + + public static String hash(String s) + { + try + { + return new String(toHex(md5(s)).getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8); + } + catch (Exception e) + { + log.error("not supported charset...{}", e); + return s; + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java new file mode 100644 index 0000000..f290ec3 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java @@ -0,0 +1,158 @@ +package com.ruoyi.common.utils.spring; + +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; +import com.ruoyi.common.utils.StringUtils; + +/** + * spring工具类 方便在非spring管理环境中获取bean + * + * @author ruoyi + */ +@Component +public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware +{ + /** Spring应用上下文环境 */ + private static ConfigurableListableBeanFactory beanFactory; + + private static ApplicationContext applicationContext; + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException + { + SpringUtils.beanFactory = beanFactory; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException + { + SpringUtils.applicationContext = applicationContext; + } + + /** + * 获取对象 + * + * @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); + } + + /** + * 获取类型为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; + } + + /** + * 如果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 StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null; + } + + /** + * 获取配置文件中的值 + * + * @param key 配置文件的key + * @return 当前的配置文件的值 + * + */ + public static String getRequiredProperty(String key) + { + return applicationContext.getEnvironment().getRequiredProperty(key); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java new file mode 100644 index 0000000..93b0347 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java @@ -0,0 +1,70 @@ +package com.ruoyi.common.utils.sql; + +import com.ruoyi.common.exception.UtilException; +import com.ruoyi.common.utils.StringUtils; + +/** + * sql操作工具类 + * + * @author ruoyi + */ +public class SqlUtil +{ + /** + * 定义常用的 sql关键字 + */ + public static String SQL_REGEX = "and |extractvalue|updatexml|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |+|user()"; + + /** + * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) + */ + public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+"; + + /** + * 限制orderBy最大长度 + */ + private static final int ORDER_BY_MAX_LENGTH = 500; + + /** + * 检查字符,防止注入绕过 + */ + public static String escapeOrderBySql(String value) + { + if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) + { + throw new UtilException("参数不符合规范,不能进行查询"); + } + if (StringUtils.length(value) > ORDER_BY_MAX_LENGTH) + { + throw new UtilException("参数已超过最大限制,不能进行查询"); + } + return value; + } + + /** + * 验证 order by 语法是否符合规范 + */ + public static boolean isValidOrderBySql(String value) + { + return value.matches(SQL_PATTERN); + } + + /** + * SQL关键字检查 + */ + public static void filterKeyword(String value) + { + if (StringUtils.isEmpty(value)) + { + return; + } + String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|"); + for (String sqlKeyword : sqlKeywords) + { + if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) + { + throw new UtilException("参数存在SQL注入风险"); + } + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/IdUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/IdUtils.java new file mode 100644 index 0000000..2c84427 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/IdUtils.java @@ -0,0 +1,49 @@ +package com.ruoyi.common.utils.uuid; + +/** + * ID生成器工具类 + * + * @author ruoyi + */ +public class IdUtils +{ + /** + * 获取随机UUID + * + * @return 随机UUID + */ + public static String randomUUID() + { + return UUID.randomUUID().toString(); + } + + /** + * 简化的UUID,去掉了横线 + * + * @return 简化的UUID,去掉了横线 + */ + public static String simpleUUID() + { + return UUID.randomUUID().toString(true); + } + + /** + * 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID + * + * @return 随机UUID + */ + public static String fastUUID() + { + return UUID.fastUUID().toString(); + } + + /** + * 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID + * + * @return 简化的UUID,去掉了横线 + */ + public static String fastSimpleUUID() + { + return UUID.fastUUID().toString(true); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/Seq.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/Seq.java new file mode 100644 index 0000000..bf99611 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/Seq.java @@ -0,0 +1,86 @@ +package com.ruoyi.common.utils.uuid; + +import java.util.concurrent.atomic.AtomicInteger; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; + +/** + * @author ruoyi 序列生成类 + */ +public class Seq +{ + // 通用序列类型 + public static final String commSeqType = "COMMON"; + + // 上传序列类型 + public static final String uploadSeqType = "UPLOAD"; + + // 通用接口序列数 + private static AtomicInteger commSeq = new AtomicInteger(1); + + // 上传接口序列数 + private static AtomicInteger uploadSeq = new AtomicInteger(1); + + // 机器标识 + private static final String machineCode = "A"; + + /** + * 获取通用序列号 + * + * @return 序列值 + */ + public static String getId() + { + return getId(commSeqType); + } + + /** + * 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串 + * + * @return 序列值 + */ + public static String getId(String type) + { + AtomicInteger atomicInt = commSeq; + if (uploadSeqType.equals(type)) + { + atomicInt = uploadSeq; + } + return getId(atomicInt, 3); + } + + /** + * 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串 + * + * @param atomicInt 序列数 + * @param length 数值长度 + * @return 序列值 + */ + public static String getId(AtomicInteger atomicInt, int length) + { + String result = DateUtils.dateTimeNow(); + result += machineCode; + result += getSeq(atomicInt, length); + return result; + } + + /** + * 序列循环递增字符串[1, 10 的 (length)幂次方), 用0左补齐length位数 + * + * @return 序列值 + */ + private synchronized static String getSeq(AtomicInteger atomicInt, int length) + { + // 先取值再+1 + int value = atomicInt.getAndIncrement(); + + // 如果更新后值>=10 的 (length)幂次方则重置为1 + int maxSeq = (int) Math.pow(10, length); + if (atomicInt.get() >= maxSeq) + { + atomicInt.set(1); + } + // 转字符串,用0左补齐 + return StringUtils.padl(value, length); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/UUID.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/UUID.java new file mode 100644 index 0000000..a5585d6 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/UUID.java @@ -0,0 +1,484 @@ +package com.ruoyi.common.utils.uuid; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; +import com.ruoyi.common.exception.UtilException; + +/** + * 提供通用唯一识别码(universally unique identifier)(UUID)实现 + * + * @author ruoyi + */ +public final class UUID implements java.io.Serializable, Comparable<UUID> +{ + private static final long serialVersionUID = -1185015143654744140L; + + /** + * SecureRandom 的单例 + * + */ + private static class Holder + { + static final SecureRandom numberGenerator = getSecureRandom(); + } + + /** 此UUID的最高64有效位 */ + private final long mostSigBits; + + /** 此UUID的最低64有效位 */ + private final long leastSigBits; + + /** + * 私有构造 + * + * @param data 数据 + */ + private UUID(byte[] data) + { + long msb = 0; + long lsb = 0; + assert data.length == 16 : "data must be 16 bytes in length"; + for (int i = 0; i < 8; i++) + { + msb = (msb << 8) | (data[i] & 0xff); + } + for (int i = 8; i < 16; i++) + { + lsb = (lsb << 8) | (data[i] & 0xff); + } + this.mostSigBits = msb; + this.leastSigBits = lsb; + } + + /** + * 使用指定的数据构造新的 UUID。 + * + * @param mostSigBits 用于 {@code UUID} 的最高有效 64 位 + * @param leastSigBits 用于 {@code UUID} 的最低有效 64 位 + */ + public UUID(long mostSigBits, long leastSigBits) + { + this.mostSigBits = mostSigBits; + this.leastSigBits = leastSigBits; + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID fastUUID() + { + return randomUUID(false); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID() + { + return randomUUID(true); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能 + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID(boolean isSecure) + { + final Random ng = isSecure ? Holder.numberGenerator : getRandom(); + + byte[] randomBytes = new byte[16]; + ng.nextBytes(randomBytes); + randomBytes[6] &= 0x0f; /* clear version */ + randomBytes[6] |= 0x40; /* set to version 4 */ + randomBytes[8] &= 0x3f; /* clear variant */ + randomBytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(randomBytes); + } + + /** + * 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。 + * + * @param name 用于构造 UUID 的字节数组。 + * + * @return 根据指定数组生成的 {@code UUID} + */ + public static UUID nameUUIDFromBytes(byte[] name) + { + MessageDigest md; + try + { + md = MessageDigest.getInstance("MD5"); + } + catch (NoSuchAlgorithmException nsae) + { + throw new InternalError("MD5 not supported"); + } + byte[] md5Bytes = md.digest(name); + md5Bytes[6] &= 0x0f; /* clear version */ + md5Bytes[6] |= 0x30; /* set to version 3 */ + md5Bytes[8] &= 0x3f; /* clear variant */ + md5Bytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(md5Bytes); + } + + /** + * 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。 + * + * @param name 指定 {@code UUID} 字符串 + * @return 具有指定值的 {@code UUID} + * @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常 + * + */ + public static UUID fromString(String name) + { + String[] components = name.split("-"); + if (components.length != 5) + { + throw new IllegalArgumentException("Invalid UUID string: " + name); + } + for (int i = 0; i < 5; i++) + { + components[i] = "0x" + components[i]; + } + + long mostSigBits = Long.decode(components[0]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[1]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[2]).longValue(); + + long leastSigBits = Long.decode(components[3]).longValue(); + leastSigBits <<= 48; + leastSigBits |= Long.decode(components[4]).longValue(); + + return new UUID(mostSigBits, leastSigBits); + } + + /** + * 返回此 UUID 的 128 位值中的最低有效 64 位。 + * + * @return 此 UUID 的 128 位值中的最低有效 64 位。 + */ + public long getLeastSignificantBits() + { + return leastSigBits; + } + + /** + * 返回此 UUID 的 128 位值中的最高有效 64 位。 + * + * @return 此 UUID 的 128 位值中最高有效 64 位。 + */ + public long getMostSignificantBits() + { + return mostSigBits; + } + + /** + * 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。 + * <p> + * 版本号具有以下含意: + * <ul> + * <li>1 基于时间的 UUID + * <li>2 DCE 安全 UUID + * <li>3 基于名称的 UUID + * <li>4 随机生成的 UUID + * </ul> + * + * @return 此 {@code UUID} 的版本号 + */ + public int version() + { + // Version is bits masked by 0x000000000000F000 in MS long + return (int) ((mostSigBits >> 12) & 0x0f); + } + + /** + * 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。 + * <p> + * 变体号具有以下含意: + * <ul> + * <li>0 为 NCS 向后兼容保留 + * <li>2 <a href="http://www.ietf.org/rfc/rfc4122.txt">IETF RFC 4122</a>(Leach-Salz), 用于此类 + * <li>6 保留,微软向后兼容 + * <li>7 保留供以后定义使用 + * </ul> + * + * @return 此 {@code UUID} 相关联的变体号 + */ + public int variant() + { + // This field is composed of a varying number of bits. + // 0 - - Reserved for NCS backward compatibility + // 1 0 - The IETF aka Leach-Salz variant (used by this class) + // 1 1 0 Reserved, Microsoft backward compatibility + // 1 1 1 Reserved for future definition. + return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63)); + } + + /** + * 与此 UUID 相关联的时间戳值。 + * + * <p> + * 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。<br> + * 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。 + * + * <p> + * 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。<br> + * 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。 + */ + public long timestamp() throws UnsupportedOperationException + { + checkTimeBase(); + return (mostSigBits & 0x0FFFL) << 48// + | ((mostSigBits >> 16) & 0x0FFFFL) << 32// + | mostSigBits >>> 32; + } + + /** + * 与此 UUID 相关联的时钟序列值。 + * + * <p> + * 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。 + * <p> + * {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出 + * UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的时钟序列 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public int clockSequence() throws UnsupportedOperationException + { + checkTimeBase(); + return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48); + } + + /** + * 与此 UUID 相关的节点值。 + * + * <p> + * 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。 + * <p> + * 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。<br> + * 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的节点值 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public long node() throws UnsupportedOperationException + { + checkTimeBase(); + return leastSigBits & 0x0000FFFFFFFFFFFFL; + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + * <p> + * UUID 的字符串表示形式由此 BNF 描述: + * + * <pre> + * {@code + * UUID = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node> + * time_low = 4*<hexOctet> + * time_mid = 2*<hexOctet> + * time_high_and_version = 2*<hexOctet> + * variant_and_sequence = 2*<hexOctet> + * node = 6*<hexOctet> + * hexOctet = <hexDigit><hexDigit> + * hexDigit = [0-9a-fA-F] + * } + * </pre> + * + * </blockquote> + * + * @return 此{@code UUID} 的字符串表现形式 + * @see #toString(boolean) + */ + @Override + public String toString() + { + return toString(false); + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + * <p> + * UUID 的字符串表示形式由此 BNF 描述: + * + * <pre> + * {@code + * UUID = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node> + * time_low = 4*<hexOctet> + * time_mid = 2*<hexOctet> + * time_high_and_version = 2*<hexOctet> + * variant_and_sequence = 2*<hexOctet> + * node = 6*<hexOctet> + * hexOctet = <hexDigit><hexDigit> + * hexDigit = [0-9a-fA-F] + * } + * </pre> + * + * </blockquote> + * + * @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串 + * @return 此{@code UUID} 的字符串表现形式 + */ + public String toString(boolean isSimple) + { + final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36); + // time_low + builder.append(digits(mostSigBits >> 32, 8)); + if (!isSimple) + { + builder.append('-'); + } + // time_mid + builder.append(digits(mostSigBits >> 16, 4)); + if (!isSimple) + { + builder.append('-'); + } + // time_high_and_version + builder.append(digits(mostSigBits, 4)); + if (!isSimple) + { + builder.append('-'); + } + // variant_and_sequence + builder.append(digits(leastSigBits >> 48, 4)); + if (!isSimple) + { + builder.append('-'); + } + // node + builder.append(digits(leastSigBits, 12)); + + return builder.toString(); + } + + /** + * 返回此 UUID 的哈希码。 + * + * @return UUID 的哈希码值。 + */ + @Override + public int hashCode() + { + long hilo = mostSigBits ^ leastSigBits; + return ((int) (hilo >> 32)) ^ (int) hilo; + } + + /** + * 将此对象与指定对象比较。 + * <p> + * 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。 + * + * @param obj 要与之比较的对象 + * + * @return 如果对象相同,则返回 {@code true};否则返回 {@code false} + */ + @Override + public boolean equals(Object obj) + { + if ((null == obj) || (obj.getClass() != UUID.class)) + { + return false; + } + UUID id = (UUID) obj; + return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits); + } + + // Comparison Operations + + /** + * 将此 UUID 与指定的 UUID 比较。 + * + * <p> + * 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。 + * + * @param val 与此 UUID 比较的 UUID + * + * @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。 + * + */ + @Override + public int compareTo(UUID val) + { + // The ordering is intentionally set up so that the UUIDs + // can simply be numerically compared as two numbers + return (this.mostSigBits < val.mostSigBits ? -1 : // + (this.mostSigBits > val.mostSigBits ? 1 : // + (this.leastSigBits < val.leastSigBits ? -1 : // + (this.leastSigBits > val.leastSigBits ? 1 : // + 0)))); + } + + // ------------------------------------------------------------------------------------------------------------------- + // Private method start + /** + * 返回指定数字对应的hex值 + * + * @param val 值 + * @param digits 位 + * @return 值 + */ + private static String digits(long val, int digits) + { + long hi = 1L << (digits * 4); + return Long.toHexString(hi | (val & (hi - 1))).substring(1); + } + + /** + * 检查是否为time-based版本UUID + */ + private void checkTimeBase() + { + if (version() != 1) + { + throw new UnsupportedOperationException("Not a time-based UUID"); + } + } + + /** + * 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG) + * + * @return {@link SecureRandom} + */ + public static SecureRandom getSecureRandom() + { + try + { + return SecureRandom.getInstance("SHA1PRNG"); + } + catch (NoSuchAlgorithmException e) + { + throw new UtilException(e); + } + } + + /** + * 获取随机数生成器对象<br> + * ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。 + * + * @return {@link ThreadLocalRandom} + */ + public static ThreadLocalRandom getRandom() + { + return ThreadLocalRandom.current(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/xss/Xss.java b/ruoyi-common/src/main/java/com/ruoyi/common/xss/Xss.java new file mode 100644 index 0000000..7bfdf04 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/xss/Xss.java @@ -0,0 +1,27 @@ +package com.ruoyi.common.xss; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义xss校验注解 + * + * @author ruoyi + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(value = { ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER }) +@Constraint(validatedBy = { XssValidator.class }) +public @interface Xss +{ + String message() + + default "不允许任何脚本运行"; + + Class<?>[] groups() default {}; + + Class<? extends Payload>[] payload() default {}; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/xss/XssValidator.java b/ruoyi-common/src/main/java/com/ruoyi/common/xss/XssValidator.java new file mode 100644 index 0000000..ed9ec1f --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/xss/XssValidator.java @@ -0,0 +1,34 @@ +package com.ruoyi.common.xss; + +import com.ruoyi.common.utils.StringUtils; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 自定义xss校验注解实现 + * + * @author ruoyi + */ +public class XssValidator implements ConstraintValidator<Xss, String> +{ + private static final String HTML_PATTERN = "<(\\S*?)[^>]*>.*?|<.*? />"; + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) + { + if (StringUtils.isBlank(value)) + { + return true; + } + return !containsHtml(value); + } + + public static boolean containsHtml(String value) + { + Pattern pattern = Pattern.compile(HTML_PATTERN); + Matcher matcher = pattern.matcher(value); + return matcher.matches(); + } +} \ No newline at end of file diff --git a/ruoyi-framework/pom.xml b/ruoyi-framework/pom.xml new file mode 100644 index 0000000..9c6949d --- /dev/null +++ b/ruoyi-framework/pom.xml @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>zhengshengxin</artifactId> + <groupId>com.ruoyi</groupId> + <version>3.8.6</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>ruoyi-framework</artifactId> + + <description> + framework框架核心 + </description> + + <dependencies> + + <!-- SpringBoot Web容器 --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-web</artifactId> + </dependency> + + <!-- SpringBoot 拦截器 --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-aop</artifactId> + </dependency> + + <!-- 阿里数据库连接池 --> + <dependency> + <groupId>com.alibaba</groupId> + <artifactId>druid-spring-boot-starter</artifactId> + </dependency> + + <!-- 验证码 --> + <dependency> + <groupId>pro.fessional</groupId> + <artifactId>kaptcha</artifactId> + <exclusions> + <exclusion> + <artifactId>servlet-api</artifactId> + <groupId>javax.servlet</groupId> + </exclusion> + </exclusions> + </dependency> + + <!-- 获取系统信息 --> + <dependency> + <groupId>com.github.oshi</groupId> + <artifactId>oshi-core</artifactId> + </dependency> + + <!-- 系统模块--> + <dependency> + <groupId>com.ruoyi</groupId> + <artifactId>ruoyi-system</artifactId> + </dependency> + + </dependencies> + +</project> \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java new file mode 100644 index 0000000..1bc2f69 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java @@ -0,0 +1,174 @@ +package com.ruoyi.framework.aspectj; + +import java.util.ArrayList; +import java.util.List; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.stereotype.Component; +import com.ruoyi.common.annotation.DataScope; +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.security.context.PermissionContextHolder; + +/** + * 数据过滤处理 + * + * @author ruoyi + */ +@Aspect +@Component +public class DataScopeAspect +{ + /** + * 全部数据权限 + */ + public static final String DATA_SCOPE_ALL = "1"; + + /** + * 自定数据权限 + */ + public static final String DATA_SCOPE_CUSTOM = "2"; + + /** + * 部门数据权限 + */ + public static final String DATA_SCOPE_DEPT = "3"; + + /** + * 部门及以下数据权限 + */ + public static final String DATA_SCOPE_DEPT_AND_CHILD = "4"; + + /** + * 仅本人数据权限 + */ + public static final String DATA_SCOPE_SELF = "5"; + + /** + * 数据权限过滤关键字 + */ + public static final String DATA_SCOPE = "dataScope"; + + @Before("@annotation(controllerDataScope)") + public void doBefore(JoinPoint point, DataScope controllerDataScope) throws Throwable + { + clearDataScope(point); + handleDataScope(point, controllerDataScope); + } + + protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope) + { + // 获取当前的用户 + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNotNull(loginUser)) + { + SysUser currentUser = loginUser.getUser(); + // 如果是超级管理员,则不过滤数据 + if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) + { + String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), PermissionContextHolder.getContext()); + dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), + controllerDataScope.userAlias(), permission); + } + } + } + + /** + * 数据范围过滤 + * + * @param joinPoint 切点 + * @param user 用户 + * @param deptAlias 部门别名 + * @param userAlias 用户别名 + * @param permission 权限字符 + */ + public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, String permission) + { + StringBuilder sqlString = new StringBuilder(); + List<String> conditions = new ArrayList<String>(); + + for (SysRole role : user.getRoles()) + { + String dataScope = role.getDataScope(); + if (!DATA_SCOPE_CUSTOM.equals(dataScope) && conditions.contains(dataScope)) + { + continue; + } + if (StringUtils.isNotEmpty(permission) && StringUtils.isNotEmpty(role.getPermissions()) + && !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))) + { + continue; + } + if (DATA_SCOPE_ALL.equals(dataScope)) + { + sqlString = new StringBuilder(); + conditions.add(dataScope); + break; + } + else if (DATA_SCOPE_CUSTOM.equals(dataScope)) + { + sqlString.append(StringUtils.format( + " OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, + role.getRoleId())); + } + else if (DATA_SCOPE_DEPT.equals(dataScope)) + { + sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId())); + } + else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) + { + sqlString.append(StringUtils.format( + " OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", + deptAlias, user.getDeptId(), user.getDeptId())); + } + else if (DATA_SCOPE_SELF.equals(dataScope)) + { + if (StringUtils.isNotBlank(userAlias)) + { + sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId())); + } + else + { + // 数据权限为仅本人且没有userAlias别名不查询任何数据 + sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias)); + } + } + conditions.add(dataScope); + } + + // 多角色情况下,所有角色都不包含传递过来的权限字符,这个时候sqlString也会为空,所以要限制一下,不查询任何数据 + if (StringUtils.isEmpty(conditions)) + { + sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias)); + } + + if (StringUtils.isNotBlank(sqlString.toString())) + { + Object params = joinPoint.getArgs()[0]; + if (StringUtils.isNotNull(params) && params instanceof BaseEntity) + { + BaseEntity baseEntity = (BaseEntity) params; + baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")"); + } + } + } + + /** + * 拼接权限sql前先清空params.dataScope参数防止注入 + */ + private void clearDataScope(final JoinPoint joinPoint) + { + Object params = joinPoint.getArgs()[0]; + if (StringUtils.isNotNull(params) && params instanceof BaseEntity) + { + BaseEntity baseEntity = (BaseEntity) params; + baseEntity.getParams().put(DATA_SCOPE, ""); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java new file mode 100644 index 0000000..8c2c9f4 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java @@ -0,0 +1,72 @@ +package com.ruoyi.framework.aspectj; + +import java.util.Objects; +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; +import com.ruoyi.common.annotation.DataSource; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.datasource.DynamicDataSourceContextHolder; + +/** + * 多数据源处理 + * + * @author ruoyi + */ +@Aspect +@Order(1) +@Component +public class DataSourceAspect +{ + protected Logger logger = LoggerFactory.getLogger(getClass()); + + @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); + + if (StringUtils.isNotNull(dataSource)) + { + DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name()); + } + + 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; + } + + return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java new file mode 100644 index 0000000..5f5d764 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java @@ -0,0 +1,267 @@ +package com.ruoyi.framework.aspectj; + +import java.util.Collection; +import java.util.Map; +import java.util.Objects; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.system.service.ISysRoleService; +import org.apache.commons.lang3.ArrayUtils; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.NamedThreadLocal; +import org.springframework.stereotype.Component; +import org.springframework.validation.BindingResult; +import org.springframework.web.multipart.MultipartFile; +import com.alibaba.fastjson2.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.filter.PropertyPreExcludeFilter; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.system.domain.SysOperLog; + +/** + * 操作日志记录处理 + * + * @author ruoyi + */ +@Aspect +@Component +public class LogAspect +{ + @Autowired + private ISysRoleService roleService; + + private static final Logger log = LoggerFactory.getLogger(LogAspect.class); + + /** 排除敏感属性字段 */ + public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" }; + + /** 计算操作消耗时间 */ + private static final ThreadLocal<Long> TIME_THREADLOCAL = new NamedThreadLocal<Long>("Cost Time"); + + /** + * 处理请求前执行 + */ + @Before(value = "@annotation(controllerLog)") + public void boBefore(JoinPoint joinPoint, Log controllerLog) + { + TIME_THREADLOCAL.set(System.currentTimeMillis()); + } + + /** + * 处理完请求后执行 + * + * @param joinPoint 切点 + */ + @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult") + public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) + { + handleLog(joinPoint, controllerLog, null, jsonResult); + } + + /** + * 拦截异常操作 + * + * @param joinPoint 切点 + * @param e 异常 + */ + @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e") + public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) + { + handleLog(joinPoint, controllerLog, e, null); + } + + protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) + { + try + { + // 获取当前的用户 + LoginUser loginUser = SecurityUtils.getLoginUser(); + + // *========数据库日志=========*// + SysOperLog operLog = new SysOperLog(); + operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); + // 请求的地址 + String ip = IpUtils.getIpAddr(); + operLog.setOperIp(ip); + operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255)); + if (loginUser != null) + { + operLog.setOperName(loginUser.getUsername()); + operLog.setUserId(loginUser.getUserId()); + if(Objects.nonNull(loginUser.getUser())){ + operLog.setNickName(loginUser.getUser().getNickName()); + operLog.setPhonenumber(loginUser.getUser().getPhonenumber()); + // 设置角色名称 + SysRole sysRole = roleService.selectRoleByUserId(loginUser.getUserId()); + if(Objects.nonNull(sysRole)){ + operLog.setRoleName(sysRole.getRoleName()); + } + } + } + + if (e != null) + { + operLog.setStatus(BusinessStatus.FAIL.ordinal()); + operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000)); + } + // 设置方法名称 + String className = joinPoint.getTarget().getClass().getName(); + String methodName = joinPoint.getSignature().getName(); + operLog.setMethod(className + "." + methodName + "()"); + // 设置请求方式 + operLog.setRequestMethod(ServletUtils.getRequest().getMethod()); + // 处理设置注解上的参数 + getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult); + // 设置消耗时间 + operLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get()); + // 保存数据库 + AsyncManager.me().execute(AsyncFactory.recordOper(operLog)); + } + catch (Exception exp) + { + // 记录本地异常日志 + log.error("异常信息:{}", exp.getMessage()); + exp.printStackTrace(); + } + finally + { + TIME_THREADLOCAL.remove(); + } + } + + /** + * 获取注解中对方法的描述信息 用于Controller层注解 + * + * @param log 日志 + * @param operLog 操作日志 + * @throws Exception + */ + public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception + { + // 设置action动作 + operLog.setBusinessType(log.businessType().ordinal()); + // 设置标题 + operLog.setTitle(log.title()); + // 设置操作人类别 + operLog.setOperatorType(log.operatorType().ordinal()); + // 是否需要保存request,参数和值 + if (log.isSaveRequestData()) + { + // 获取参数的信息,传入到数据库中。 + setRequestValue(joinPoint, operLog, log.excludeParamNames()); + } + // 是否需要保存response,参数和值 + if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult)) + { + operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000)); + } + } + + /** + * 获取请求的参数,放到log中 + * + * @param operLog 操作日志 + * @throws Exception 异常 + */ + private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog, String[] excludeParamNames) throws Exception + { + Map<?, ?> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest()); + String requestMethod = operLog.getRequestMethod(); + if (StringUtils.isEmpty(paramsMap) + && (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod))) + { + String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames); + operLog.setOperParam(StringUtils.substring(params, 0, 2000)); + } + else + { + operLog.setOperParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, 2000)); + } + } + + /** + * 参数拼装 + */ + private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames) + { + String params = ""; + if (paramsArray != null && paramsArray.length > 0) + { + for (Object o : paramsArray) + { + if (StringUtils.isNotNull(o) && !isFilterObject(o)) + { + try + { + String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter(excludeParamNames)); + params += jsonObj.toString() + " "; + } + catch (Exception e) + { + } + } + } + } + return params.trim(); + } + + /** + * 忽略敏感属性 + */ + public PropertyPreExcludeFilter excludePropertyPreFilter(String[] excludeParamNames) + { + return new PropertyPreExcludeFilter().addExcludes(ArrayUtils.addAll(EXCLUDE_PROPERTIES, excludeParamNames)); + } + + /** + * 判断是否需要过滤的对象。 + * + * @param o 对象信息。 + * @return 如果是需要过滤的对象,则返回true;否则返回false。 + */ + @SuppressWarnings("rawtypes") + public boolean isFilterObject(final Object o) + { + Class<?> clazz = o.getClass(); + if (clazz.isArray()) + { + return clazz.getComponentType().isAssignableFrom(MultipartFile.class); + } + else if (Collection.class.isAssignableFrom(clazz)) + { + Collection collection = (Collection) o; + for (Object value : collection) + { + return value instanceof MultipartFile; + } + } + else if (Map.class.isAssignableFrom(clazz)) + { + Map map = (Map) o; + 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; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java new file mode 100644 index 0000000..b720bc1 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java @@ -0,0 +1,89 @@ +package com.ruoyi.framework.aspectj; + +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.script.RedisScript; +import org.springframework.stereotype.Component; +import com.ruoyi.common.annotation.RateLimiter; +import com.ruoyi.common.enums.LimitType; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.IpUtils; + +/** + * 限流处理 + * + * @author ruoyi + */ +@Aspect +@Component +public class RateLimiterAspect +{ + private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class); + + private RedisTemplate<Object, Object> redisTemplate; + + private RedisScript<Long> limitScript; + + @Autowired + public void setRedisTemplate1(RedisTemplate<Object, Object> redisTemplate) + { + this.redisTemplate = redisTemplate; + } + + @Autowired + public void setLimitScript(RedisScript<Long> limitScript) + { + this.limitScript = limitScript; + } + + @Before("@annotation(rateLimiter)") + public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable + { + int time = rateLimiter.time(); + int count = rateLimiter.count(); + + String combineKey = getCombineKey(rateLimiter, point); + List<Object> keys = Collections.singletonList(combineKey); + try + { + Long number = redisTemplate.execute(limitScript, keys, count, time); + if (StringUtils.isNull(number) || number.intValue() > count) + { + throw new ServiceException("访问过于频繁,请稍候再试"); + } + log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), combineKey); + } + catch (ServiceException e) + { + throw e; + } + catch (Exception e) + { + throw new RuntimeException("服务器限流异常,请稍候再试"); + } + } + + public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) + { + StringBuffer stringBuffer = new StringBuffer(rateLimiter.key()); + if (rateLimiter.limitType() == LimitType.IP) + { + stringBuffer.append(IpUtils.getIpAddr()).append("-"); + } + MethodSignature signature = (MethodSignature) point.getSignature(); + Method method = signature.getMethod(); + Class<?> targetClass = method.getDeclaringClass(); + stringBuffer.append(targetClass.getName()).append("-").append(method.getName()); + return stringBuffer.toString(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java new file mode 100644 index 0000000..1d4dc1f --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java @@ -0,0 +1,30 @@ +package com.ruoyi.framework.config; + +import java.util.TimeZone; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; + +/** + * 程序注解配置 + * + * @author ruoyi + */ +@Configuration +// 表示通过aop框架暴露该代理对象,AopContext能够访问 +@EnableAspectJAutoProxy(exposeProxy = true) +// 指定要扫描的Mapper类的包的路径 +@MapperScan("com.ruoyi.**.mapper") +public class ApplicationConfig +{ + /** + * 时区配置 + */ + @Bean + public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() + { + return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault()); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/CaptchaConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/CaptchaConfig.java new file mode 100644 index 0000000..43e78ae --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/CaptchaConfig.java @@ -0,0 +1,83 @@ +package com.ruoyi.framework.config; + +import java.util.Properties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import com.google.code.kaptcha.impl.DefaultKaptcha; +import com.google.code.kaptcha.util.Config; +import static com.google.code.kaptcha.Constants.*; + +/** + * 验证码配置 + * + * @author ruoyi + */ +@Configuration +public class CaptchaConfig +{ + @Bean(name = "captchaProducer") + public DefaultKaptcha getKaptchaBean() + { + DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); + Properties properties = new Properties(); + // 是否有边框 默认为true 我们可以自己设置yes,no + properties.setProperty(KAPTCHA_BORDER, "yes"); + // 验证码文本字符颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black"); + // 验证码图片宽度 默认为200 + properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160"); + // 验证码图片高度 默认为50 + properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60"); + // 验证码文本字符大小 默认为40 + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38"); + // KAPTCHA_SESSION_KEY + properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode"); + // 验证码文本字符长度 默认为5 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4"); + // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize) + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); + // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy + properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); + Config config = new Config(properties); + defaultKaptcha.setConfig(config); + return defaultKaptcha; + } + + @Bean(name = "captchaProducerMath") + public DefaultKaptcha getKaptchaBeanMath() + { + DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); + Properties properties = new Properties(); + // 是否有边框 默认为true 我们可以自己设置yes,no + properties.setProperty(KAPTCHA_BORDER, "yes"); + // 边框颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90"); + // 验证码文本字符颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue"); + // 验证码图片宽度 默认为200 + properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160"); + // 验证码图片高度 默认为50 + properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60"); + // 验证码文本字符大小 默认为40 + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35"); + // KAPTCHA_SESSION_KEY + properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath"); + // 验证码文本生成器 + properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.ruoyi.framework.config.KaptchaTextCreator"); + // 验证码文本字符间距 默认为2 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3"); + // 验证码文本字符长度 默认为5 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6"); + // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize) + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); + // 验证码噪点颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_NOISE_COLOR, "white"); + // 干扰实现类 + properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise"); + // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy + properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); + Config config = new Config(properties); + defaultKaptcha.setConfig(config); + return defaultKaptcha; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java new file mode 100644 index 0000000..f6abac1 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java @@ -0,0 +1,126 @@ +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 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; + +/** + * 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) + { + } + } + + /** + * 去除监控页面底部的广告 + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true") + public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties) + { + // 获取web监控页面的参数 + DruidStatProperties.StatViewServlet config = properties.getStatViewServlet(); + // 提取common.js的配置路径 + String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*"; + String commonJsPattern = pattern.replaceAll("\\*", "js/common.js"); + final String filePath = "support/http/resources/js/common.js"; + // 创建filter进行过滤 + Filter filter = new Filter() + { + @Override + public void init(javax.servlet.FilterConfig filterConfig) throws ServletException + { + } + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + chain.doFilter(request, response); + // 重置缓冲区,响应头不会被重置 + response.resetBuffer(); + // 获取common.js + String text = Utils.readFromResource(filePath); + // 正则替换banner, 除去底部的广告信息 + text = text.replaceAll("<a.*?banner\"></a><br/>", ""); + text = text.replaceAll("powered.*?shrek.wang</a>", ""); + response.getWriter().write(text); + } + @Override + public void destroy() + { + } + }; + FilterRegistrationBean registrationBean = new FilterRegistrationBean(); + registrationBean.setFilter(filter); + registrationBean.addUrlPatterns(commonJsPattern); + return registrationBean; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java new file mode 100644 index 0000000..4adbb7f --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java @@ -0,0 +1,52 @@ +package com.ruoyi.framework.config; + +import java.nio.charset.Charset; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.SerializationException; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONReader; +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.filter.Filter; +import com.ruoyi.common.constant.Constants; + +/** + * Redis使用FastJson序列化 + * + * @author ruoyi + */ +public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T> +{ + public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + + static final Filter AUTO_TYPE_FILTER = JSONReader.autoTypeFilter(Constants.JSON_WHITELIST_STR); + + private Class<T> clazz; + + public FastJson2JsonRedisSerializer(Class<T> clazz) + { + super(); + this.clazz = clazz; + } + + @Override + public byte[] serialize(T t) throws SerializationException + { + if (t == null) + { + return new byte[0]; + } + return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET); + } + + @Override + public T deserialize(byte[] bytes) throws SerializationException + { + if (bytes == null || bytes.length <= 0) + { + return null; + } + String str = new String(bytes, DEFAULT_CHARSET); + + return JSON.parseObject(str, clazz, AUTO_TYPE_FILTER); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java new file mode 100644 index 0000000..bb14c04 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java @@ -0,0 +1,58 @@ +package com.ruoyi.framework.config; + +import java.util.HashMap; +import java.util.Map; +import javax.servlet.DispatcherType; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import com.ruoyi.common.filter.RepeatableFilter; +import com.ruoyi.common.filter.XssFilter; +import com.ruoyi.common.utils.StringUtils; + +/** + * Filter配置 + * + * @author ruoyi + */ +@Configuration +public class FilterConfig +{ + @Value("${xss.excludes}") + private String excludes; + + @Value("${xss.urlPatterns}") + private String urlPatterns; + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + @ConditionalOnProperty(value = "xss.enabled", havingValue = "true") + public FilterRegistrationBean xssFilterRegistration() + { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setDispatcherTypes(DispatcherType.REQUEST); + registration.setFilter(new XssFilter()); + registration.addUrlPatterns(StringUtils.split(urlPatterns, ",")); + registration.setName("xssFilter"); + registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE); + Map<String, String> initParameters = new HashMap<String, String>(); + initParameters.put("excludes", excludes); + registration.setInitParameters(initParameters); + return registration; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + public FilterRegistrationBean someFilterRegistration() + { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setFilter(new RepeatableFilter()); + registration.addUrlPatterns("/*"); + registration.setName("repeatableFilter"); + registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE); + return registration; + } + +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java new file mode 100644 index 0000000..7f8e1d5 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java @@ -0,0 +1,68 @@ +package com.ruoyi.framework.config; + +import java.util.Random; +import com.google.code.kaptcha.text.impl.DefaultTextCreator; + +/** + * 验证码文本生成器 + * + * @author ruoyi + */ +public class KaptchaTextCreator extends DefaultTextCreator +{ + private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(","); + + @Override + public String getText() + { + Integer result = 0; + Random random = new Random(); + int x = random.nextInt(10); + int y = random.nextInt(10); + StringBuilder suChinese = new StringBuilder(); + int randomoperands = random.nextInt(3); + if (randomoperands == 0) + { + result = x * y; + suChinese.append(CNUMBERS[x]); + suChinese.append("*"); + suChinese.append(CNUMBERS[y]); + } + else if (randomoperands == 1) + { + if ((x != 0) && y % x == 0) + { + result = y / x; + suChinese.append(CNUMBERS[y]); + suChinese.append("/"); + suChinese.append(CNUMBERS[x]); + } + else + { + result = x + y; + suChinese.append(CNUMBERS[x]); + suChinese.append("+"); + suChinese.append(CNUMBERS[y]); + } + } + else + { + if (x >= y) + { + result = x - y; + suChinese.append(CNUMBERS[x]); + suChinese.append("-"); + suChinese.append(CNUMBERS[y]); + } + else + { + result = y - x; + suChinese.append(CNUMBERS[y]); + suChinese.append("-"); + suChinese.append(CNUMBERS[x]); + } + } + suChinese.append("=?@" + result); + return suChinese.toString(); + } +} \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java new file mode 100644 index 0000000..bc6618a --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java @@ -0,0 +1,132 @@ +//package com.ruoyi.framework.config; +// +//import java.io.IOException; +//import java.util.ArrayList; +//import java.util.Arrays; +//import java.util.HashSet; +//import java.util.List; +//import javax.sql.DataSource; +//import org.apache.ibatis.io.VFS; +//import org.apache.ibatis.session.SqlSessionFactory; +//import org.mybatis.spring.SqlSessionFactoryBean; +//import org.mybatis.spring.boot.autoconfigure.SpringBootVFS; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import org.springframework.core.env.Environment; +//import org.springframework.core.io.DefaultResourceLoader; +//import org.springframework.core.io.Resource; +//import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +//import org.springframework.core.io.support.ResourcePatternResolver; +//import org.springframework.core.type.classreading.CachingMetadataReaderFactory; +//import org.springframework.core.type.classreading.MetadataReader; +//import org.springframework.core.type.classreading.MetadataReaderFactory; +//import org.springframework.util.ClassUtils; +//import com.ruoyi.common.utils.StringUtils; +// +///** +// * Mybatis支持*匹配扫描包 +// * +// * @author ruoyi +// */ +//@Configuration +//public class MyBatisConfig +//{ +// @Autowired +// private Environment env; +// +// static final String DEFAULT_RESOURCE_PATTERN = "**/*.class"; +// +// public static String setTypeAliasesPackage(String typeAliasesPackage) +// { +// ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver(); +// MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver); +// List<String> allResult = new ArrayList<String>(); +// try +// { +// for (String aliasesPackage : typeAliasesPackage.split(",")) +// { +// List<String> result = new ArrayList<String>(); +// aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +// + ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN; +// Resource[] resources = resolver.getResources(aliasesPackage); +// if (resources != null && resources.length > 0) +// { +// MetadataReader metadataReader = null; +// for (Resource resource : resources) +// { +// if (resource.isReadable()) +// { +// metadataReader = metadataReaderFactory.getMetadataReader(resource); +// try +// { +// result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName()); +// } +// catch (ClassNotFoundException e) +// { +// e.printStackTrace(); +// } +// } +// } +// } +// if (result.size() > 0) +// { +// HashSet<String> hashResult = new HashSet<String>(result); +// allResult.addAll(hashResult); +// } +// } +// if (allResult.size() > 0) +// { +// typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0])); +// } +// else +// { +// throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包"); +// } +// } +// catch (IOException e) +// { +// e.printStackTrace(); +// } +// return typeAliasesPackage; +// } +// +// public Resource[] resolveMapperLocations(String[] mapperLocations) +// { +// ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver(); +// List<Resource> resources = new ArrayList<Resource>(); +// if (mapperLocations != null) +// { +// for (String mapperLocation : mapperLocations) +// { +// try +// { +// Resource[] mappers = resourceResolver.getResources(mapperLocation); +// resources.addAll(Arrays.asList(mappers)); +// } +// catch (IOException e) +// { +// // ignore +// } +// } +// } +// return resources.toArray(new Resource[resources.size()]); +// } +// +// @Bean +// public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception +// { +// String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage"); +// String mapperLocations = env.getProperty("mybatis.mapperLocations"); +// String configLocation = env.getProperty("mybatis.configLocation"); +// typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage); +// VFS.addImplClass(SpringBootVFS.class); +// +// final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); +// sessionFactory.setDataSource(dataSource); +// sessionFactory.setTypeAliasesPackage(typeAliasesPackage); +// sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ","))); +// sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation)); +// return sessionFactory.getObject(); +// } +//} \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java new file mode 100644 index 0000000..3f4f485 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java @@ -0,0 +1,69 @@ +package com.ruoyi.framework.config; + +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +/** + * redis配置 + * + * @author ruoyi + */ +@Configuration +@EnableCaching +public class RedisConfig extends CachingConfigurerSupport +{ + @Bean + @SuppressWarnings(value = { "unchecked", "rawtypes" }) + public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) + { + RedisTemplate<Object, Object> template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + + FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); + + // 使用StringRedisSerializer来序列化和反序列化redis的key值 + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(serializer); + + // Hash的key也采用StringRedisSerializer的序列化方式 + template.setHashKeySerializer(new StringRedisSerializer()); + template.setHashValueSerializer(serializer); + + template.afterPropertiesSet(); + return template; + } + + @Bean + public DefaultRedisScript<Long> limitScript() + { + DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(); + redisScript.setScriptText(limitScriptText()); + redisScript.setResultType(Long.class); + return redisScript; + } + + /** + * 限流脚本 + */ + private String limitScriptText() + { + return "local key = KEYS[1]\n" + + "local count = tonumber(ARGV[1])\n" + + "local time = tonumber(ARGV[2])\n" + + "local current = redis.call('get', key);\n" + + "if current and tonumber(current) > count then\n" + + " return tonumber(current);\n" + + "end\n" + + "current = redis.call('incr', key)\n" + + "if tonumber(current) == 1 then\n" + + " redis.call('expire', key, time)\n" + + "end\n" + + "return tonumber(current);"; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java new file mode 100644 index 0000000..74fb93e --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java @@ -0,0 +1,73 @@ +package com.ruoyi.framework.config; + +import java.util.concurrent.TimeUnit; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.CacheControl; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor; + +/** + * 通用配置 + * + * @author ruoyi + */ +@Configuration +public class ResourcesConfig implements WebMvcConfigurer +{ + @Autowired + private RepeatSubmitInterceptor repeatSubmitInterceptor; + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) + { + /** 本地文件上传路径 */ + registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**") + .addResourceLocations("file:" + RuoYiConfig.getProfile() + "/"); + + /** swagger配置 */ + registry.addResourceHandler("/swagger-ui/**") + .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/") + .setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic());; + } + + /** + * 自定义拦截规则 + */ + @Override + public void addInterceptors(InterceptorRegistry registry) + { + registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**"); + } + + /** + * 跨域配置 + */ + @Bean + public CorsFilter corsFilter() + { + CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); + // 设置访问源地址 + config.addAllowedOriginPattern("*"); + // 设置访问源请求头 + config.addAllowedHeader("*"); + // 设置访问源请求方法 + config.addAllowedMethod("*"); + // 有效期 1800秒 + config.setMaxAge(1800L); + // 添加映射路径,拦截一切请求 + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", config); + // 返回新的CorsFilter + return new CorsFilter(source); + } +} \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java new file mode 100644 index 0000000..96244f8 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java @@ -0,0 +1,157 @@ +package com.ruoyi.framework.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.authentication.logout.LogoutFilter; +import org.springframework.web.filter.CorsFilter; +import com.ruoyi.framework.config.properties.PermitAllUrlProperties; +import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter; +import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl; +import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl; + +/** + * spring security配置 + * + * @author ruoyi + */ +@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) +public class SecurityConfig extends WebSecurityConfigurerAdapter +{ + /** + * 自定义用户认证逻辑 + */ + @Autowired + private UserDetailsService userDetailsService; + + /** + * 认证失败处理类 + */ + @Autowired + private AuthenticationEntryPointImpl unauthorizedHandler; + + /** + * 退出处理类 + */ + @Autowired + private LogoutSuccessHandlerImpl logoutSuccessHandler; + + /** + * token认证过滤器 + */ + @Autowired + private JwtAuthenticationTokenFilter authenticationTokenFilter; + + /** + * 跨域过滤器 + */ + @Autowired + private CorsFilter corsFilter; + + /** + * 允许匿名访问的地址 + */ + @Autowired + private PermitAllUrlProperties permitAllUrl; + + /** + * 解决 无法直接注入 AuthenticationManager + * + * @return + * @throws Exception + */ + @Bean + @Override + public AuthenticationManager authenticationManagerBean() throws Exception + { + return super.authenticationManagerBean(); + } + + /** + * anyRequest | 匹配所有请求路径 + * access | SpringEl表达式结果为true时可以访问 + * anonymous | 匿名可以访问 + * denyAll | 用户不能访问 + * fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录) + * hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问 + * hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问 + * hasAuthority | 如果有参数,参数表示权限,则其权限可以访问 + * hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问 + * hasRole | 如果有参数,参数表示角色,则其角色可以访问 + * permitAll | 用户可以任意访问 + * rememberMe | 允许通过remember-me登录的用户访问 + * authenticated | 用户登录后可访问 + */ + @Override + protected void configure(HttpSecurity httpSecurity) throws Exception + { + // 注解标记允许匿名访问的url + ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests(); + permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll()); + + httpSecurity + // CSRF禁用,因为不使用session + .csrf().disable() + // 禁用HTTP响应标头 + .headers().cacheControl().disable().and() + // 认证失败处理类 + .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() + // 基于token,所以不需要session + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() + // 过滤请求 + .authorizeRequests() + // 对于登录login 注册register 验证码captchaImage 允许匿名访问 + .antMatchers("/getPrivacyAgreement/{agreementType}", + "/applet/queryProtocolConfigByType","/applet/login1", + "/login1","/applet/queryProtocolConfigByType", + "/register","/applet/getCode","/applet/loginCode", + "/applet/changepwd", "/captchaImage","/getCode","/loginCode", + "/operations/getBySingleNum/**", + "/user/getUserInfoByNumber/**", + "/wxLogin/**", + "/open/**","/cos/get/**" + ).permitAll() + // 静态资源,可匿名访问 + .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll() + .antMatchers("/swagger-ui.html","/doc.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll() + // 除上面外的所有请求全部需要鉴权认证 + .anyRequest().authenticated() + .and() + .headers().frameOptions().disable(); + // 添加Logout filter + httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler); + // 添加JWT filter + httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); + // 添加CORS filter + httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class); + httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class); + } + + /** + * 强散列哈希加密实现 + */ + @Bean + public BCryptPasswordEncoder bCryptPasswordEncoder() + { + return new BCryptPasswordEncoder(); + } + + /** + * 身份认证接口 + */ + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception + { + auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder()); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ServerConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ServerConfig.java new file mode 100644 index 0000000..b5b7de3 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ServerConfig.java @@ -0,0 +1,32 @@ +package com.ruoyi.framework.config; + +import javax.servlet.http.HttpServletRequest; +import org.springframework.stereotype.Component; +import com.ruoyi.common.utils.ServletUtils; + +/** + * 服务相关配置 + * + * @author ruoyi + */ +@Component +public class ServerConfig +{ + /** + * 获取完整的请求路径,包括:域名,端口,上下文访问路径 + * + * @return 服务地址 + */ + public String getUrl() + { + HttpServletRequest request = ServletUtils.getRequest(); + return getDomain(request); + } + + public static String getDomain(HttpServletRequest request) + { + StringBuffer url = request.getRequestURL(); + String contextPath = request.getServletContext().getContextPath(); + return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java new file mode 100644 index 0000000..7840141 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java @@ -0,0 +1,63 @@ +package com.ruoyi.framework.config; + +import com.ruoyi.common.utils.Threads; +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * 线程池配置 + * + * @author ruoyi + **/ +@Configuration +public class ThreadPoolConfig +{ + // 核心线程池大小 + private int corePoolSize = 50; + + // 最大可创建的线程数 + private int maxPoolSize = 200; + + // 队列最大长度 + private int queueCapacity = 1000; + + // 线程池维护线程所允许的空闲时间 + private int keepAliveSeconds = 300; + + @Bean(name = "threadPoolTaskExecutor") + public ThreadPoolTaskExecutor threadPoolTaskExecutor() + { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setMaxPoolSize(maxPoolSize); + executor.setCorePoolSize(corePoolSize); + executor.setQueueCapacity(queueCapacity); + executor.setKeepAliveSeconds(keepAliveSeconds); + // 线程池对拒绝任务(无线程可用)的处理策略 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + return executor; + } + + /** + * 执行周期性或定时任务 + */ + @Bean(name = "scheduledExecutorService") + protected ScheduledExecutorService scheduledExecutorService() + { + return new ScheduledThreadPoolExecutor(corePoolSize, + new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(), + new ThreadPoolExecutor.CallerRunsPolicy()) + { + @Override + protected void afterExecute(Runnable r, Throwable t) + { + super.afterExecute(r, t); + Threads.printException(r, t); + } + }; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java new file mode 100644 index 0000000..c8a5c8a --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java @@ -0,0 +1,89 @@ +package com.ruoyi.framework.config.properties; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import com.alibaba.druid.pool.DruidDataSource; + +/** + * druid 配置属性 + * + * @author ruoyi + */ +@Configuration +public class DruidProperties +{ + @Value("${spring.datasource.druid.initialSize}") + private int initialSize; + + @Value("${spring.datasource.druid.minIdle}") + private int minIdle; + + @Value("${spring.datasource.druid.maxActive}") + private int maxActive; + + @Value("${spring.datasource.druid.maxWait}") + private int maxWait; + + @Value("${spring.datasource.druid.connectTimeout}") + private int connectTimeout; + + @Value("${spring.datasource.druid.socketTimeout}") + private int socketTimeout; + + @Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}") + private int timeBetweenEvictionRunsMillis; + + @Value("${spring.datasource.druid.minEvictableIdleTimeMillis}") + private int minEvictableIdleTimeMillis; + + @Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}") + private int maxEvictableIdleTimeMillis; + + @Value("${spring.datasource.druid.validationQuery}") + private String validationQuery; + + @Value("${spring.datasource.druid.testWhileIdle}") + private boolean testWhileIdle; + + @Value("${spring.datasource.druid.testOnBorrow}") + private boolean testOnBorrow; + + @Value("${spring.datasource.druid.testOnReturn}") + private boolean testOnReturn; + + public DruidDataSource dataSource(DruidDataSource datasource) + { + /** 配置初始化大小、最小、最大 */ + datasource.setInitialSize(initialSize); + datasource.setMaxActive(maxActive); + datasource.setMinIdle(minIdle); + + /** 配置获取连接等待超时的时间 */ + datasource.setMaxWait(maxWait); + + /** 配置驱动连接超时时间,检测数据库建立连接的超时时间,单位是毫秒 */ + datasource.setConnectTimeout(connectTimeout); + + /** 配置网络超时时间,等待数据库操作完成的网络超时时间,单位是毫秒 */ + datasource.setSocketTimeout(socketTimeout); + + /** 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 */ + datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); + + /** 配置一个连接在池中最小、最大生存的时间,单位是毫秒 */ + datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); + datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis); + + /** + * 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。 + */ + datasource.setValidationQuery(validationQuery); + /** 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 */ + datasource.setTestWhileIdle(testWhileIdle); + /** 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */ + datasource.setTestOnBorrow(testOnBorrow); + /** 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */ + datasource.setTestOnReturn(testOnReturn); + return datasource; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java new file mode 100644 index 0000000..29118fa --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java @@ -0,0 +1,73 @@ +package com.ruoyi.framework.config.properties; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.regex.Pattern; +import org.apache.commons.lang3.RegExUtils; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; +import com.ruoyi.common.annotation.Anonymous; + +/** + * 设置Anonymous注解允许匿名访问的url + * + * @author ruoyi + */ +@Configuration +public class PermitAllUrlProperties implements InitializingBean, ApplicationContextAware +{ + private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}"); + + private ApplicationContext applicationContext; + + private List<String> urls = new ArrayList<>(); + + public String ASTERISK = "*"; + + @Override + public void afterPropertiesSet() + { + RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class); + Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods(); + + map.keySet().forEach(info -> { + HandlerMethod handlerMethod = map.get(info); + + // 获取方法上边的注解 替代path variable 为 * + Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class); + Optional.ofNullable(method).ifPresent(anonymous -> Objects.requireNonNull(info.getPatternsCondition().getPatterns()) + .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK)))); + + // 获取类上边的注解, 替代path variable 为 * + Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class); + Optional.ofNullable(controller).ifPresent(anonymous -> Objects.requireNonNull(info.getPatternsCondition().getPatterns()) + .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK)))); + }); + } + + @Override + public void setApplicationContext(ApplicationContext context) throws BeansException + { + this.applicationContext = context; + } + + public List<String> getUrls() + { + return urls; + } + + public void setUrls(List<String> urls) + { + this.urls = urls; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java new file mode 100644 index 0000000..e70b8cf --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java @@ -0,0 +1,26 @@ +package com.ruoyi.framework.datasource; + +import java.util.Map; +import javax.sql.DataSource; +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + +/** + * 动态数据源 + * + * @author ruoyi + */ +public class DynamicDataSource extends AbstractRoutingDataSource +{ + public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) + { + super.setDefaultTargetDataSource(defaultTargetDataSource); + super.setTargetDataSources(targetDataSources); + super.afterPropertiesSet(); + } + + @Override + protected Object determineCurrentLookupKey() + { + return DynamicDataSourceContextHolder.getDataSourceType(); + } +} \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java new file mode 100644 index 0000000..9770af6 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java @@ -0,0 +1,45 @@ +package com.ruoyi.framework.datasource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 数据源切换处理 + * + * @author ruoyi + */ +public class DynamicDataSourceContextHolder +{ + public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class); + + /** + * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本, + * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。 + */ + private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>(); + + /** + * 设置数据源的变量 + */ + public static void setDataSourceType(String dsType) + { + log.info("切换到{}数据源", dsType); + CONTEXT_HOLDER.set(dsType); + } + + /** + * 获得数据源的变量 + */ + public static String getDataSourceType() + { + return CONTEXT_HOLDER.get(); + } + + /** + * 清空数据源变量 + */ + public static void clearDataSourceType() + { + CONTEXT_HOLDER.remove(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java new file mode 100644 index 0000000..c49eaf4 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java @@ -0,0 +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.HandlerInterceptor; +import com.alibaba.fastjson2.JSON; +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 implements HandlerInterceptor +{ + @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, annotation)) + { + AjaxResult ajaxResult = AjaxResult.error(annotation.message()); + ServletUtils.renderString(response, JSON.toJSONString(ajaxResult)); + return false; + } + } + return true; + } + else + { + return true; + } + } + + /** + * 验证是否重复提交由子类实现具体的防重复提交的规则 + * + * @param request 请求信息 + * @param annotation 防重复注解参数 + * @return 结果 + * @throws Exception + */ + public abstract boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation); +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java new file mode 100644 index 0000000..9dc9511 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java @@ -0,0 +1,110 @@ +package com.ruoyi.framework.interceptor.impl; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import javax.servlet.http.HttpServletRequest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.annotation.RepeatSubmit; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.filter.RepeatedlyRequestWrapper; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.http.HttpHelper; +import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor; + +/** + * 判断请求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; + + @SuppressWarnings("unchecked") + @Override + public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation) + { + String nowParams = ""; + if (request instanceof RepeatedlyRequestWrapper) + { + RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request; + nowParams = HttpHelper.getBodyString(repeatedlyRequest); + } + + // body参数为空,获取Parameter的数据 + if (StringUtils.isEmpty(nowParams)) + { + nowParams = JSON.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 = StringUtils.trimToEmpty(request.getHeader(header)); + + // 唯一标识(指定key + url + 消息头) + String cacheRepeatKey = CacheConstants.REPEAT_SUBMIT_KEY + url + 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, annotation.interval())) + { + return true; + } + } + } + Map<String, Object> cacheMap = new HashMap<String, Object>(); + cacheMap.put(url, nowDataMap); + redisCache.setCacheObject(cacheRepeatKey, cacheMap, annotation.interval(), TimeUnit.MILLISECONDS); + 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, int interval) + { + long time1 = (Long) nowMap.get(REPEAT_TIME); + long time2 = (Long) preMap.get(REPEAT_TIME); + if ((time1 - time2) < interval) + { + return true; + } + return false; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java new file mode 100644 index 0000000..7387a02 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java @@ -0,0 +1,55 @@ +package com.ruoyi.framework.manager; + +import java.util.TimerTask; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import com.ruoyi.common.utils.Threads; +import com.ruoyi.common.utils.spring.SpringUtils; + +/** + * 异步任务管理器 + * + * @author ruoyi + */ +public class AsyncManager +{ + /** + * 操作延迟10毫秒 + */ + private final int OPERATE_DELAY_TIME = 10; + + /** + * 异步操作任务调度线程池 + */ + private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService"); + + /** + * 单例模式 + */ + private AsyncManager(){} + + private static AsyncManager me = new AsyncManager(); + + public static AsyncManager me() + { + return me; + } + + /** + * 执行任务 + * + * @param task 任务 + */ + public void execute(TimerTask task) + { + executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS); + } + + /** + * 停止任务线程池 + */ + public void shutdown() + { + Threads.shutdownAndAwaitTermination(executor); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java new file mode 100644 index 0000000..e36ca3c --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java @@ -0,0 +1,39 @@ +package com.ruoyi.framework.manager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import javax.annotation.PreDestroy; + +/** + * 确保应用退出时能关闭后台线程 + * + * @author ruoyi + */ +@Component +public class ShutdownManager +{ + private static final Logger logger = LoggerFactory.getLogger("sys-user"); + + @PreDestroy + public void destroy() + { + shutdownAsyncManager(); + } + + /** + * 停止异步执行任务 + */ + private void shutdownAsyncManager() + { + try + { + logger.info("====关闭后台任务任务线程池===="); + AsyncManager.me().shutdown(); + } + catch (Exception e) + { + logger.error(e.getMessage(), e); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java new file mode 100644 index 0000000..267e305 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java @@ -0,0 +1,102 @@ +package com.ruoyi.framework.manager.factory; + +import java.util.TimerTask; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.LogUtils; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.AddressUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +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 eu.bitwalker.useragentutils.UserAgent; + +/** + * 异步工厂(产生任务用) + * + * @author ruoyi + */ +public class AsyncFactory +{ + private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user"); + + /** + * 记录登录信息 + * + * @param username 用户名 + * @param status 状态 + * @param message 消息 + * @param args 列表 + * @return 任务task + */ + public static TimerTask recordLogininfor(final String username, final String status, final String message, + final Object... args) + { + final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); + final String ip = IpUtils.getIpAddr(); + return new TimerTask() + { + @Override + public void run() + { + String address = AddressUtils.getRealAddressByIP(ip); + StringBuilder s = new StringBuilder(); + s.append(LogUtils.getBlock(ip)); + s.append(address); + s.append(LogUtils.getBlock(username)); + s.append(LogUtils.getBlock(status)); + s.append(LogUtils.getBlock(message)); + // 打印信息到日志 + sys_user_logger.info(s.toString(), args); + // 获取客户端操作系统 + String os = userAgent.getOperatingSystem().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 (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) + { + logininfor.setStatus(Constants.SUCCESS); + } + else if (Constants.LOGIN_FAIL.equals(status)) + { + logininfor.setStatus(Constants.FAIL); + } + // 插入数据 + SpringUtils.getBean(ISysLogininforService.class).insertLogininfor(logininfor); + } + }; + } + + /** + * 操作日志记录 + * + * @param operLog 操作日志信息 + * @return 任务task + */ + public static TimerTask recordOper(final SysOperLog operLog) + { + return new TimerTask() + { + @Override + public void run() + { + // 远程查询操作地点 + operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp())); + SpringUtils.getBean(ISysOperLogService.class).insertOperlog(operLog); + } + }; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java new file mode 100644 index 0000000..6c776ce --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java @@ -0,0 +1,28 @@ +package com.ruoyi.framework.security.context; + +import org.springframework.security.core.Authentication; + +/** + * 身份验证信息 + * + * @author ruoyi + */ +public class AuthenticationContextHolder +{ + private static final ThreadLocal<Authentication> contextHolder = new ThreadLocal<>(); + + public static Authentication getContext() + { + return contextHolder.get(); + } + + public static void setContext(Authentication context) + { + contextHolder.set(context); + } + + public static void clearContext() + { + contextHolder.remove(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java new file mode 100644 index 0000000..5472f3d --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java @@ -0,0 +1,27 @@ +package com.ruoyi.framework.security.context; + +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import com.ruoyi.common.core.text.Convert; + +/** + * 权限信息 + * + * @author ruoyi + */ +public class PermissionContextHolder +{ + private static final String PERMISSION_CONTEXT_ATTRIBUTES = "PERMISSION_CONTEXT"; + + public static void setContext(String permission) + { + RequestContextHolder.currentRequestAttributes().setAttribute(PERMISSION_CONTEXT_ATTRIBUTES, permission, + RequestAttributes.SCOPE_REQUEST); + } + + public static String getContext() + { + return Convert.toStr(RequestContextHolder.currentRequestAttributes().getAttribute(PERMISSION_CONTEXT_ATTRIBUTES, + RequestAttributes.SCOPE_REQUEST)); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java new file mode 100644 index 0000000..9015708 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java @@ -0,0 +1,56 @@ +package com.ruoyi.framework.security.filter; + +import java.io.IOException; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.ruoyi.common.core.domain.model.LoginUserApplet; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.service.TokenService; + +/** + * token过滤器 验证token有效性 + * + * @author ruoyi + */ +@Component +public class JwtAuthenticationTokenFilter extends OncePerRequestFilter +{ + @Autowired + private TokenService tokenService; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + throws ServletException, IOException + { + LoginUser loginUser = tokenService.getLoginUser(request); + LoginUserApplet applet = tokenService.getLoginUserApplet(request); + if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())|| + StringUtils.isNotNull(applet)) + { + if (StringUtils.isNotNull(loginUser)){ + tokenService.verifyToken(loginUser); + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); + authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authenticationToken); + } + if (StringUtils.isNotNull(applet)){ + tokenService.verifyTokenApplet(applet); + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(applet, null, applet.getAuthorities()); + authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authenticationToken); + } + } + chain.doFilter(request, response); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java new file mode 100644 index 0000000..93b7032 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java @@ -0,0 +1,34 @@ +package com.ruoyi.framework.security.handle; + +import java.io.IOException; +import java.io.Serializable; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; + +/** + * 认证失败处理类 返回未授权 + * + * @author ruoyi + */ +@Component +public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable +{ + private static final long serialVersionUID = -8970718410437077606L; + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) + throws IOException + { + int code = HttpStatus.UNAUTHORIZED; + String msg = StringUtils.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI()); + ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg))); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java new file mode 100644 index 0000000..c01f691 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java @@ -0,0 +1,52 @@ +package com.ruoyi.framework.security.handle; + +import java.io.IOException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; +import com.alibaba.fastjson2.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.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.framework.web.service.TokenService; + +/** + * 自定义退出处理类 返回成功 + * + * @author ruoyi + */ +@Configuration +public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler +{ + @Autowired + private TokenService tokenService; + + /** + * 退出处理 + * + * @return + */ + @Override + public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) + throws IOException, ServletException + { + LoginUser loginUser = tokenService.getLoginUser(request); + if (StringUtils.isNotNull(loginUser)) + { + String userName = loginUser.getUsername(); + // 删除用户缓存记录 + tokenService.delLoginUser(loginUser.getToken()); + // 记录用户退出日志 + AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, "退出成功")); + } + ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.success("退出成功"))); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/Server.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/Server.java new file mode 100644 index 0000000..63b03da --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/Server.java @@ -0,0 +1,240 @@ +package com.ruoyi.framework.web.domain; + +import java.net.UnknownHostException; +import java.util.LinkedList; +import java.util.List; +import java.util.Properties; +import com.ruoyi.common.utils.Arith; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.framework.web.domain.server.Cpu; +import com.ruoyi.framework.web.domain.server.Jvm; +import com.ruoyi.framework.web.domain.server.Mem; +import com.ruoyi.framework.web.domain.server.Sys; +import com.ruoyi.framework.web.domain.server.SysFile; +import oshi.SystemInfo; +import oshi.hardware.CentralProcessor; +import oshi.hardware.CentralProcessor.TickType; +import oshi.hardware.GlobalMemory; +import oshi.hardware.HardwareAbstractionLayer; +import oshi.software.os.FileSystem; +import oshi.software.os.OSFileStore; +import oshi.software.os.OperatingSystem; +import oshi.util.Util; + +/** + * 服务器相关信息 + * + * @author ruoyi + */ +public class Server +{ + private static final int OSHI_WAIT_SECOND = 1000; + + /** + * CPU相关信息 + */ + private Cpu cpu = new Cpu(); + + /** + * 內存相关信息 + */ + private Mem mem = new Mem(); + + /** + * JVM相关信息 + */ + private Jvm jvm = new Jvm(); + + /** + * 服务器相关信息 + */ + private Sys sys = new Sys(); + + /** + * 磁盘相关信息 + */ + private List<SysFile> sysFiles = new LinkedList<SysFile>(); + + public Cpu getCpu() + { + return cpu; + } + + public void setCpu(Cpu cpu) + { + this.cpu = cpu; + } + + public Mem getMem() + { + return mem; + } + + public void setMem(Mem mem) + { + this.mem = mem; + } + + public Jvm getJvm() + { + return jvm; + } + + public void setJvm(Jvm jvm) + { + this.jvm = jvm; + } + + public Sys getSys() + { + return sys; + } + + public void setSys(Sys sys) + { + this.sys = sys; + } + + public List<SysFile> getSysFiles() + { + return sysFiles; + } + + public void setSysFiles(List<SysFile> sysFiles) + { + this.sysFiles = sysFiles; + } + + public void copyTo() throws Exception + { + SystemInfo si = new SystemInfo(); + HardwareAbstractionLayer hal = si.getHardware(); + + setCpuInfo(hal.getProcessor()); + + setMemInfo(hal.getMemory()); + + setSysInfo(); + + setJvmInfo(); + + setSysFiles(si.getOperatingSystem()); + } + + /** + * 设置CPU信息 + */ + private void setCpuInfo(CentralProcessor processor) + { + // CPU信息 + long[] prevTicks = processor.getSystemCpuLoadTicks(); + Util.sleep(OSHI_WAIT_SECOND); + long[] ticks = processor.getSystemCpuLoadTicks(); + long nice = ticks[TickType.NICE.getIndex()] - prevTicks[TickType.NICE.getIndex()]; + long irq = ticks[TickType.IRQ.getIndex()] - prevTicks[TickType.IRQ.getIndex()]; + long softirq = ticks[TickType.SOFTIRQ.getIndex()] - prevTicks[TickType.SOFTIRQ.getIndex()]; + long steal = ticks[TickType.STEAL.getIndex()] - prevTicks[TickType.STEAL.getIndex()]; + long cSys = ticks[TickType.SYSTEM.getIndex()] - prevTicks[TickType.SYSTEM.getIndex()]; + long user = ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()]; + long iowait = ticks[TickType.IOWAIT.getIndex()] - prevTicks[TickType.IOWAIT.getIndex()]; + long idle = ticks[TickType.IDLE.getIndex()] - prevTicks[TickType.IDLE.getIndex()]; + long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal; + cpu.setCpuNum(processor.getLogicalProcessorCount()); + cpu.setTotal(totalCpu); + cpu.setSys(cSys); + cpu.setUsed(user); + cpu.setWait(iowait); + cpu.setFree(idle); + } + + /** + * 设置内存信息 + */ + private void setMemInfo(GlobalMemory memory) + { + mem.setTotal(memory.getTotal()); + mem.setUsed(memory.getTotal() - memory.getAvailable()); + mem.setFree(memory.getAvailable()); + } + + /** + * 设置服务器信息 + */ + private void setSysInfo() + { + Properties props = System.getProperties(); + sys.setComputerName(IpUtils.getHostName()); + sys.setComputerIp(IpUtils.getHostIp()); + sys.setOsName(props.getProperty("os.name")); + sys.setOsArch(props.getProperty("os.arch")); + sys.setUserDir(props.getProperty("user.dir")); + } + + /** + * 设置Java虚拟机 + */ + private void setJvmInfo() throws UnknownHostException + { + Properties props = System.getProperties(); + jvm.setTotal(Runtime.getRuntime().totalMemory()); + jvm.setMax(Runtime.getRuntime().maxMemory()); + jvm.setFree(Runtime.getRuntime().freeMemory()); + jvm.setVersion(props.getProperty("java.version")); + jvm.setHome(props.getProperty("java.home")); + } + + /** + * 设置磁盘信息 + */ + private void setSysFiles(OperatingSystem os) + { + FileSystem fileSystem = os.getFileSystem(); + List<OSFileStore> fsArray = fileSystem.getFileStores(); + for (OSFileStore fs : fsArray) + { + long free = fs.getUsableSpace(); + long total = fs.getTotalSpace(); + long used = total - free; + SysFile sysFile = new SysFile(); + sysFile.setDirName(fs.getMount()); + sysFile.setSysTypeName(fs.getType()); + sysFile.setTypeName(fs.getName()); + sysFile.setTotal(convertFileSize(total)); + sysFile.setFree(convertFileSize(free)); + sysFile.setUsed(convertFileSize(used)); + sysFile.setUsage(Arith.mul(Arith.div(used, total, 4), 100)); + sysFiles.add(sysFile); + } + } + + /** + * 字节转换 + * + * @param size 字节大小 + * @return 转换后值 + */ + public String convertFileSize(long size) + { + long kb = 1024; + long mb = kb * 1024; + long gb = mb * 1024; + if (size >= gb) + { + return String.format("%.1f GB", (float) size / gb); + } + else if (size >= mb) + { + float f = (float) size / mb; + return String.format(f > 100 ? "%.0f MB" : "%.1f MB", f); + } + else if (size >= kb) + { + float f = (float) size / kb; + return String.format(f > 100 ? "%.0f KB" : "%.1f KB", f); + } + else + { + return String.format("%d B", size); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java new file mode 100644 index 0000000..a13a66c --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java @@ -0,0 +1,101 @@ +package com.ruoyi.framework.web.domain.server; + +import com.ruoyi.common.utils.Arith; + +/** + * CPU相关信息 + * + * @author ruoyi + */ +public class Cpu +{ + /** + * 核心数 + */ + private int cpuNum; + + /** + * CPU总的使用率 + */ + private double total; + + /** + * CPU系统使用率 + */ + private double sys; + + /** + * CPU用户使用率 + */ + private double used; + + /** + * CPU当前等待率 + */ + private double wait; + + /** + * CPU当前空闲率 + */ + private double free; + + public int getCpuNum() + { + return cpuNum; + } + + public void setCpuNum(int cpuNum) + { + this.cpuNum = cpuNum; + } + + public double getTotal() + { + return Arith.round(Arith.mul(total, 100), 2); + } + + public void setTotal(double total) + { + this.total = total; + } + + public double getSys() + { + return Arith.round(Arith.mul(sys / total, 100), 2); + } + + public void setSys(double sys) + { + this.sys = sys; + } + + public double getUsed() + { + return Arith.round(Arith.mul(used / total, 100), 2); + } + + public void setUsed(double used) + { + this.used = used; + } + + public double getWait() + { + return Arith.round(Arith.mul(wait / total, 100), 2); + } + + public void setWait(double wait) + { + this.wait = wait; + } + + public double getFree() + { + return Arith.round(Arith.mul(free / total, 100), 2); + } + + public void setFree(double free) + { + this.free = free; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java new file mode 100644 index 0000000..1fdc6ac --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java @@ -0,0 +1,130 @@ +package com.ruoyi.framework.web.domain.server; + +import java.lang.management.ManagementFactory; +import com.ruoyi.common.utils.Arith; +import com.ruoyi.common.utils.DateUtils; + +/** + * JVM相关信息 + * + * @author ruoyi + */ +public class Jvm +{ + /** + * 当前JVM占用的内存总数(M) + */ + private double total; + + /** + * JVM最大可用内存总数(M) + */ + private double max; + + /** + * JVM空闲内存(M) + */ + private double free; + + /** + * JDK版本 + */ + private String version; + + /** + * JDK路径 + */ + private String home; + + public double getTotal() + { + return Arith.div(total, (1024 * 1024), 2); + } + + public void setTotal(double total) + { + this.total = total; + } + + public double getMax() + { + return Arith.div(max, (1024 * 1024), 2); + } + + public void setMax(double max) + { + this.max = max; + } + + public double getFree() + { + return Arith.div(free, (1024 * 1024), 2); + } + + public void setFree(double free) + { + this.free = free; + } + + public double getUsed() + { + return Arith.div(total - free, (1024 * 1024), 2); + } + + public double getUsage() + { + return Arith.mul(Arith.div(total - free, total, 4), 100); + } + + /** + * 获取JDK名称 + */ + public String getName() + { + return ManagementFactory.getRuntimeMXBean().getVmName(); + } + + public String getVersion() + { + return version; + } + + public void setVersion(String version) + { + this.version = version; + } + + public String getHome() + { + return home; + } + + public void setHome(String home) + { + this.home = home; + } + + /** + * JDK启动时间 + */ + public String getStartTime() + { + return DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, DateUtils.getServerStartDate()); + } + + /** + * JDK运行时间 + */ + public String getRunTime() + { + return DateUtils.timeDistance(DateUtils.getNowDate(), DateUtils.getServerStartDate()); + } + + /** + * 运行参数 + */ + public String getInputArgs() + { + return ManagementFactory.getRuntimeMXBean().getInputArguments().toString(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java new file mode 100644 index 0000000..13eec52 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java @@ -0,0 +1,61 @@ +package com.ruoyi.framework.web.domain.server; + +import com.ruoyi.common.utils.Arith; + +/** + * 內存相关信息 + * + * @author ruoyi + */ +public class Mem +{ + /** + * 内存总量 + */ + private double total; + + /** + * 已用内存 + */ + private double used; + + /** + * 剩余内存 + */ + private double free; + + public double getTotal() + { + return Arith.div(total, (1024 * 1024 * 1024), 2); + } + + public void setTotal(long total) + { + this.total = total; + } + + public double getUsed() + { + return Arith.div(used, (1024 * 1024 * 1024), 2); + } + + public void setUsed(long used) + { + this.used = used; + } + + public double getFree() + { + return Arith.div(free, (1024 * 1024 * 1024), 2); + } + + public void setFree(long free) + { + this.free = free; + } + + public double getUsage() + { + return Arith.mul(Arith.div(used, total, 4), 100); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java new file mode 100644 index 0000000..45d64d9 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java @@ -0,0 +1,84 @@ +package com.ruoyi.framework.web.domain.server; + +/** + * 系统相关信息 + * + * @author ruoyi + */ +public class Sys +{ + /** + * 服务器名称 + */ + private String computerName; + + /** + * 服务器Ip + */ + private String computerIp; + + /** + * 项目路径 + */ + private String userDir; + + /** + * 操作系统 + */ + private String osName; + + /** + * 系统架构 + */ + private String osArch; + + public String getComputerName() + { + return computerName; + } + + public void setComputerName(String computerName) + { + this.computerName = computerName; + } + + public String getComputerIp() + { + return computerIp; + } + + public void setComputerIp(String computerIp) + { + this.computerIp = computerIp; + } + + public String getUserDir() + { + return userDir; + } + + public void setUserDir(String userDir) + { + this.userDir = userDir; + } + + public String getOsName() + { + return osName; + } + + public void setOsName(String osName) + { + this.osName = osName; + } + + public String getOsArch() + { + return osArch; + } + + public void setOsArch(String osArch) + { + this.osArch = osArch; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java new file mode 100644 index 0000000..1320cde --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java @@ -0,0 +1,114 @@ +package com.ruoyi.framework.web.domain.server; + +/** + * 系统文件相关信息 + * + * @author ruoyi + */ +public class SysFile +{ + /** + * 盘符路径 + */ + private String dirName; + + /** + * 盘符类型 + */ + private String sysTypeName; + + /** + * 文件类型 + */ + private String typeName; + + /** + * 总大小 + */ + private String total; + + /** + * 剩余大小 + */ + private String free; + + /** + * 已经使用量 + */ + private String used; + + /** + * 资源的使用率 + */ + private double usage; + + public String getDirName() + { + return dirName; + } + + public void setDirName(String dirName) + { + this.dirName = dirName; + } + + public String getSysTypeName() + { + return sysTypeName; + } + + public void setSysTypeName(String sysTypeName) + { + this.sysTypeName = sysTypeName; + } + + public String getTypeName() + { + return typeName; + } + + public void setTypeName(String typeName) + { + this.typeName = typeName; + } + + public String getTotal() + { + return total; + } + + public void setTotal(String total) + { + this.total = total; + } + + public String getFree() + { + return free; + } + + public void setFree(String free) + { + this.free = free; + } + + public String getUsed() + { + return used; + } + + public void setUsed(String used) + { + this.used = used; + } + + public double getUsage() + { + return usage; + } + + public void setUsage(double usage) + { + this.usage = usage; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..a3ec182 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java @@ -0,0 +1,138 @@ +package com.ruoyi.framework.web.exception; + +import javax.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.validation.BindException; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingPathVariableException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.exception.DemoModeException; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; + +/** + * 全局异常处理器 + * + * @author ruoyi + */ +@RestControllerAdvice +public class GlobalExceptionHandler +{ + private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + /** + * 权限校验异常 + */ + @ExceptionHandler(AccessDeniedException.class) + public AjaxResult handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage()); + return AjaxResult.error(HttpStatus.FORBIDDEN, "没有权限,请联系管理员授权"); + } + + /** + * 请求方式不支持 + */ + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, + HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod()); + return AjaxResult.error(e.getMessage()); + } + + /** + * 业务异常 + */ + @ExceptionHandler(ServiceException.class) + public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request) + { + log.error(e.getMessage(), e); + Integer code = e.getCode(); + return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage()); + } + + /** + * 请求路径中缺少必需的路径变量 + */ + @ExceptionHandler(MissingPathVariableException.class) + public AjaxResult handleMissingPathVariableException(MissingPathVariableException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI, e); + return AjaxResult.error(String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName())); + } + + /** + * 请求参数类型不匹配 + */ + @ExceptionHandler(MethodArgumentTypeMismatchException.class) + public AjaxResult handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI, e); + return AjaxResult.error(String.format("请求参数类型不匹配,参数[%s]要求类型为:'%s',但输入值为:'%s'", e.getName(), e.getRequiredType().getName(), e.getValue())); + } + + /** + * 拦截未知的运行时异常 + */ + @ExceptionHandler(RuntimeException.class) + public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生未知异常.", requestURI, e); + return AjaxResult.error(e.getMessage()); + } + + /** + * 系统异常 + */ + @ExceptionHandler(Exception.class) + public AjaxResult handleException(Exception e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生系统异常.", requestURI, e); + return AjaxResult.error(e.getMessage()); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(BindException.class) + public AjaxResult handleBindException(BindException e) + { + log.error(e.getMessage(), e); + String message = e.getAllErrors().get(0).getDefaultMessage(); + return AjaxResult.error(message); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) + { + log.error(e.getMessage(), e); + String message = e.getBindingResult().getFieldError().getDefaultMessage(); + return AjaxResult.error(message); + } + + /** + * 演示模式异常 + */ + @ExceptionHandler(DemoModeException.class) + public AjaxResult handleDemoModeException(DemoModeException e) + { + return AjaxResult.error("演示模式,不允许操作"); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java new file mode 100644 index 0000000..249c6a0 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java @@ -0,0 +1,168 @@ +package com.ruoyi.framework.web.service; + +import java.util.Set; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.security.context.PermissionContextHolder; + +/** + * RuoYi首创 自定义权限实现,ss取自SpringSecurity首字母 + * + * @author ruoyi + */ +@Service("ss") +public class PermissionService +{ + /** 所有权限标识 */ + private static final String ALL_PERMISSION = "*:*:*"; + + /** 管理员角色权限标识 */ + private static final String SUPER_ADMIN = "admin"; + + private static final String ROLE_DELIMETER = ","; + + private static final String PERMISSION_DELIMETER = ","; + + /** + * 验证用户是否具备某权限 + * + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + public boolean hasPermi(String permission) + { + if (StringUtils.isEmpty(permission)) + { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) + { + return false; + } + PermissionContextHolder.setContext(permission); + return hasPermissions(loginUser.getPermissions(), permission); + } + + /** + * 验证用户是否不具备某权限,与 hasPermi逻辑相反 + * + * @param permission 权限字符串 + * @return 用户是否不具备某权限 + */ + public boolean lacksPermi(String permission) + { + return hasPermi(permission) != true; + } + + /** + * 验证用户是否具有以下任意一个权限 + * + * @param permissions 以 PERMISSION_DELIMETER 为分隔符的权限列表 + * @return 用户是否具有以下任意一个权限 + */ + public boolean hasAnyPermi(String permissions) + { + if (StringUtils.isEmpty(permissions)) + { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) + { + return false; + } + PermissionContextHolder.setContext(permissions); + Set<String> authorities = loginUser.getPermissions(); + for (String permission : permissions.split(PERMISSION_DELIMETER)) + { + if (permission != null && hasPermissions(authorities, permission)) + { + return true; + } + } + return false; + } + + /** + * 判断用户是否拥有某个角色 + * + * @param role 角色字符串 + * @return 用户是否具备某角色 + */ + public boolean hasRole(String role) + { + if (StringUtils.isEmpty(role)) + { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) + { + return false; + } + for (SysRole sysRole : loginUser.getUser().getRoles()) + { + String roleKey = sysRole.getRoleKey(); + if (SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role))) + { + return true; + } + } + return false; + } + + /** + * 验证用户是否不具备某角色,与 isRole逻辑相反。 + * + * @param role 角色名称 + * @return 用户是否不具备某角色 + */ + public boolean lacksRole(String role) + { + return hasRole(role) != true; + } + + /** + * 验证用户是否具有以下任意一个角色 + * + * @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表 + * @return 用户是否具有以下任意一个角色 + */ + public boolean hasAnyRoles(String roles) + { + if (StringUtils.isEmpty(roles)) + { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) + { + return false; + } + for (String role : roles.split(ROLE_DELIMETER)) + { + if (hasRole(role)) + { + return true; + } + } + return false; + } + + /** + * 判断是否包含权限 + * + * @param permissions 权限列表 + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + private boolean hasPermissions(Set<String> permissions, String permission) + { + return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission)); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java new file mode 100644 index 0000000..4f8e6ed --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java @@ -0,0 +1,284 @@ +package com.ruoyi.framework.web.service; + +import javax.annotation.Resource; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ruoyi.common.core.domain.entity.TTenantResp; +import com.ruoyi.common.core.domain.model.LoginUserApplet; +import com.ruoyi.common.enums.UserStatus; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.exception.user.BlackListException; +import com.ruoyi.common.exception.user.CaptchaException; +import com.ruoyi.common.exception.user.CaptchaExpireException; +import com.ruoyi.common.exception.user.UserNotExistsException; +import com.ruoyi.common.exception.user.UserPasswordNotMatchException; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.framework.security.context.AuthenticationContextHolder; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.system.service.ISysUserService; + +/** + * 登录校验方法 + * + * @author ruoyi + */ +@Slf4j +@Component +public class SysLoginService +{ + @Autowired + private TokenService tokenService; + + @Resource + private AuthenticationManager authenticationManager; + + @Autowired + private RedisCache redisCache; + + @Autowired + private ISysUserService userService; + + @Autowired + private ISysConfigService configService; + @Autowired + private SysPermissionService permissionService; + + /** + * 登录验证 + * + * @param username 用户名 + * @param password 密码 + * @param code 验证码 + * @param uuid 唯一标识 + * @return 结果 + */ + public LoginUser login(String username, String password, String code, String uuid) + { + // 验证码校验 + validateCaptcha(username, code, uuid); + // 登录前置校验 + loginPreCheck(username, password); + // 用户验证 + Authentication authentication = null; + // 用户验证 + SysUser user = userService.selectUserByUserName(username); + if (StringUtils.isNull(user)){ + log.info("登录用户:{} 不存在.", username); + throw new ServiceException(MessageUtils.message("user.not.exists")); + } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) { + log.info("登录用户:{} 已被删除.", username); + throw new ServiceException(MessageUtils.message("user.password.delete")); + } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { + log.info("登录用户:{} 已被停用.", username); + throw new ServiceException(MessageUtils.message("user.blocked")); + } + try + { + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password); + AuthenticationContextHolder.setContext(authenticationToken); + // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername + authentication = authenticationManager.authenticate(authenticationToken); + } + catch (Exception e) + { + if (e instanceof BadCredentialsException) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); + throw new UserPasswordNotMatchException(); + } + else + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage())); + throw new ServiceException(e.getMessage()); + } + } + finally + { + AuthenticationContextHolder.clearContext(); + } + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"))); + LoginUser loginUser = (LoginUser) authentication.getPrincipal(); + recordLoginInfo(loginUser.getUserId()); + // 生成token + return loginUser; + } + + /** + * 登录验证 + * + * @param username 用户名 + * @param code 验证码 + * @return 结果 + */ + public LoginUser loginCode(String username,String code) + { + // 登录前置校验 + if (StringUtils.isEmpty(username)){ + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null"))); + throw new UserNotExistsException(); + } + // 用户验证 + SysUser user = userService.selectUserByUserName(username); + if (StringUtils.isNull(user)){ + log.info("登录用户:{} 不存在.", username); + throw new ServiceException(MessageUtils.message("user.not.exists")); + } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) { + log.info("登录用户:{} 已被删除.", username); + throw new ServiceException(MessageUtils.message("user.password.delete")); + } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { + log.info("登录用户:{} 已被停用.", username); + throw new ServiceException(MessageUtils.message("user.blocked")); + } + if(user.isAdmin()){ + log.info("登录用户:{} 不可用短信验证码登录.", username); + throw new ServiceException("不可用短信验证码登录"); + } + // 校验验证码 + Object cacheObject = redisCache.getCacheObject(user.getPhonenumber()); + if(!code.equals(String.valueOf(cacheObject))){ + log.info("登录用户:{} 短信验证码错误{}", username,code); + throw new ServiceException("短信验证码错误"); + } + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"))); + LoginUser loginUser = new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user)); + recordLoginInfo(loginUser.getUserId()); + // 生成token + return loginUser; + } + + /** + * 登录验证 + * + * @param username 用户名 + * @param code 验证码 + * @return 结果 + */ + public LoginUserApplet loginCodeApplet(String username, String code) + { + // 登录前置校验 +// if (StringUtils.isEmpty(username)){ +// AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null"))); +// throw new UserNotExistsException(); +// } +// // 用户验证 +// TTenant user = tenantService.getOne(Wrappers.<TTenant>lambdaQuery().eq(TTenant::getAccount,username)); +// if (StringUtils.isNull(user)){ +// log.info("登录用户:{} 不存在.", username); +// throw new ServiceException(MessageUtils.message("user.not.exists")); +// } else if (user.getDisabled()) { +// log.info("登录用户:{} 已被删除.", username); +// throw new ServiceException(MessageUtils.message("user.password.delete")); +// } +// // 校验验证码 +// Object cacheObject = redisCache.getCacheObject(user.getAccount()); +// if(!code.equals(String.valueOf(cacheObject))){ +// log.info("登录用户:{} 短信验证码错误{}", username,code); +// throw new ServiceException("短信验证码错误"); +// } +// AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"))); +// TTenantResp tTenantResp = new TTenantResp(); +// BeanUtils.copyProperties(user,tTenantResp); +// LoginUserApplet loginUser = new LoginUserApplet(user.getId(), null, tTenantResp, null); + // 生成token + return null; + } + + + /** + * 校验验证码 + * + * @param username 用户名 + * @param code 验证码 + * @param uuid 唯一标识 + * @return 结果 + */ + public void validateCaptcha(String username, String code, String uuid) + { + boolean captchaEnabled = configService.selectCaptchaEnabled(); + if (captchaEnabled) + { + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(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"))); + throw new CaptchaExpireException(); + } + if (!code.equalsIgnoreCase(captcha)) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"))); + throw new CaptchaException(); + } + } + } + + /** + * 登录前置校验 + * @param username 用户名 + * @param password 用户密码 + */ + public void loginPreCheck(String username, String password) + { + // 用户名或密码为空 错误 + if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null"))); + throw new UserNotExistsException(); + } + // 密码如果不在指定范围内 错误 + if (password.length() < UserConstants.PASSWORD_MIN_LENGTH + || password.length() > UserConstants.PASSWORD_MAX_LENGTH) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); + throw new UserPasswordNotMatchException(); + } + // 用户名不在指定范围内 错误 + if (username.length() < UserConstants.USERNAME_MIN_LENGTH + || username.length() > UserConstants.USERNAME_MAX_LENGTH) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); + throw new UserPasswordNotMatchException(); + } + // IP黑名单校验 + String blackStr = configService.selectConfigByKey("sys.login.blackIPList"); + if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked"))); + throw new BlackListException(); + } + } + + /** + * 记录登录信息 + * + * @param userId 用户ID + */ + public void recordLoginInfo(Long userId) + { + SysUser sysUser = new SysUser(); + sysUser.setUserId(userId); + sysUser.setLoginIp(IpUtils.getIpAddr()); + sysUser.setLoginDate(DateUtils.getNowDate()); + userService.updateUserProfile(sysUser); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.java new file mode 100644 index 0000000..6ad91b0 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.java @@ -0,0 +1,94 @@ +package com.ruoyi.framework.web.service; + +import java.util.concurrent.TimeUnit; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.exception.user.UserPasswordNotMatchException; +import com.ruoyi.common.exception.user.UserPasswordRetryLimitExceedException; +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.framework.security.context.AuthenticationContextHolder; + +/** + * 登录密码方法 + * + * @author ruoyi + */ +@Component +public class SysPasswordService +{ + @Autowired + private RedisCache redisCache; + + @Value(value = "${user.password.maxRetryCount}") + private int maxRetryCount; + + @Value(value = "${user.password.lockTime}") + private int lockTime; + + /** + * 登录账户密码错误次数缓存键名 + * + * @param username 用户名 + * @return 缓存键key + */ + private String getCacheKey(String username) + { + return CacheConstants.PWD_ERR_CNT_KEY + username; + } + + public void validate(SysUser user) + { + Authentication usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext(); + String username = usernamePasswordAuthenticationToken.getName(); + String password = usernamePasswordAuthenticationToken.getCredentials().toString(); + + Integer retryCount = redisCache.getCacheObject(getCacheKey(username)); + + if (retryCount == null) + { + retryCount = 0; + } + + if (retryCount >= Integer.valueOf(maxRetryCount).intValue()) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, + MessageUtils.message("user.password.retry.limit.exceed", maxRetryCount, lockTime))); + throw new UserPasswordRetryLimitExceedException(maxRetryCount, lockTime); + } + + if (!matches(user, password)) + { + retryCount = retryCount + 1; + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, + MessageUtils.message("user.password.retry.limit.count", retryCount))); + redisCache.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES); + throw new UserPasswordNotMatchException(); + } + else + { + clearLoginRecordCache(username); + } + } + + public boolean matches(SysUser user, String rawPassword) + { + return SecurityUtils.matchesPassword(rawPassword, user.getPassword()); + } + + public void clearLoginRecordCache(String loginName) + { + if (redisCache.hasKey(getCacheKey(loginName))) + { + redisCache.deleteObject(getCacheKey(loginName)); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java new file mode 100644 index 0000000..d1fb4ed --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java @@ -0,0 +1,83 @@ +package com.ruoyi.framework.web.service; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.system.service.ISysMenuService; +import com.ruoyi.system.service.ISysRoleService; + +/** + * 用户权限处理 + * + * @author ruoyi + */ +@Component +public class SysPermissionService +{ + @Autowired + private ISysRoleService roleService; + + @Autowired + private ISysMenuService menuService; + + /** + * 获取角色数据权限 + * + * @param user 用户信息 + * @return 角色权限信息 + */ + public Set<String> getRolePermission(SysUser user) + { + Set<String> roles = new HashSet<String>(); + // 管理员拥有所有权限 + if (user.isAdmin()) + { + roles.add("admin"); + } + else + { + roles.addAll(roleService.selectRolePermissionByUserId(user.getUserId())); + } + return roles; + } + + /** + * 获取菜单数据权限 + * + * @param user 用户信息 + * @return 菜单权限信息 + */ + public Set<String> getMenuPermission(SysUser user) + { + Set<String> perms = new HashSet<String>(); + // 管理员拥有所有权限 + if (user.isAdmin()) + { + perms.add("*:*:*"); + } + else + { + List<SysRole> roles = user.getRoles(); + if (!CollectionUtils.isEmpty(roles)) + { + // 多角色设置permissions属性,以便数据权限匹配权限 + for (SysRole role : roles) + { + Set<String> rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId()); + role.setPermissions(rolePerms); + perms.addAll(rolePerms); + } + } + else + { + perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId())); + } + } + return perms; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java new file mode 100644 index 0000000..239337f --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java @@ -0,0 +1,68 @@ +package com.ruoyi.framework.web.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.RegisterBody; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.exception.user.CaptchaException; +import com.ruoyi.common.exception.user.CaptchaExpireException; +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.system.service.ISysUserService; + +/** + * 注册校验方法 + * + * @author ruoyi + */ +@Component +public class SysRegisterService +{ + @Autowired + private ISysUserService userService; + + @Autowired + private ISysConfigService configService; + + @Autowired + private RedisCache redisCache; + + /** + * 注册 + */ + public String register(RegisterBody registerBody) + { + return null; + } + + /** + * 校验验证码 + * + * @param username 用户名 + * @param code 验证码 + * @param uuid 唯一标识 + * @return 结果 + */ + public void validateCaptcha(String username, String code, String uuid) + { + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, ""); + String captcha = redisCache.getCacheObject(verifyKey); + redisCache.deleteObject(verifyKey); + if (captcha == null) + { + throw new CaptchaExpireException(); + } + if (!code.equalsIgnoreCase(captcha)) + { + throw new CaptchaException(); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java new file mode 100644 index 0000000..206c1a4 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java @@ -0,0 +1,360 @@ +package com.ruoyi.framework.web.service; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import javax.servlet.http.HttpServletRequest; + +import com.ruoyi.common.core.domain.model.LoginUserApplet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.AddressUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.common.utils.uuid.IdUtils; +import eu.bitwalker.useragentutils.UserAgent; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; + +/** + * token验证处理 + * + * @author ruoyi + */ +@Component +public class TokenService +{ + private static final Logger log = LoggerFactory.getLogger(TokenService.class); + + // 令牌自定义标识 + @Value("${token.header}") + private String header; + + // 令牌秘钥 + @Value("${token.secret}") + private String secret; + + // 令牌有效期(默认30分钟) + @Value("${token.expireTime}") + private int expireTime; + + protected static final long MILLIS_SECOND = 1000; + + protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND; + + private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L; + + @Autowired + private RedisCache redisCache; + + /** + * 获取用户身份信息 + * + * @return 用户信息 + */ + public LoginUser getLoginUser() + { + return getLoginUser(ServletUtils.getRequest()); + } + /** + * 小程序获取用户身份信息 + * + * @return 用户信息 + */ + public LoginUserApplet getLoginUserApplet() + { + return getLoginUserApplet(ServletUtils.getRequest()); + } + /** + * 获取用户身份信息 + * + * @return 用户信息 + */ + public LoginUser getLoginUser(HttpServletRequest request) + { + // 获取请求携带的令牌 + String token = getToken(request); + if (StringUtils.isNotEmpty(token)) + { + try + { + Claims claims = parseToken(token); + // 解析对应的权限以及用户信息 + String uuid = (String) claims.get(Constants.LOGIN_USER_KEY); + String userKey = getTokenKey(uuid); + LoginUser user = redisCache.getCacheObject(userKey); + return user; + } + catch (Exception e) + { + log.error("获取用户信息异常'{}'", e.getMessage()); + } + } + return null; + } + /** + * 小程序获取用户身份信息 + * + * @return 用户信息 + */ + public LoginUserApplet getLoginUserApplet(HttpServletRequest request) + { + // 获取请求携带的令牌 + String token = getToken(request); + if (StringUtils.isNotEmpty(token)) + { + try + { + Claims claims = parseToken(token); + // 解析对应的权限以及用户信息 + String uuid = (String) claims.get(Constants.LOGIN_USER_APPLET_KEY); + String userKey = getTokenKey(uuid); + LoginUserApplet user = redisCache.getCacheObject(userKey); + return user; + } + catch (Exception e) + { + log.error("获取用户信息异常'{}'", e.getMessage()); + } + } + return null; + } + + /** + * 设置用户身份信息 + */ + public void setLoginUser(LoginUser loginUser) + { + if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken())) + { + refreshToken(loginUser); + } + } + + /** + * 删除用户身份信息 + */ + public void delLoginUser(String token) + { + if (StringUtils.isNotEmpty(token)) + { + String userKey = getTokenKey(token); + redisCache.deleteObject(userKey); + } + } + + /** + * 创建令牌 + * + * @param loginUser 用户信息 + * @return 令牌 + */ + public String createToken(LoginUser loginUser) + { + String token = IdUtils.fastUUID(); + loginUser.setToken(token); + setUserAgent(loginUser); + refreshToken(loginUser); + + Map<String, Object> claims = new HashMap<>(); + claims.put(Constants.LOGIN_USER_KEY, token); + return createToken(claims); + } + /** + * 创建用户小程序令牌 + * + * @param loginUser 用户信息 + * @return 令牌 + */ + public String createTokenApplet(LoginUserApplet loginUser) + { + String token = IdUtils.fastUUID(); + loginUser.setToken(token); + setUserAgentApplet(loginUser); + refreshTokenApplet(loginUser); + + Map<String, Object> claims = new HashMap<>(); + claims.put(Constants.LOGIN_USER_APPLET_KEY, token); + return createTokenApplet(claims); + } + + /** + * 验证令牌有效期,相差不足20分钟,自动刷新缓存 + * + * @param loginUser + * @return 令牌 + */ + public void verifyToken(LoginUser loginUser) + { + long expireTime = loginUser.getExpireTime(); + long currentTime = System.currentTimeMillis(); + if (expireTime - currentTime <= MILLIS_MINUTE_TEN) + { + refreshToken(loginUser); + } + } + + + public boolean verifyToken(String token) + { + Claims claims = parseToken(token); + + return true; + } + /** + * 小程序验证令牌有效期,相差不足20分钟,自动刷新缓存 + * + * @param loginUser + * @return 令牌 + */ + public void verifyTokenApplet(LoginUserApplet loginUser) + { + long expireTime = loginUser.getExpireTime(); + long currentTime = System.currentTimeMillis(); + if (expireTime - currentTime <= MILLIS_MINUTE_TEN) + { + refreshTokenApplet(loginUser); + } + } + + /** + * 刷新令牌有效期 + * + * @param loginUser 登录信息 + */ + public void refreshToken(LoginUser loginUser) + { + loginUser.setLoginTime(System.currentTimeMillis()); + loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE); + // 根据uuid将loginUser缓存 + String userKey = getTokenKey(loginUser.getToken()); + redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES); + } + /** + * 刷新令牌有效期 + * + * @param loginUser 登录信息 + */ + public void refreshTokenApplet(LoginUserApplet loginUser) + { + loginUser.setLoginTime(System.currentTimeMillis()); + loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE); + // 根据uuid将loginUser缓存 + String userKey = getTokenKey(loginUser.getToken()); + redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES); + } + + /** + * 设置用户代理信息 + * + * @param loginUser 登录信息 + */ + public void setUserAgent(LoginUser loginUser) + { + UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); + String ip = IpUtils.getIpAddr(); + loginUser.setIpaddr(ip); + loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); + loginUser.setBrowser(userAgent.getBrowser().getName()); + loginUser.setOs(userAgent.getOperatingSystem().getName()); + } + /** + * 设置用户代理信息 + * + * @param loginUser 登录信息 + */ + public void setUserAgentApplet(LoginUserApplet loginUser) + { + UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); + String ip = IpUtils.getIpAddr(); + loginUser.setIpaddr(ip); + loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); + loginUser.setBrowser(userAgent.getBrowser().getName()); + loginUser.setOs(userAgent.getOperatingSystem().getName()); + } + + /** + * 从数据声明生成令牌 + * + * @param claims 数据声明 + * @return 令牌 + */ + private String createToken(Map<String, Object> claims) + { + String token = Jwts.builder() + .setClaims(claims) + .signWith(SignatureAlgorithm.HS512, secret).compact(); + return token; + } + /** + * 小程序从数据声明生成令牌 + * + * @param claims 数据声明 + * @return 令牌 + */ + private String createTokenApplet(Map<String, Object> claims) + { + String token = Jwts.builder() + .setClaims(claims) + .signWith(SignatureAlgorithm.HS512, secret).compact(); + return token; + } + + /** + * 从令牌中获取数据声明 + * + * @param token 令牌 + * @return 数据声明 + */ + private Claims parseToken(String token) + { + return Jwts.parser() + .setSigningKey(secret) + .parseClaimsJws(token) + .getBody(); + } + + /** + * 从令牌中获取用户名 + * + * @param token 令牌 + * @return 用户名 + */ + public String getUsernameFromToken(String token) + { + Claims claims = parseToken(token); + return claims.getSubject(); + } + + /** + * 获取请求token + * + * @param request + * @return token + */ + private String getToken(HttpServletRequest request) + { + String token = request.getHeader(header); + if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) + { + token = token.replace(Constants.TOKEN_PREFIX, ""); + } + return token; + } + + private String getTokenKey(String uuid) + { + return CacheConstants.LOGIN_TOKEN_KEY + uuid; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java new file mode 100644 index 0000000..5dcdf90 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java @@ -0,0 +1,66 @@ +package com.ruoyi.framework.web.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.enums.UserStatus; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.service.ISysUserService; + +/** + * 用户验证处理 + * + * @author ruoyi + */ +@Service +public class UserDetailsServiceImpl implements UserDetailsService +{ + private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class); + + @Autowired + private ISysUserService userService; + + @Autowired + private SysPasswordService passwordService; + + @Autowired + private SysPermissionService permissionService; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException + { + SysUser user = userService.selectUserByUserName(username); + if (StringUtils.isNull(user)) + { + log.info("登录用户:{} 不存在.", username); + throw new ServiceException(MessageUtils.message("user.not.exists")); + } + else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) + { + log.info("登录用户:{} 已被删除.", username); + throw new ServiceException(MessageUtils.message("user.password.delete")); + } + else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) + { + log.info("登录用户:{} 已被停用.", username); + throw new ServiceException(MessageUtils.message("user.blocked")); + } + + passwordService.validate(user); + + return createLoginUser(user); + } + + public UserDetails createLoginUser(SysUser user) + { + return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user)); + } +} diff --git a/ruoyi-generator/pom.xml b/ruoyi-generator/pom.xml new file mode 100644 index 0000000..047cfa8 --- /dev/null +++ b/ruoyi-generator/pom.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>zhengshengxin</artifactId> + <groupId>com.ruoyi</groupId> + <version>3.8.6</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>ruoyi-generator</artifactId> + + <description> + generator代码生成 + </description> + + <dependencies> + + <!--velocity代码生成使用模板 --> + <dependency> + <groupId>org.apache.velocity</groupId> + <artifactId>velocity-engine-core</artifactId> + </dependency> + + <!-- collections工具类 --> + <dependency> + <groupId>commons-collections</groupId> + <artifactId>commons-collections</artifactId> + </dependency> + + <!-- 通用工具--> + <dependency> + <groupId>com.ruoyi</groupId> + <artifactId>ruoyi-common</artifactId> + </dependency> + + </dependencies> + +</project> \ No newline at end of file diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/config/GenConfig.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/config/GenConfig.java new file mode 100644 index 0000000..cc4cd14 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/config/GenConfig.java @@ -0,0 +1,73 @@ +package com.ruoyi.generator.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.PropertySource; +import org.springframework.stereotype.Component; + +/** + * 读取代码生成相关配置 + * + * @author ruoyi + */ +@Component +@ConfigurationProperties(prefix = "gen") +@PropertySource(value = { "classpath:generator.yml" }) +public class GenConfig +{ + /** 作者 */ + public static String author; + + /** 生成包路径 */ + public static String packageName; + + /** 自动去除表前缀,默认是false */ + public static boolean autoRemovePre; + + /** 表前缀(类名不会包含表前缀) */ + public static String tablePrefix; + + public static String getAuthor() + { + return author; + } + + @Value("${author}") + public void setAuthor(String author) + { + GenConfig.author = author; + } + + public static String getPackageName() + { + return packageName; + } + + @Value("${packageName}") + public void setPackageName(String packageName) + { + GenConfig.packageName = packageName; + } + + public static boolean getAutoRemovePre() + { + return autoRemovePre; + } + + @Value("${autoRemovePre}") + public void setAutoRemovePre(boolean autoRemovePre) + { + GenConfig.autoRemovePre = autoRemovePre; + } + + public static String getTablePrefix() + { + return tablePrefix; + } + + @Value("${tablePrefix}") + public void setTablePrefix(String tablePrefix) + { + GenConfig.tablePrefix = tablePrefix; + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java new file mode 100644 index 0000000..51ab569 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java @@ -0,0 +1,214 @@ +package com.ruoyi.generator.controller; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.servlet.http.HttpServletResponse; +import org.apache.commons.io.IOUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +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.core.text.Convert; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.generator.domain.GenTable; +import com.ruoyi.generator.domain.GenTableColumn; +import com.ruoyi.generator.service.IGenTableColumnService; +import com.ruoyi.generator.service.IGenTableService; + +/** + * 代码生成 操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/tool/gen") +public class GenController extends BaseController +{ + @Autowired + private IGenTableService genTableService; + + @Autowired + private IGenTableColumnService genTableColumnService; + + /** + * 查询代码生成列表 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:list')") + @GetMapping("/list") + public TableDataInfo genList(GenTable genTable) + { +// startPage(); + List<GenTable> list = genTableService.selectGenTableList(genTable); + return getDataTable(list); + } + + /** + * 修改代码生成业务 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:query')") + @GetMapping(value = "/{tableId}") + public AjaxResult getInfo(@PathVariable Long tableId) + { + GenTable table = genTableService.selectGenTableById(tableId); + List<GenTable> tables = genTableService.selectGenTableAll(); + List<GenTableColumn> list = genTableColumnService.selectGenTableColumnListByTableId(tableId); + Map<String, Object> map = new HashMap<String, Object>(); + map.put("info", table); + map.put("rows", list); + map.put("tables", tables); + return success(map); + } + + /** + * 查询数据库列表 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:list')") + @GetMapping("/db/list") + public TableDataInfo dataList(GenTable genTable) + { +// startPage(); + List<GenTable> list = genTableService.selectDbTableList(genTable); + return getDataTable(list); + } + + /** + * 查询数据表字段列表 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:list')") + @GetMapping(value = "/column/{tableId}") + public TableDataInfo columnList(Long tableId) + { + TableDataInfo dataInfo = new TableDataInfo(); + List<GenTableColumn> list = genTableColumnService.selectGenTableColumnListByTableId(tableId); + dataInfo.setRecords(list); + dataInfo.setTotal(list.size()); + return dataInfo; + } + + /** + * 导入表结构(保存) + */ + @PreAuthorize("@ss.hasPermi('tool:gen:import')") + @Log(title = "代码生成", businessType = BusinessType.IMPORT) + @PostMapping("/importTable") + public AjaxResult importTableSave(String tables) + { + String[] tableNames = Convert.toStrArray(tables); + // 查询表信息 + List<GenTable> tableList = genTableService.selectDbTableListByNames(tableNames); + genTableService.importGenTable(tableList); + return success(); + } + + /** + * 修改保存代码生成业务 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:edit')") + @Log(title = "代码生成", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult editSave(@Validated @RequestBody GenTable genTable) + { + genTableService.validateEdit(genTable); + genTableService.updateGenTable(genTable); + return success(); + } + + /** + * 删除代码生成 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:remove')") + @Log(title = "代码生成", businessType = BusinessType.DELETE) + @DeleteMapping("/{tableIds}") + public AjaxResult remove(@PathVariable Long[] tableIds) + { + genTableService.deleteGenTableByIds(tableIds); + return success(); + } + + /** + * 预览代码 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:preview')") + @GetMapping("/preview/{tableId}") + public AjaxResult preview(@PathVariable("tableId") Long tableId) throws IOException + { + Map<String, String> dataMap = genTableService.previewCode(tableId); + return success(dataMap); + } + + /** + * 生成代码(下载方式) + */ + @PreAuthorize("@ss.hasPermi('tool:gen:code')") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/download/{tableName}") + public void download(HttpServletResponse response, @PathVariable("tableName") String tableName) throws IOException + { + byte[] data = genTableService.downloadCode(tableName); + genCode(response, data); + } + + /** + * 生成代码(自定义路径) + */ + @PreAuthorize("@ss.hasPermi('tool:gen:code')") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/genCode/{tableName}") + public AjaxResult genCode(@PathVariable("tableName") String tableName) + { + genTableService.generatorCode(tableName); + return success(); + } + + /** + * 同步数据库 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:edit')") + @Log(title = "代码生成", businessType = BusinessType.UPDATE) + @GetMapping("/synchDb/{tableName}") + public AjaxResult synchDb(@PathVariable("tableName") String tableName) + { + genTableService.synchDb(tableName); + return success(); + } + + /** + * 批量生成代码 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:code')") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/batchGenCode") + public void batchGenCode(HttpServletResponse response, String tables) throws IOException + { + String[] tableNames = Convert.toStrArray(tables); + byte[] data = genTableService.downloadCode(tableNames); + genCode(response, data); + } + + /** + * 生成zip文件 + */ + private void genCode(HttpServletResponse response, byte[] data) throws IOException + { + response.reset(); + response.addHeader("Access-Control-Allow-Origin", "*"); + response.addHeader("Access-Control-Expose-Headers", "Content-Disposition"); + response.setHeader("Content-Disposition", "attachment; filename=\"ruoyi.zip\""); + response.addHeader("Content-Length", "" + data.length); + response.setContentType("application/octet-stream; charset=UTF-8"); + IOUtils.write(data, response.getOutputStream()); + } +} \ No newline at end of file diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java new file mode 100644 index 0000000..269779c --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java @@ -0,0 +1,372 @@ +package com.ruoyi.generator.domain; + +import java.util.List; +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import org.apache.commons.lang3.ArrayUtils; +import com.ruoyi.common.constant.GenConstants; +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.utils.StringUtils; + +/** + * 业务表 gen_table + * + * @author ruoyi + */ +public class GenTable extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 编号 */ + private Long tableId; + + /** 表名称 */ + @NotBlank(message = "表名称不能为空") + private String tableName; + + /** 表描述 */ + @NotBlank(message = "表描述不能为空") + private String tableComment; + + /** 关联父表的表名 */ + private String subTableName; + + /** 本表关联父表的外键名 */ + private String subTableFkName; + + /** 实体类名称(首字母大写) */ + @NotBlank(message = "实体类名称不能为空") + private String className; + + /** 使用的模板(crud单表操作 tree树表操作 sub主子表操作) */ + private String tplCategory; + + /** 生成包路径 */ + @NotBlank(message = "生成包路径不能为空") + private String packageName; + + /** 生成模块名 */ + @NotBlank(message = "生成模块名不能为空") + private String moduleName; + + /** 生成业务名 */ + @NotBlank(message = "生成业务名不能为空") + private String businessName; + + /** 生成功能名 */ + @NotBlank(message = "生成功能名不能为空") + private String functionName; + + /** 生成作者 */ + @NotBlank(message = "作者不能为空") + private String functionAuthor; + + /** 生成代码方式(0zip压缩包 1自定义路径) */ + private String genType; + + /** 生成路径(不填默认项目路径) */ + private String genPath; + + /** 主键信息 */ + private GenTableColumn pkColumn; + + /** 子表信息 */ + private GenTable subTable; + + /** 表列信息 */ + @Valid + private List<GenTableColumn> columns; + + /** 其它生成选项 */ + private String options; + + /** 树编码字段 */ + private String treeCode; + + /** 树父编码字段 */ + private String treeParentCode; + + /** 树名称字段 */ + private String treeName; + + /** 上级菜单ID字段 */ + private String parentMenuId; + + /** 上级菜单名称字段 */ + private String parentMenuName; + + public Long getTableId() + { + return tableId; + } + + public void setTableId(Long tableId) + { + this.tableId = tableId; + } + + public String getTableName() + { + return tableName; + } + + public void setTableName(String tableName) + { + this.tableName = tableName; + } + + public String getTableComment() + { + return tableComment; + } + + public void setTableComment(String tableComment) + { + this.tableComment = tableComment; + } + + public String getSubTableName() + { + return subTableName; + } + + public void setSubTableName(String subTableName) + { + this.subTableName = subTableName; + } + + public String getSubTableFkName() + { + return subTableFkName; + } + + public void setSubTableFkName(String subTableFkName) + { + this.subTableFkName = subTableFkName; + } + + public String getClassName() + { + return className; + } + + public void setClassName(String className) + { + this.className = className; + } + + public String getTplCategory() + { + return tplCategory; + } + + public void setTplCategory(String tplCategory) + { + this.tplCategory = tplCategory; + } + + public String getPackageName() + { + return packageName; + } + + public void setPackageName(String packageName) + { + this.packageName = packageName; + } + + public String getModuleName() + { + return moduleName; + } + + public void setModuleName(String moduleName) + { + this.moduleName = moduleName; + } + + public String getBusinessName() + { + return businessName; + } + + public void setBusinessName(String businessName) + { + this.businessName = businessName; + } + + public String getFunctionName() + { + return functionName; + } + + public void setFunctionName(String functionName) + { + this.functionName = functionName; + } + + public String getFunctionAuthor() + { + return functionAuthor; + } + + public void setFunctionAuthor(String functionAuthor) + { + this.functionAuthor = functionAuthor; + } + + public String getGenType() + { + return genType; + } + + public void setGenType(String genType) + { + this.genType = genType; + } + + public String getGenPath() + { + return genPath; + } + + public void setGenPath(String genPath) + { + this.genPath = genPath; + } + + public GenTableColumn getPkColumn() + { + return pkColumn; + } + + public void setPkColumn(GenTableColumn pkColumn) + { + this.pkColumn = pkColumn; + } + + public GenTable getSubTable() + { + return subTable; + } + + public void setSubTable(GenTable subTable) + { + this.subTable = subTable; + } + + public List<GenTableColumn> getColumns() + { + return columns; + } + + public void setColumns(List<GenTableColumn> columns) + { + this.columns = columns; + } + + public String getOptions() + { + return options; + } + + public void setOptions(String options) + { + this.options = options; + } + + public String getTreeCode() + { + return treeCode; + } + + public void setTreeCode(String treeCode) + { + this.treeCode = treeCode; + } + + public String getTreeParentCode() + { + return treeParentCode; + } + + public void setTreeParentCode(String treeParentCode) + { + this.treeParentCode = treeParentCode; + } + + public String getTreeName() + { + return treeName; + } + + public void setTreeName(String treeName) + { + this.treeName = treeName; + } + + public String getParentMenuId() + { + return parentMenuId; + } + + public void setParentMenuId(String parentMenuId) + { + this.parentMenuId = parentMenuId; + } + + public String getParentMenuName() + { + return parentMenuName; + } + + public void setParentMenuName(String parentMenuName) + { + this.parentMenuName = parentMenuName; + } + + public boolean isSub() + { + return isSub(this.tplCategory); + } + + public static boolean isSub(String tplCategory) + { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_SUB, tplCategory); + } + + public boolean isTree() + { + return isTree(this.tplCategory); + } + + public static boolean isTree(String tplCategory) + { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_TREE, tplCategory); + } + + public boolean isCrud() + { + return isCrud(this.tplCategory); + } + + public static boolean isCrud(String tplCategory) + { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_CRUD, tplCategory); + } + + public boolean isSuperColumn(String javaField) + { + return isSuperColumn(this.tplCategory, javaField); + } + + public static boolean isSuperColumn(String tplCategory, String javaField) + { + if (isTree(tplCategory)) + { + return StringUtils.equalsAnyIgnoreCase(javaField, + ArrayUtils.addAll(GenConstants.TREE_ENTITY, GenConstants.BASE_ENTITY)); + } + return StringUtils.equalsAnyIgnoreCase(javaField, GenConstants.BASE_ENTITY); + } +} \ No newline at end of file diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java new file mode 100644 index 0000000..d1733b6 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java @@ -0,0 +1,373 @@ +package com.ruoyi.generator.domain; + +import javax.validation.constraints.NotBlank; +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.utils.StringUtils; + +/** + * 代码生成业务字段表 gen_table_column + * + * @author ruoyi + */ +public class GenTableColumn extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 编号 */ + private Long columnId; + + /** 归属表编号 */ + private Long tableId; + + /** 列名称 */ + private String columnName; + + /** 列描述 */ + private String columnComment; + + /** 列类型 */ + private String columnType; + + /** JAVA类型 */ + private String javaType; + + /** JAVA字段名 */ + @NotBlank(message = "Java属性不能为空") + private String javaField; + + /** 是否主键(1是) */ + private String isPk; + + /** 是否自增(1是) */ + private String isIncrement; + + /** 是否必填(1是) */ + private String isRequired; + + /** 是否为插入字段(1是) */ + private String isInsert; + + /** 是否编辑字段(1是) */ + private String isEdit; + + /** 是否列表字段(1是) */ + private String isList; + + /** 是否查询字段(1是) */ + private String isQuery; + + /** 查询方式(EQ等于、NE不等于、GT大于、LT小于、LIKE模糊、BETWEEN范围) */ + private String queryType; + + /** 显示类型(input文本框、textarea文本域、select下拉框、checkbox复选框、radio单选框、datetime日期控件、image图片上传控件、upload文件上传控件、editor富文本控件) */ + private String htmlType; + + /** 字典类型 */ + private String dictType; + + /** 排序 */ + private Integer sort; + + public void setColumnId(Long columnId) + { + this.columnId = columnId; + } + + public Long getColumnId() + { + return columnId; + } + + public void setTableId(Long tableId) + { + this.tableId = tableId; + } + + public Long getTableId() + { + return tableId; + } + + public void setColumnName(String columnName) + { + this.columnName = columnName; + } + + public String getColumnName() + { + return columnName; + } + + public void setColumnComment(String columnComment) + { + this.columnComment = columnComment; + } + + public String getColumnComment() + { + return columnComment; + } + + public void setColumnType(String columnType) + { + this.columnType = columnType; + } + + public String getColumnType() + { + return columnType; + } + + public void setJavaType(String javaType) + { + this.javaType = javaType; + } + + public String getJavaType() + { + return javaType; + } + + public void setJavaField(String javaField) + { + this.javaField = javaField; + } + + public String getJavaField() + { + return javaField; + } + + public String getCapJavaField() + { + return StringUtils.capitalize(javaField); + } + + public void setIsPk(String isPk) + { + this.isPk = isPk; + } + + public String getIsPk() + { + return isPk; + } + + public boolean isPk() + { + return isPk(this.isPk); + } + + public boolean isPk(String isPk) + { + return isPk != null && StringUtils.equals("1", isPk); + } + + public String getIsIncrement() + { + return isIncrement; + } + + public void setIsIncrement(String isIncrement) + { + this.isIncrement = isIncrement; + } + + public boolean isIncrement() + { + return isIncrement(this.isIncrement); + } + + public boolean isIncrement(String isIncrement) + { + return isIncrement != null && StringUtils.equals("1", isIncrement); + } + + public void setIsRequired(String isRequired) + { + this.isRequired = isRequired; + } + + public String getIsRequired() + { + return isRequired; + } + + public boolean isRequired() + { + return isRequired(this.isRequired); + } + + public boolean isRequired(String isRequired) + { + return isRequired != null && StringUtils.equals("1", isRequired); + } + + public void setIsInsert(String isInsert) + { + this.isInsert = isInsert; + } + + public String getIsInsert() + { + return isInsert; + } + + public boolean isInsert() + { + return isInsert(this.isInsert); + } + + public boolean isInsert(String isInsert) + { + return isInsert != null && StringUtils.equals("1", isInsert); + } + + public void setIsEdit(String isEdit) + { + this.isEdit = isEdit; + } + + public String getIsEdit() + { + return isEdit; + } + + public boolean isEdit() + { + return isInsert(this.isEdit); + } + + public boolean isEdit(String isEdit) + { + return isEdit != null && StringUtils.equals("1", isEdit); + } + + public void setIsList(String isList) + { + this.isList = isList; + } + + public String getIsList() + { + return isList; + } + + public boolean isList() + { + return isList(this.isList); + } + + public boolean isList(String isList) + { + return isList != null && StringUtils.equals("1", isList); + } + + public void setIsQuery(String isQuery) + { + this.isQuery = isQuery; + } + + public String getIsQuery() + { + return isQuery; + } + + public boolean isQuery() + { + return isQuery(this.isQuery); + } + + public boolean isQuery(String isQuery) + { + return isQuery != null && StringUtils.equals("1", isQuery); + } + + public void setQueryType(String queryType) + { + this.queryType = queryType; + } + + public String getQueryType() + { + return queryType; + } + + public String getHtmlType() + { + return htmlType; + } + + public void setHtmlType(String htmlType) + { + this.htmlType = htmlType; + } + + public void setDictType(String dictType) + { + this.dictType = dictType; + } + + public String getDictType() + { + return dictType; + } + + public void setSort(Integer sort) + { + this.sort = sort; + } + + public Integer getSort() + { + return sort; + } + + public boolean isSuperColumn() + { + return isSuperColumn(this.javaField); + } + + public static boolean isSuperColumn(String javaField) + { + return StringUtils.equalsAnyIgnoreCase(javaField, + // BaseEntity + "createBy", "createTime", "updateBy", "updateTime", "remark", + // TreeEntity + "parentName", "parentId", "orderNum", "ancestors"); + } + + public boolean isUsableColumn() + { + return isUsableColumn(javaField); + } + + public static boolean isUsableColumn(String javaField) + { + // isSuperColumn()中的名单用于避免生成多余Domain属性,若某些属性在生成页面时需要用到不能忽略,则放在此处白名单 + return StringUtils.equalsAnyIgnoreCase(javaField, "parentId", "orderNum", "remark"); + } + + public String readConverterExp() + { + String remarks = StringUtils.substringBetween(this.columnComment, "(", ")"); + StringBuffer sb = new StringBuffer(); + if (StringUtils.isNotEmpty(remarks)) + { + for (String value : remarks.split(" ")) + { + if (StringUtils.isNotEmpty(value)) + { + Object startStr = value.subSequence(0, 1); + String endStr = value.substring(1); + sb.append("").append(startStr).append("=").append(endStr).append(","); + } + } + return sb.deleteCharAt(sb.length() - 1).toString(); + } + else + { + return this.columnComment; + } + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java new file mode 100644 index 0000000..951e166 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java @@ -0,0 +1,60 @@ +package com.ruoyi.generator.mapper; + +import java.util.List; +import com.ruoyi.generator.domain.GenTableColumn; + +/** + * 业务字段 数据层 + * + * @author ruoyi + */ +public interface GenTableColumnMapper +{ + /** + * 根据表名称查询列信息 + * + * @param tableName 表名称 + * @return 列信息 + */ + public List<GenTableColumn> selectDbTableColumnsByName(String tableName); + + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + public List<GenTableColumn> selectGenTableColumnListByTableId(Long tableId); + + /** + * 新增业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + public int insertGenTableColumn(GenTableColumn genTableColumn); + + /** + * 修改业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + public int updateGenTableColumn(GenTableColumn genTableColumn); + + /** + * 删除业务字段 + * + * @param genTableColumns 列数据 + * @return 结果 + */ + public int deleteGenTableColumns(List<GenTableColumn> genTableColumns); + + /** + * 批量删除业务字段 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteGenTableColumnByIds(Long[] ids); +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java new file mode 100644 index 0000000..9b330df --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java @@ -0,0 +1,83 @@ +package com.ruoyi.generator.mapper; + +import java.util.List; +import com.ruoyi.generator.domain.GenTable; + +/** + * 业务 数据层 + * + * @author ruoyi + */ +public interface GenTableMapper +{ + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * @return 业务集合 + */ + public List<GenTable> selectGenTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * @return 数据库表集合 + */ + public List<GenTable> selectDbTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @return 数据库表集合 + */ + public List<GenTable> selectDbTableListByNames(String[] tableNames); + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + public List<GenTable> selectGenTableAll(); + + /** + * 查询表ID业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + public GenTable selectGenTableById(Long id); + + /** + * 查询表名称业务信息 + * + * @param tableName 表名称 + * @return 业务信息 + */ + public GenTable selectGenTableByName(String tableName); + + /** + * 新增业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + public int insertGenTable(GenTable genTable); + + /** + * 修改业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + public int updateGenTable(GenTable genTable); + + /** + * 批量删除业务 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteGenTableByIds(Long[] ids); +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableColumnServiceImpl.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableColumnServiceImpl.java new file mode 100644 index 0000000..0679689 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableColumnServiceImpl.java @@ -0,0 +1,68 @@ +package com.ruoyi.generator.service; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.generator.domain.GenTableColumn; +import com.ruoyi.generator.mapper.GenTableColumnMapper; + +/** + * 业务字段 服务层实现 + * + * @author ruoyi + */ +@Service +public class GenTableColumnServiceImpl implements IGenTableColumnService +{ + @Autowired + private GenTableColumnMapper genTableColumnMapper; + + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + @Override + public List<GenTableColumn> selectGenTableColumnListByTableId(Long tableId) + { + return genTableColumnMapper.selectGenTableColumnListByTableId(tableId); + } + + /** + * 新增业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + @Override + public int insertGenTableColumn(GenTableColumn genTableColumn) + { + return genTableColumnMapper.insertGenTableColumn(genTableColumn); + } + + /** + * 修改业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + @Override + public int updateGenTableColumn(GenTableColumn genTableColumn) + { + return genTableColumnMapper.updateGenTableColumn(genTableColumn); + } + + /** + * 删除业务字段对象 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + @Override + public int deleteGenTableColumnByIds(String ids) + { + return genTableColumnMapper.deleteGenTableColumnByIds(Convert.toLongArray(ids)); + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java new file mode 100644 index 0000000..4889f81 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java @@ -0,0 +1,521 @@ +package com.ruoyi.generator.service; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.Velocity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.GenConstants; +import com.ruoyi.common.core.text.CharsetKit; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.generator.domain.GenTable; +import com.ruoyi.generator.domain.GenTableColumn; +import com.ruoyi.generator.mapper.GenTableColumnMapper; +import com.ruoyi.generator.mapper.GenTableMapper; +import com.ruoyi.generator.util.GenUtils; +import com.ruoyi.generator.util.VelocityInitializer; +import com.ruoyi.generator.util.VelocityUtils; + +/** + * 业务 服务层实现 + * + * @author ruoyi + */ +@Service +public class GenTableServiceImpl implements IGenTableService +{ + private static final Logger log = LoggerFactory.getLogger(GenTableServiceImpl.class); + + @Autowired + private GenTableMapper genTableMapper; + + @Autowired + private GenTableColumnMapper genTableColumnMapper; + + /** + * 查询业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + @Override + public GenTable selectGenTableById(Long id) + { + GenTable genTable = genTableMapper.selectGenTableById(id); + setTableFromOptions(genTable); + return genTable; + } + + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * @return 业务集合 + */ + @Override + public List<GenTable> selectGenTableList(GenTable genTable) + { + return genTableMapper.selectGenTableList(genTable); + } + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * @return 数据库表集合 + */ + @Override + public List<GenTable> selectDbTableList(GenTable genTable) + { + return genTableMapper.selectDbTableList(genTable); + } + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @return 数据库表集合 + */ + @Override + public List<GenTable> selectDbTableListByNames(String[] tableNames) + { + return genTableMapper.selectDbTableListByNames(tableNames); + } + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + @Override + public List<GenTable> selectGenTableAll() + { + return genTableMapper.selectGenTableAll(); + } + + /** + * 修改业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + @Override + @Transactional + public void updateGenTable(GenTable genTable) + { + String options = JSON.toJSONString(genTable.getParams()); + genTable.setOptions(options); + int row = genTableMapper.updateGenTable(genTable); + if (row > 0) + { + for (GenTableColumn cenTableColumn : genTable.getColumns()) + { + genTableColumnMapper.updateGenTableColumn(cenTableColumn); + } + } + } + + /** + * 删除业务对象 + * + * @param tableIds 需要删除的数据ID + * @return 结果 + */ + @Override + @Transactional + public void deleteGenTableByIds(Long[] tableIds) + { + genTableMapper.deleteGenTableByIds(tableIds); + genTableColumnMapper.deleteGenTableColumnByIds(tableIds); + } + + /** + * 导入表结构 + * + * @param tableList 导入表列表 + */ + @Override + @Transactional + public void importGenTable(List<GenTable> tableList) + { + String operName = SecurityUtils.getUsername(); + try + { + for (GenTable table : tableList) + { + String tableName = table.getTableName(); + GenUtils.initTable(table, operName); + int row = genTableMapper.insertGenTable(table); + if (row > 0) + { + // 保存列信息 + List<GenTableColumn> genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName); + for (GenTableColumn column : genTableColumns) + { + GenUtils.initColumnField(column, table); + genTableColumnMapper.insertGenTableColumn(column); + } + } + } + } + catch (Exception e) + { + throw new ServiceException("导入失败:" + e.getMessage()); + } + } + + /** + * 预览代码 + * + * @param tableId 表编号 + * @return 预览数据列表 + */ + @Override + public Map<String, String> previewCode(Long tableId) + { + Map<String, String> dataMap = new LinkedHashMap<>(); + // 查询表信息 + GenTable table = genTableMapper.selectGenTableById(tableId); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory()); + for (String template : templates) + { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + dataMap.put(template, sw.toString()); + } + return dataMap; + } + + /** + * 生成代码(下载方式) + * + * @param tableName 表名称 + * @return 数据 + */ + @Override + public byte[] downloadCode(String tableName) + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ZipOutputStream zip = new ZipOutputStream(outputStream); + generatorCode(tableName, zip); + IOUtils.closeQuietly(zip); + return outputStream.toByteArray(); + } + + /** + * 生成代码(自定义路径) + * + * @param tableName 表名称 + */ + @Override + public void generatorCode(String tableName) + { + // 查询表信息 + GenTable table = genTableMapper.selectGenTableByName(tableName); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory()); + for (String template : templates) + { + if (!StringUtils.containsAny(template, "sql.vm", "api.js.vm", "index.vue.vm", "index-tree.vue.vm")) + { + // 渲染模板 + 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(), CharsetKit.UTF_8); + } + catch (IOException e) + { + throw new ServiceException("渲染模板失败,表名:" + table.getTableName()); + } + } + } + } + + /** + * 同步数据库 + * + * @param tableName 表名称 + */ + @Override + @Transactional + public void synchDb(String tableName) + { + GenTable table = genTableMapper.selectGenTableByName(tableName); + List<GenTableColumn> tableColumns = table.getColumns(); + Map<String, GenTableColumn> tableColumnMap = tableColumns.stream().collect(Collectors.toMap(GenTableColumn::getColumnName, Function.identity())); + + List<GenTableColumn> dbTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName); + if (StringUtils.isEmpty(dbTableColumns)) + { + throw new ServiceException("同步数据失败,原表结构不存在"); + } + List<String> dbTableColumnNames = dbTableColumns.stream().map(GenTableColumn::getColumnName).collect(Collectors.toList()); + + dbTableColumns.forEach(column -> { + GenUtils.initColumnField(column, table); + if (tableColumnMap.containsKey(column.getColumnName())) + { + GenTableColumn prevColumn = tableColumnMap.get(column.getColumnName()); + column.setColumnId(prevColumn.getColumnId()); + if (column.isList()) + { + // 如果是列表,继续保留查询方式/字典类型选项 + column.setDictType(prevColumn.getDictType()); + column.setQueryType(prevColumn.getQueryType()); + } + if (StringUtils.isNotEmpty(prevColumn.getIsRequired()) && !column.isPk() + && (column.isInsert() || column.isEdit()) + && ((column.isUsableColumn()) || (!column.isSuperColumn()))) + { + // 如果是(新增/修改&非主键/非忽略及父属性),继续保留必填/显示类型选项 + column.setIsRequired(prevColumn.getIsRequired()); + column.setHtmlType(prevColumn.getHtmlType()); + } + genTableColumnMapper.updateGenTableColumn(column); + } + else + { + genTableColumnMapper.insertGenTableColumn(column); + } + }); + + List<GenTableColumn> delColumns = tableColumns.stream().filter(column -> !dbTableColumnNames.contains(column.getColumnName())).collect(Collectors.toList()); + if (StringUtils.isNotEmpty(delColumns)) + { + genTableColumnMapper.deleteGenTableColumns(delColumns); + } + } + + /** + * 批量生成代码(下载方式) + * + * @param tableNames 表数组 + * @return 数据 + */ + @Override + public byte[] downloadCode(String[] tableNames) + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ZipOutputStream zip = new ZipOutputStream(outputStream); + for (String tableName : tableNames) + { + generatorCode(tableName, zip); + } + IOUtils.closeQuietly(zip); + return outputStream.toByteArray(); + } + + /** + * 查询表信息并生成代码 + */ + private void generatorCode(String tableName, ZipOutputStream zip) + { + // 查询表信息 + GenTable table = genTableMapper.selectGenTableByName(tableName); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory()); + for (String template : templates) + { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + try + { + // 添加到zip + zip.putNextEntry(new ZipEntry(VelocityUtils.getFileName(template, table))); + IOUtils.write(sw.toString(), zip, Constants.UTF8); + IOUtils.closeQuietly(sw); + zip.flush(); + zip.closeEntry(); + } + catch (IOException e) + { + log.error("渲染模板失败,表名:" + table.getTableName(), e); + } + } + } + + /** + * 修改保存参数校验 + * + * @param genTable 业务信息 + */ + @Override + public void validateEdit(GenTable genTable) + { + if (GenConstants.TPL_TREE.equals(genTable.getTplCategory())) + { + String options = JSON.toJSONString(genTable.getParams()); + JSONObject paramsObj = JSON.parseObject(options); + if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_CODE))) + { + throw new ServiceException("树编码字段不能为空"); + } + else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_PARENT_CODE))) + { + throw new ServiceException("树父编码字段不能为空"); + } + else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_NAME))) + { + throw new ServiceException("树名称字段不能为空"); + } + else if (GenConstants.TPL_SUB.equals(genTable.getTplCategory())) + { + if (StringUtils.isEmpty(genTable.getSubTableName())) + { + throw new ServiceException("关联子表的表名不能为空"); + } + else if (StringUtils.isEmpty(genTable.getSubTableFkName())) + { + throw new ServiceException("子表关联的外键名不能为空"); + } + } + } + } + + /** + * 设置主键列信息 + * + * @param table 业务表信息 + */ + public void setPkColumn(GenTable table) + { + for (GenTableColumn column : table.getColumns()) + { + if (column.isPk()) + { + table.setPkColumn(column); + break; + } + } + if (StringUtils.isNull(table.getPkColumn())) + { + table.setPkColumn(table.getColumns().get(0)); + } + if (GenConstants.TPL_SUB.equals(table.getTplCategory())) + { + for (GenTableColumn column : table.getSubTable().getColumns()) + { + if (column.isPk()) + { + table.getSubTable().setPkColumn(column); + break; + } + } + if (StringUtils.isNull(table.getSubTable().getPkColumn())) + { + table.getSubTable().setPkColumn(table.getSubTable().getColumns().get(0)); + } + } + } + + /** + * 设置主子表信息 + * + * @param table 业务表信息 + */ + public void setSubTable(GenTable table) + { + String subTableName = table.getSubTableName(); + if (StringUtils.isNotEmpty(subTableName)) + { + table.setSubTable(genTableMapper.selectGenTableByName(subTableName)); + } + } + + /** + * 设置代码生成其他选项值 + * + * @param genTable 设置后的生成对象 + */ + public void setTableFromOptions(GenTable genTable) + { + JSONObject paramsObj = JSON.parseObject(genTable.getOptions()); + if (StringUtils.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); + + genTable.setTreeCode(treeCode); + genTable.setTreeParentCode(treeParentCode); + genTable.setTreeName(treeName); + genTable.setParentMenuId(parentMenuId); + genTable.setParentMenuName(parentMenuName); + } + } + + /** + * 获取代码生成地址 + * + * @param table 业务表信息 + * @param template 模板文件路径 + * @return 生成地址 + */ + public static String getGenPath(GenTable table, String template) + { + String genPath = table.getGenPath(); + if (StringUtils.equals(genPath, "/")) + { + return System.getProperty("user.dir") + File.separator + "src" + File.separator + VelocityUtils.getFileName(template, table); + } + return genPath + File.separator + VelocityUtils.getFileName(template, table); + } +} \ No newline at end of file diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableColumnService.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableColumnService.java new file mode 100644 index 0000000..3037f70 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableColumnService.java @@ -0,0 +1,44 @@ +package com.ruoyi.generator.service; + +import java.util.List; +import com.ruoyi.generator.domain.GenTableColumn; + +/** + * 业务字段 服务层 + * + * @author ruoyi + */ +public interface IGenTableColumnService +{ + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + public List<GenTableColumn> selectGenTableColumnListByTableId(Long tableId); + + /** + * 新增业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + public int insertGenTableColumn(GenTableColumn genTableColumn); + + /** + * 修改业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + public int updateGenTableColumn(GenTableColumn genTableColumn); + + /** + * 删除业务字段信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteGenTableColumnByIds(String ids); +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java new file mode 100644 index 0000000..9d53f95 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java @@ -0,0 +1,121 @@ +package com.ruoyi.generator.service; + +import java.util.List; +import java.util.Map; +import com.ruoyi.generator.domain.GenTable; + +/** + * 业务 服务层 + * + * @author ruoyi + */ +public interface IGenTableService +{ + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * @return 业务集合 + */ + public List<GenTable> selectGenTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * @return 数据库表集合 + */ + public List<GenTable> selectDbTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @return 数据库表集合 + */ + public List<GenTable> selectDbTableListByNames(String[] tableNames); + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + public List<GenTable> selectGenTableAll(); + + /** + * 查询业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + public GenTable selectGenTableById(Long id); + + /** + * 修改业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + public void updateGenTable(GenTable genTable); + + /** + * 删除业务信息 + * + * @param tableIds 需要删除的表数据ID + * @return 结果 + */ + public void deleteGenTableByIds(Long[] tableIds); + + /** + * 导入表结构 + * + * @param tableList 导入表列表 + */ + public void importGenTable(List<GenTable> tableList); + + /** + * 预览代码 + * + * @param tableId 表编号 + * @return 预览数据列表 + */ + public Map<String, String> previewCode(Long tableId); + + /** + * 生成代码(下载方式) + * + * @param tableName 表名称 + * @return 数据 + */ + public byte[] downloadCode(String tableName); + + /** + * 生成代码(自定义路径) + * + * @param tableName 表名称 + * @return 数据 + */ + public void generatorCode(String tableName); + + /** + * 同步数据库 + * + * @param tableName 表名称 + */ + public void synchDb(String tableName); + + /** + * 批量生成代码(下载方式) + * + * @param tableNames 表数组 + * @return 数据 + */ + public byte[] downloadCode(String[] tableNames); + + /** + * 修改保存参数校验 + * + * @param genTable 业务信息 + */ + public void validateEdit(GenTable genTable); +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java new file mode 100644 index 0000000..e7ebc20 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java @@ -0,0 +1,257 @@ +package com.ruoyi.generator.util; + +import java.util.Arrays; +import org.apache.commons.lang3.RegExUtils; +import com.ruoyi.common.constant.GenConstants; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.generator.config.GenConfig; +import com.ruoyi.generator.domain.GenTable; +import com.ruoyi.generator.domain.GenTableColumn; + +/** + * 代码生成器 工具类 + * + * @author ruoyi + */ +public class GenUtils +{ + /** + * 初始化表信息 + */ + public static void initTable(GenTable genTable, String operName) + { + genTable.setClassName(convertClassName(genTable.getTableName())); + genTable.setPackageName(GenConfig.getPackageName()); + genTable.setModuleName(getModuleName(GenConfig.getPackageName())); + genTable.setBusinessName(getBusinessName(genTable.getTableName())); + genTable.setFunctionName(replaceText(genTable.getTableComment())); + genTable.setFunctionAuthor(GenConfig.getAuthor()); + genTable.setCreateBy(operName); + } + + /** + * 初始化列属性字段 + */ + public static void initColumnField(GenTableColumn column, GenTable table) + { + String dataType = getDbType(column.getColumnType()); + String columnName = column.getColumnName(); + column.setTableId(table.getTableId()); + column.setCreateBy(table.getCreateBy()); + // 设置java字段名 + column.setJavaField(StringUtils.toCamelCase(columnName)); + // 设置默认类型 + column.setJavaType(GenConstants.TYPE_STRING); + column.setQueryType(GenConstants.QUERY_EQ); + + if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType) || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType)) + { + // 字符串长度超过500设置为文本域 + Integer columnLength = getColumnLength(column.getColumnType()); + String htmlType = columnLength >= 500 || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType) ? GenConstants.HTML_TEXTAREA : GenConstants.HTML_INPUT; + column.setHtmlType(htmlType); + } + else if (arraysContains(GenConstants.COLUMNTYPE_TIME, dataType)) + { + column.setJavaType(GenConstants.TYPE_DATE); + column.setHtmlType(GenConstants.HTML_DATETIME); + } + else if (arraysContains(GenConstants.COLUMNTYPE_NUMBER, dataType)) + { + column.setHtmlType(GenConstants.HTML_INPUT); + + // 如果是浮点型 统一用BigDecimal + String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), ","); + if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0) + { + column.setJavaType(GenConstants.TYPE_BIGDECIMAL); + } + // 如果是整形 + else if (str != null && str.length == 1 && Integer.parseInt(str[0]) <= 10) + { + column.setJavaType(GenConstants.TYPE_INTEGER); + } + // 长整形 + else + { + column.setJavaType(GenConstants.TYPE_LONG); + } + } + + // 插入字段(默认所有字段都需要插入) + column.setIsInsert(GenConstants.REQUIRE); + + // 编辑字段 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName) && !column.isPk()) + { + column.setIsEdit(GenConstants.REQUIRE); + } + // 列表字段 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_LIST, columnName) && !column.isPk()) + { + column.setIsList(GenConstants.REQUIRE); + } + // 查询字段 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_QUERY, columnName) && !column.isPk()) + { + column.setIsQuery(GenConstants.REQUIRE); + } + + // 查询字段类型 + if (StringUtils.endsWithIgnoreCase(columnName, "name")) + { + column.setQueryType(GenConstants.QUERY_LIKE); + } + // 状态字段设置单选框 + if (StringUtils.endsWithIgnoreCase(columnName, "status")) + { + column.setHtmlType(GenConstants.HTML_RADIO); + } + // 类型&性别字段设置下拉框 + else if (StringUtils.endsWithIgnoreCase(columnName, "type") + || StringUtils.endsWithIgnoreCase(columnName, "sex")) + { + column.setHtmlType(GenConstants.HTML_SELECT); + } + // 图片字段设置图片上传控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "image")) + { + column.setHtmlType(GenConstants.HTML_IMAGE_UPLOAD); + } + // 文件字段设置文件上传控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "file")) + { + column.setHtmlType(GenConstants.HTML_FILE_UPLOAD); + } + // 内容字段设置富文本控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "content")) + { + column.setHtmlType(GenConstants.HTML_EDITOR); + } + } + + /** + * 校验数组是否包含指定值 + * + * @param arr 数组 + * @param targetValue 值 + * @return 是否包含 + */ + public static boolean arraysContains(String[] arr, String targetValue) + { + return Arrays.asList(arr).contains(targetValue); + } + + /** + * 获取模块名 + * + * @param packageName 包名 + * @return 模块名 + */ + public static String getModuleName(String packageName) + { + int lastIndex = packageName.lastIndexOf("."); + int nameLength = packageName.length(); + return StringUtils.substring(packageName, lastIndex + 1, nameLength); + } + + /** + * 获取业务名 + * + * @param tableName 表名 + * @return 业务名 + */ + public static String getBusinessName(String tableName) + { + int lastIndex = tableName.lastIndexOf("_"); + int nameLength = tableName.length(); + return StringUtils.substring(tableName, lastIndex + 1, nameLength); + } + + /** + * 表名转换成Java类名 + * + * @param tableName 表名称 + * @return 类名 + */ + public static String convertClassName(String tableName) + { + boolean autoRemovePre = GenConfig.getAutoRemovePre(); + String tablePrefix = GenConfig.getTablePrefix(); + if (autoRemovePre && StringUtils.isNotEmpty(tablePrefix)) + { + String[] searchList = StringUtils.split(tablePrefix, ","); + tableName = replaceFirst(tableName, searchList); + } + return StringUtils.convertToCamelCase(tableName); + } + + /** + * 批量替换前缀 + * + * @param replacementm 替换值 + * @param searchList 替换列表 + * @return + */ + public static String replaceFirst(String replacementm, String[] searchList) + { + String text = replacementm; + for (String searchString : searchList) + { + if (replacementm.startsWith(searchString)) + { + text = replacementm.replaceFirst(searchString, ""); + break; + } + } + return text; + } + + /** + * 关键字替换 + * + * @param text 需要被替换的名字 + * @return 替换后的名字 + */ + public static String replaceText(String text) + { + return RegExUtils.replaceAll(text, "(?:表|若依)", ""); + } + + /** + * 获取数据库类型字段 + * + * @param columnType 列类型 + * @return 截取后的列类型 + */ + public static String getDbType(String columnType) + { + if (StringUtils.indexOf(columnType, "(") > 0) + { + return StringUtils.substringBefore(columnType, "("); + } + else + { + return columnType; + } + } + + /** + * 获取字段长度 + * + * @param columnType 列类型 + * @return 截取后的列类型 + */ + public static Integer getColumnLength(String columnType) + { + if (StringUtils.indexOf(columnType, "(") > 0) + { + String length = StringUtils.substringBetween(columnType, "(", ")"); + return Integer.valueOf(length); + } + else + { + return 0; + } + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityInitializer.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityInitializer.java new file mode 100644 index 0000000..9f69403 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityInitializer.java @@ -0,0 +1,34 @@ +package com.ruoyi.generator.util; + +import java.util.Properties; +import org.apache.velocity.app.Velocity; +import com.ruoyi.common.constant.Constants; + +/** + * VelocityEngine工厂 + * + * @author ruoyi + */ +public class VelocityInitializer +{ + /** + * 初始化vm方法 + */ + public static void initVelocity() + { + Properties p = new Properties(); + try + { + // 加载classpath目录下的vm文件 + p.setProperty("resource.loader.file.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); + // 定义字符集 + p.setProperty(Velocity.INPUT_ENCODING, Constants.UTF8); + // 初始化Velocity引擎,指定配置Properties + Velocity.init(p); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java new file mode 100644 index 0000000..7ede02d --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java @@ -0,0 +1,402 @@ +package com.ruoyi.generator.util; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.apache.velocity.VelocityContext; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.common.constant.GenConstants; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.generator.domain.GenTable; +import com.ruoyi.generator.domain.GenTableColumn; + +/** + * 模板处理工具类 + * + * @author ruoyi + */ +public class VelocityUtils +{ + /** 项目空间路径 */ + private static final String PROJECT_PATH = "main/java"; + + /** mybatis空间路径 */ + private static final String MYBATIS_PATH = "main/resources/mapper"; + + /** 默认上级菜单,系统工具 */ + private static final String DEFAULT_PARENT_MENU_ID = "3"; + + /** + * 设置模板变量信息 + * + * @return 模板列表 + */ + public static VelocityContext prepareContext(GenTable genTable) + { + String moduleName = genTable.getModuleName(); + String businessName = genTable.getBusinessName(); + String packageName = genTable.getPackageName(); + String tplCategory = genTable.getTplCategory(); + String functionName = genTable.getFunctionName(); + + VelocityContext velocityContext = new VelocityContext(); + velocityContext.put("tplCategory", genTable.getTplCategory()); + velocityContext.put("tableName", genTable.getTableName()); + velocityContext.put("functionName", StringUtils.isNotEmpty(functionName) ? functionName : "【请填写功能名称】"); + velocityContext.put("ClassName", genTable.getClassName()); + velocityContext.put("className", StringUtils.uncapitalize(genTable.getClassName())); + velocityContext.put("moduleName", genTable.getModuleName()); + velocityContext.put("BusinessName", StringUtils.capitalize(genTable.getBusinessName())); + velocityContext.put("businessName", genTable.getBusinessName()); + velocityContext.put("basePackage", getPackagePrefix(packageName)); + velocityContext.put("packageName", packageName); + velocityContext.put("author", genTable.getFunctionAuthor()); + velocityContext.put("datetime", DateUtils.getDate()); + velocityContext.put("pkColumn", genTable.getPkColumn()); + velocityContext.put("importList", getImportList(genTable)); + velocityContext.put("permissionPrefix", getPermissionPrefix(moduleName, businessName)); + velocityContext.put("columns", genTable.getColumns()); + velocityContext.put("table", genTable); + velocityContext.put("dicts", getDicts(genTable)); + setMenuVelocityContext(velocityContext, genTable); + if (GenConstants.TPL_TREE.equals(tplCategory)) + { + setTreeVelocityContext(velocityContext, genTable); + } + if (GenConstants.TPL_SUB.equals(tplCategory)) + { + setSubVelocityContext(velocityContext, genTable); + } + return velocityContext; + } + + public static void setMenuVelocityContext(VelocityContext context, GenTable genTable) + { + String options = genTable.getOptions(); + JSONObject paramsObj = JSON.parseObject(options); + String parentMenuId = getParentMenuId(paramsObj); + context.put("parentMenuId", parentMenuId); + } + + public static void setTreeVelocityContext(VelocityContext context, GenTable genTable) + { + String options = genTable.getOptions(); + JSONObject paramsObj = JSON.parseObject(options); + String treeCode = getTreecode(paramsObj); + String treeParentCode = getTreeParentCode(paramsObj); + String treeName = getTreeName(paramsObj); + + context.put("treeCode", treeCode); + context.put("treeParentCode", treeParentCode); + context.put("treeName", treeName); + context.put("expandColumn", getExpandColumn(genTable)); + if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) + { + context.put("tree_parent_code", paramsObj.getString(GenConstants.TREE_PARENT_CODE)); + } + if (paramsObj.containsKey(GenConstants.TREE_NAME)) + { + context.put("tree_name", paramsObj.getString(GenConstants.TREE_NAME)); + } + } + + public static void setSubVelocityContext(VelocityContext context, GenTable genTable) + { + GenTable subTable = genTable.getSubTable(); + String subTableName = genTable.getSubTableName(); + String subTableFkName = genTable.getSubTableFkName(); + String subClassName = genTable.getSubTable().getClassName(); + String subTableFkClassName = StringUtils.convertToCamelCase(subTableFkName); + + context.put("subTable", subTable); + context.put("subTableName", subTableName); + context.put("subTableFkName", subTableFkName); + context.put("subTableFkClassName", subTableFkClassName); + context.put("subTableFkclassName", StringUtils.uncapitalize(subTableFkClassName)); + context.put("subClassName", subClassName); + context.put("subclassName", StringUtils.uncapitalize(subClassName)); + context.put("subImportList", getImportList(genTable.getSubTable())); + } + + /** + * 获取模板信息 + * + * @return 模板列表 + */ + public static List<String> getTemplateList(String tplCategory) + { + List<String> templates = new ArrayList<String>(); + templates.add("vm/java/domain.java.vm"); + templates.add("vm/java/mapper.java.vm"); + templates.add("vm/java/service.java.vm"); + templates.add("vm/java/serviceImpl.java.vm"); + templates.add("vm/java/controller.java.vm"); + templates.add("vm/xml/mapper.xml.vm"); + templates.add("vm/sql/sql.vm"); + templates.add("vm/js/api.js.vm"); + if (GenConstants.TPL_CRUD.equals(tplCategory)) + { + templates.add("vm/vue/index.vue.vm"); + } + else if (GenConstants.TPL_TREE.equals(tplCategory)) + { + templates.add("vm/vue/index-tree.vue.vm"); + } + else if (GenConstants.TPL_SUB.equals(tplCategory)) + { + templates.add("vm/vue/index.vue.vm"); + templates.add("vm/java/sub-domain.java.vm"); + } + return templates; + } + + /** + * 获取文件名 + */ + public static String getFileName(String template, GenTable genTable) + { + // 文件名称 + String fileName = ""; + // 包路径 + String packageName = genTable.getPackageName(); + // 模块名 + String moduleName = genTable.getModuleName(); + // 大写类名 + String className = genTable.getClassName(); + // 业务名称 + String businessName = genTable.getBusinessName(); + + String javaPath = PROJECT_PATH + "/" + StringUtils.replace(packageName, ".", "/"); + String mybatisPath = MYBATIS_PATH + "/" + moduleName; + String vuePath = "vue"; + + if (template.contains("domain.java.vm")) + { + fileName = StringUtils.format("{}/domain/{}.java", javaPath, className); + } + if (template.contains("sub-domain.java.vm") && StringUtils.equals(GenConstants.TPL_SUB, genTable.getTplCategory())) + { + fileName = StringUtils.format("{}/domain/{}.java", javaPath, genTable.getSubTable().getClassName()); + } + else if (template.contains("mapper.java.vm")) + { + fileName = StringUtils.format("{}/mapper/{}Mapper.java", javaPath, className); + } + else if (template.contains("service.java.vm")) + { + fileName = StringUtils.format("{}/service/I{}Service.java", javaPath, className); + } + else if (template.contains("serviceImpl.java.vm")) + { + fileName = StringUtils.format("{}/service/impl/{}ServiceImpl.java", javaPath, className); + } + else if (template.contains("controller.java.vm")) + { + fileName = StringUtils.format("{}/controller/{}Controller.java", javaPath, className); + } + else if (template.contains("mapper.xml.vm")) + { + fileName = StringUtils.format("{}/{}Mapper.xml", mybatisPath, className); + } + else if (template.contains("sql.vm")) + { + fileName = businessName + "Menu.sql"; + } + else if (template.contains("api.js.vm")) + { + fileName = StringUtils.format("{}/api/{}/{}.js", vuePath, moduleName, businessName); + } + else if (template.contains("index.vue.vm")) + { + fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName); + } + else if (template.contains("index-tree.vue.vm")) + { + fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName); + } + return fileName; + } + + /** + * 获取包前缀 + * + * @param packageName 包名称 + * @return 包前缀名称 + */ + public static String getPackagePrefix(String packageName) + { + int lastIndex = packageName.lastIndexOf("."); + return StringUtils.substring(packageName, 0, lastIndex); + } + + /** + * 根据列类型获取导入包 + * + * @param genTable 业务表对象 + * @return 返回需要导入的包列表 + */ + public static HashSet<String> getImportList(GenTable genTable) + { + List<GenTableColumn> columns = genTable.getColumns(); + GenTable subGenTable = genTable.getSubTable(); + HashSet<String> importList = new HashSet<String>(); + if (StringUtils.isNotNull(subGenTable)) + { + importList.add("java.util.List"); + } + for (GenTableColumn column : columns) + { + if (!column.isSuperColumn() && GenConstants.TYPE_DATE.equals(column.getJavaType())) + { + importList.add("java.util.Date"); + importList.add("com.fasterxml.jackson.annotation.JsonFormat"); + } + else if (!column.isSuperColumn() && GenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType())) + { + importList.add("java.math.BigDecimal"); + } + } + return importList; + } + + /** + * 根据列类型获取字典组 + * + * @param genTable 业务表对象 + * @return 返回字典组 + */ + public static String getDicts(GenTable genTable) + { + List<GenTableColumn> columns = genTable.getColumns(); + Set<String> dicts = new HashSet<String>(); + addDicts(dicts, columns); + if (StringUtils.isNotNull(genTable.getSubTable())) + { + List<GenTableColumn> subColumns = genTable.getSubTable().getColumns(); + addDicts(dicts, subColumns); + } + return StringUtils.join(dicts, ", "); + } + + /** + * 添加字典列表 + * + * @param dicts 字典列表 + * @param columns 列集合 + */ + public static void addDicts(Set<String> dicts, List<GenTableColumn> columns) + { + for (GenTableColumn column : columns) + { + if (!column.isSuperColumn() && StringUtils.isNotEmpty(column.getDictType()) && StringUtils.equalsAny( + column.getHtmlType(), + new String[] { GenConstants.HTML_SELECT, GenConstants.HTML_RADIO, GenConstants.HTML_CHECKBOX })) + { + dicts.add("'" + column.getDictType() + "'"); + } + } + } + + /** + * 获取权限前缀 + * + * @param moduleName 模块名称 + * @param businessName 业务名称 + * @return 返回权限前缀 + */ + public static String getPermissionPrefix(String moduleName, String businessName) + { + return StringUtils.format("{}:{}", moduleName, businessName); + } + + /** + * 获取上级菜单ID字段 + * + * @param paramsObj 生成其他选项 + * @return 上级菜单ID字段 + */ + public static String getParentMenuId(JSONObject paramsObj) + { + if (StringUtils.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.PARENT_MENU_ID) + && StringUtils.isNotEmpty(paramsObj.getString(GenConstants.PARENT_MENU_ID))) + { + return paramsObj.getString(GenConstants.PARENT_MENU_ID); + } + return DEFAULT_PARENT_MENU_ID; + } + + /** + * 获取树编码 + * + * @param paramsObj 生成其他选项 + * @return 树编码 + */ + public static String getTreecode(JSONObject paramsObj) + { + if (paramsObj.containsKey(GenConstants.TREE_CODE)) + { + return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_CODE)); + } + return StringUtils.EMPTY; + } + + /** + * 获取树父编码 + * + * @param paramsObj 生成其他选项 + * @return 树父编码 + */ + public static String getTreeParentCode(JSONObject paramsObj) + { + if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) + { + return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_PARENT_CODE)); + } + return StringUtils.EMPTY; + } + + /** + * 获取树名称 + * + * @param paramsObj 生成其他选项 + * @return 树名称 + */ + public static String getTreeName(JSONObject paramsObj) + { + if (paramsObj.containsKey(GenConstants.TREE_NAME)) + { + return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_NAME)); + } + return StringUtils.EMPTY; + } + + /** + * 获取需要在哪一列上面显示展开按钮 + * + * @param genTable 业务表对象 + * @return 展开按钮列序号 + */ + public static int getExpandColumn(GenTable genTable) + { + String options = genTable.getOptions(); + JSONObject paramsObj = JSON.parseObject(options); + String treeName = paramsObj.getString(GenConstants.TREE_NAME); + int num = 0; + for (GenTableColumn column : genTable.getColumns()) + { + if (column.isList()) + { + num++; + String columnName = column.getColumnName(); + if (columnName.equals(treeName)) + { + break; + } + } + } + return num; + } +} diff --git a/ruoyi-generator/src/main/resources/generator.yml b/ruoyi-generator/src/main/resources/generator.yml new file mode 100644 index 0000000..7eae68e --- /dev/null +++ b/ruoyi-generator/src/main/resources/generator.yml @@ -0,0 +1,10 @@ +# 代码生成 +gen: + # 作者 + author: ruoyi + # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool + packageName: com.ruoyi.system + # 自动去除表前缀,默认是false + autoRemovePre: false + # 表前缀(生成类名不会包含表前缀,多个用逗号分隔) + tablePrefix: sys_ \ No newline at end of file diff --git a/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml b/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml new file mode 100644 index 0000000..66109de --- /dev/null +++ b/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml @@ -0,0 +1,127 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.generator.mapper.GenTableColumnMapper"> + + <resultMap type="GenTableColumn" id="GenTableColumnResult"> + <id property="columnId" column="column_id" /> + <result property="tableId" column="table_id" /> + <result property="columnName" column="column_name" /> + <result property="columnComment" column="column_comment" /> + <result property="columnType" column="column_type" /> + <result property="javaType" column="java_type" /> + <result property="javaField" column="java_field" /> + <result property="isPk" column="is_pk" /> + <result property="isIncrement" column="is_increment" /> + <result property="isRequired" column="is_required" /> + <result property="isInsert" column="is_insert" /> + <result property="isEdit" column="is_edit" /> + <result property="isList" column="is_list" /> + <result property="isQuery" column="is_query" /> + <result property="queryType" column="query_type" /> + <result property="htmlType" column="html_type" /> + <result property="dictType" column="dict_type" /> + <result property="sort" column="sort" /> + <result property="createBy" column="create_by" /> + <result property="createTime" column="create_time" /> + <result property="updateBy" column="update_by" /> + <result property="updateTime" column="update_time" /> + </resultMap> + + <sql id="selectGenTableColumnVo"> + select column_id, table_id, column_name, column_comment, column_type, java_type, java_field, is_pk, is_increment, is_required, is_insert, is_edit, is_list, is_query, query_type, html_type, dict_type, sort, create_by, create_time, update_by, update_time from gen_table_column + </sql> + + <select id="selectGenTableColumnListByTableId" parameterType="Long" resultMap="GenTableColumnResult"> + <include refid="selectGenTableColumnVo"/> + where table_id = #{tableId} + order by sort + </select> + + <select id="selectDbTableColumnsByName" parameterType="String" resultMap="GenTableColumnResult"> + select column_name, (case when (is_nullable = 'no' <![CDATA[ && ]]> column_key != 'PRI') then '1' else null end) as is_required, (case when column_key = 'PRI' then '1' else '0' end) as is_pk, ordinal_position as sort, column_comment, (case when extra = 'auto_increment' then '1' else '0' end) as is_increment, column_type + from information_schema.columns where table_schema = (select database()) and table_name = (#{tableName}) + order by ordinal_position + </select> + + <insert id="insertGenTableColumn" parameterType="GenTableColumn" useGeneratedKeys="true" keyProperty="columnId"> + insert into gen_table_column ( + <if test="tableId != null and tableId != ''">table_id,</if> + <if test="columnName != null and columnName != ''">column_name,</if> + <if test="columnComment != null and columnComment != ''">column_comment,</if> + <if test="columnType != null and columnType != ''">column_type,</if> + <if test="javaType != null and javaType != ''">java_type,</if> + <if test="javaField != null and javaField != ''">java_field,</if> + <if test="isPk != null and isPk != ''">is_pk,</if> + <if test="isIncrement != null and isIncrement != ''">is_increment,</if> + <if test="isRequired != null and isRequired != ''">is_required,</if> + <if test="isInsert != null and isInsert != ''">is_insert,</if> + <if test="isEdit != null and isEdit != ''">is_edit,</if> + <if test="isList != null and isList != ''">is_list,</if> + <if test="isQuery != null and isQuery != ''">is_query,</if> + <if test="queryType != null and queryType != ''">query_type,</if> + <if test="htmlType != null and htmlType != ''">html_type,</if> + <if test="dictType != null and dictType != ''">dict_type,</if> + <if test="sort != null">sort,</if> + <if test="createBy != null and createBy != ''">create_by,</if> + create_time + )values( + <if test="tableId != null and tableId != ''">#{tableId},</if> + <if test="columnName != null and columnName != ''">#{columnName},</if> + <if test="columnComment != null and columnComment != ''">#{columnComment},</if> + <if test="columnType != null and columnType != ''">#{columnType},</if> + <if test="javaType != null and javaType != ''">#{javaType},</if> + <if test="javaField != null and javaField != ''">#{javaField},</if> + <if test="isPk != null and isPk != ''">#{isPk},</if> + <if test="isIncrement != null and isIncrement != ''">#{isIncrement},</if> + <if test="isRequired != null and isRequired != ''">#{isRequired},</if> + <if test="isInsert != null and isInsert != ''">#{isInsert},</if> + <if test="isEdit != null and isEdit != ''">#{isEdit},</if> + <if test="isList != null and isList != ''">#{isList},</if> + <if test="isQuery != null and isQuery != ''">#{isQuery},</if> + <if test="queryType != null and queryType != ''">#{queryType},</if> + <if test="htmlType != null and htmlType != ''">#{htmlType},</if> + <if test="dictType != null and dictType != ''">#{dictType},</if> + <if test="sort != null">#{sort},</if> + <if test="createBy != null and createBy != ''">#{createBy},</if> + sysdate() + ) + </insert> + + <update id="updateGenTableColumn" parameterType="GenTableColumn"> + update gen_table_column + <set> + <if test="columnComment != null">column_comment = #{columnComment},</if> + <if test="javaType != null">java_type = #{javaType},</if> + <if test="javaField != null">java_field = #{javaField},</if> + <if test="isInsert != null">is_insert = #{isInsert},</if> + <if test="isEdit != null">is_edit = #{isEdit},</if> + <if test="isList != null">is_list = #{isList},</if> + <if test="isQuery != null">is_query = #{isQuery},</if> + <if test="isRequired != null">is_required = #{isRequired},</if> + <if test="queryType != null">query_type = #{queryType},</if> + <if test="htmlType != null">html_type = #{htmlType},</if> + <if test="dictType != null">dict_type = #{dictType},</if> + <if test="sort != null">sort = #{sort},</if> + <if test="updateBy != null">update_by = #{updateBy},</if> + update_time = sysdate() + </set> + where column_id = #{columnId} + </update> + + <delete id="deleteGenTableColumnByIds" parameterType="Long"> + delete from gen_table_column where table_id in + <foreach collection="array" item="tableId" open="(" separator="," close=")"> + #{tableId} + </foreach> + </delete> + + <delete id="deleteGenTableColumns"> + delete from gen_table_column where column_id in + <foreach collection="list" item="item" open="(" separator="," close=")"> + #{item.columnId} + </foreach> + </delete> + +</mapper> \ No newline at end of file diff --git a/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml b/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml new file mode 100644 index 0000000..b605e90 --- /dev/null +++ b/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml @@ -0,0 +1,202 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.generator.mapper.GenTableMapper"> + + <resultMap type="GenTable" id="GenTableResult"> + <id property="tableId" column="table_id" /> + <result property="tableName" column="table_name" /> + <result property="tableComment" column="table_comment" /> + <result property="subTableName" column="sub_table_name" /> + <result property="subTableFkName" column="sub_table_fk_name" /> + <result property="className" column="class_name" /> + <result property="tplCategory" column="tpl_category" /> + <result property="packageName" column="package_name" /> + <result property="moduleName" column="module_name" /> + <result property="businessName" column="business_name" /> + <result property="functionName" column="function_name" /> + <result property="functionAuthor" column="function_author" /> + <result property="genType" column="gen_type" /> + <result property="genPath" column="gen_path" /> + <result property="options" column="options" /> + <result property="createBy" column="create_by" /> + <result property="createTime" column="create_time" /> + <result property="updateBy" column="update_by" /> + <result property="updateTime" column="update_time" /> + <result property="remark" column="remark" /> + <collection property="columns" javaType="java.util.List" resultMap="GenTableColumnResult" /> + </resultMap> + + <resultMap type="GenTableColumn" id="GenTableColumnResult"> + <id property="columnId" column="column_id" /> + <result property="tableId" column="table_id" /> + <result property="columnName" column="column_name" /> + <result property="columnComment" column="column_comment" /> + <result property="columnType" column="column_type" /> + <result property="javaType" column="java_type" /> + <result property="javaField" column="java_field" /> + <result property="isPk" column="is_pk" /> + <result property="isIncrement" column="is_increment" /> + <result property="isRequired" column="is_required" /> + <result property="isInsert" column="is_insert" /> + <result property="isEdit" column="is_edit" /> + <result property="isList" column="is_list" /> + <result property="isQuery" column="is_query" /> + <result property="queryType" column="query_type" /> + <result property="htmlType" column="html_type" /> + <result property="dictType" column="dict_type" /> + <result property="sort" column="sort" /> + <result property="createBy" column="create_by" /> + <result property="createTime" column="create_time" /> + <result property="updateBy" column="update_by" /> + <result property="updateTime" column="update_time" /> + </resultMap> + + <sql id="selectGenTableVo"> + select table_id, table_name, table_comment, sub_table_name, sub_table_fk_name, class_name, tpl_category, package_name, module_name, business_name, function_name, function_author, gen_type, gen_path, options, create_by, create_time, update_by, update_time, remark from gen_table + </sql> + + <select id="selectGenTableList" parameterType="GenTable" resultMap="GenTableResult"> + <include refid="selectGenTableVo"/> + <where> + <if test="tableName != null and tableName != ''"> + AND lower(table_name) like lower(concat('%', #{tableName}, '%')) + </if> + <if test="tableComment != null and tableComment != ''"> + AND lower(table_comment) like lower(concat('%', #{tableComment}, '%')) + </if> + <if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 --> + AND date_format(create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d') + </if> + <if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 --> + AND date_format(create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d') + </if> + </where> + </select> + + <select id="selectDbTableList" parameterType="GenTable" resultMap="GenTableResult"> + select table_name, table_comment, create_time, update_time from information_schema.tables + where table_schema = (select database()) + AND table_name NOT LIKE 'qrtz_%' AND table_name NOT LIKE 'gen_%' + AND table_name NOT IN (select table_name from gen_table) + <if test="tableName != null and tableName != ''"> + AND lower(table_name) like lower(concat('%', #{tableName}, '%')) + </if> + <if test="tableComment != null and tableComment != ''"> + AND lower(table_comment) like lower(concat('%', #{tableComment}, '%')) + </if> + <if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 --> + AND date_format(create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d') + </if> + <if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 --> + AND date_format(create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d') + </if> + order by create_time desc + </select> + + <select id="selectDbTableListByNames" resultMap="GenTableResult"> + select table_name, table_comment, create_time, update_time from information_schema.tables + where table_name NOT LIKE 'qrtz_%' and table_name NOT LIKE 'gen_%' and table_schema = (select database()) + and table_name in + <foreach collection="array" item="name" open="(" separator="," close=")"> + #{name} + </foreach> + </select> + + <select id="selectTableByName" parameterType="String" resultMap="GenTableResult"> + select table_name, table_comment, create_time, update_time from information_schema.tables + where table_comment <![CDATA[ <> ]]> '' and table_schema = (select database()) + and table_name = #{tableName} + </select> + + <select id="selectGenTableById" parameterType="Long" resultMap="GenTableResult"> + SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.gen_type, t.gen_path, t.options, t.remark, + c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort + FROM gen_table t + LEFT JOIN gen_table_column c ON t.table_id = c.table_id + where t.table_id = #{tableId} order by c.sort + </select> + + <select id="selectGenTableByName" parameterType="String" resultMap="GenTableResult"> + SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.gen_type, t.gen_path, t.options, t.remark, + c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort + FROM gen_table t + LEFT JOIN gen_table_column c ON t.table_id = c.table_id + where t.table_name = #{tableName} order by c.sort + </select> + + <select id="selectGenTableAll" parameterType="String" resultMap="GenTableResult"> + SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.options, t.remark, + c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort + FROM gen_table t + LEFT JOIN gen_table_column c ON t.table_id = c.table_id + order by c.sort + </select> + + <insert id="insertGenTable" parameterType="GenTable" useGeneratedKeys="true" keyProperty="tableId"> + insert into gen_table ( + <if test="tableName != null">table_name,</if> + <if test="tableComment != null and tableComment != ''">table_comment,</if> + <if test="className != null and className != ''">class_name,</if> + <if test="tplCategory != null and tplCategory != ''">tpl_category,</if> + <if test="packageName != null and packageName != ''">package_name,</if> + <if test="moduleName != null and moduleName != ''">module_name,</if> + <if test="businessName != null and businessName != ''">business_name,</if> + <if test="functionName != null and functionName != ''">function_name,</if> + <if test="functionAuthor != null and functionAuthor != ''">function_author,</if> + <if test="genType != null and genType != ''">gen_type,</if> + <if test="genPath != null and genPath != ''">gen_path,</if> + <if test="remark != null and remark != ''">remark,</if> + <if test="createBy != null and createBy != ''">create_by,</if> + create_time + )values( + <if test="tableName != null">#{tableName},</if> + <if test="tableComment != null and tableComment != ''">#{tableComment},</if> + <if test="className != null and className != ''">#{className},</if> + <if test="tplCategory != null and tplCategory != ''">#{tplCategory},</if> + <if test="packageName != null and packageName != ''">#{packageName},</if> + <if test="moduleName != null and moduleName != ''">#{moduleName},</if> + <if test="businessName != null and businessName != ''">#{businessName},</if> + <if test="functionName != null and functionName != ''">#{functionName},</if> + <if test="functionAuthor != null and functionAuthor != ''">#{functionAuthor},</if> + <if test="genType != null and genType != ''">#{genType},</if> + <if test="genPath != null and genPath != ''">#{genPath},</if> + <if test="remark != null and remark != ''">#{remark},</if> + <if test="createBy != null and createBy != ''">#{createBy},</if> + sysdate() + ) + </insert> + + <update id="updateGenTable" parameterType="GenTable"> + update gen_table + <set> + <if test="tableName != null">table_name = #{tableName},</if> + <if test="tableComment != null and tableComment != ''">table_comment = #{tableComment},</if> + <if test="subTableName != null">sub_table_name = #{subTableName},</if> + <if test="subTableFkName != null">sub_table_fk_name = #{subTableFkName},</if> + <if test="className != null and className != ''">class_name = #{className},</if> + <if test="functionAuthor != null and functionAuthor != ''">function_author = #{functionAuthor},</if> + <if test="genType != null and genType != ''">gen_type = #{genType},</if> + <if test="genPath != null and genPath != ''">gen_path = #{genPath},</if> + <if test="tplCategory != null and tplCategory != ''">tpl_category = #{tplCategory},</if> + <if test="packageName != null and packageName != ''">package_name = #{packageName},</if> + <if test="moduleName != null and moduleName != ''">module_name = #{moduleName},</if> + <if test="businessName != null and businessName != ''">business_name = #{businessName},</if> + <if test="functionName != null and functionName != ''">function_name = #{functionName},</if> + <if test="options != null and options != ''">options = #{options},</if> + <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if> + <if test="remark != null">remark = #{remark},</if> + update_time = sysdate() + </set> + where table_id = #{tableId} + </update> + + <delete id="deleteGenTableByIds" parameterType="Long"> + delete from gen_table where table_id in + <foreach collection="array" item="tableId" open="(" separator="," close=")"> + #{tableId} + </foreach> + </delete> + +</mapper> \ No newline at end of file diff --git a/ruoyi-generator/src/main/resources/vm/java/controller.java.vm b/ruoyi-generator/src/main/resources/vm/java/controller.java.vm new file mode 100644 index 0000000..bf88988 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/java/controller.java.vm @@ -0,0 +1,115 @@ +package ${packageName}.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import ${packageName}.domain.${ClassName}; +import ${packageName}.service.I${ClassName}Service; +import com.ruoyi.common.utils.poi.ExcelUtil; +#if($table.crud || $table.sub) +import com.ruoyi.common.core.page.TableDataInfo; +#elseif($table.tree) +#end + +/** + * ${functionName}Controller + * + * @author ${author} + * @date ${datetime} + */ +@RestController +@RequestMapping("/${moduleName}/${businessName}") +public class ${ClassName}Controller extends BaseController +{ + @Autowired + private I${ClassName}Service ${className}Service; + + /** + * 查询${functionName}列表 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')") + @GetMapping("/list") +#if($table.crud || $table.sub) + public TableDataInfo list(${ClassName} ${className}) + { + startPage(); + List<${ClassName}> list = ${className}Service.select${ClassName}List(${className}); + return getDataTable(list); + } +#elseif($table.tree) + public AjaxResult list(${ClassName} ${className}) + { + List<${ClassName}> list = ${className}Service.select${ClassName}List(${className}); + return success(list); + } +#end + + /** + * 导出${functionName}列表 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:export')") + @Log(title = "${functionName}", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, ${ClassName} ${className}) + { + List<${ClassName}> list = ${className}Service.select${ClassName}List(${className}); + ExcelUtil<${ClassName}> util = new ExcelUtil<${ClassName}>(${ClassName}.class); + util.exportExcel(response, list, "${functionName}数据"); + } + + /** + * 获取${functionName}详细信息 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:query')") + @GetMapping(value = "/{${pkColumn.javaField}}") + public AjaxResult getInfo(@PathVariable("${pkColumn.javaField}") ${pkColumn.javaType} ${pkColumn.javaField}) + { + return success(${className}Service.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField})); + } + + /** + * 新增${functionName} + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:add')") + @Log(title = "${functionName}", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody ${ClassName} ${className}) + { + return toAjax(${className}Service.insert${ClassName}(${className})); + } + + /** + * 修改${functionName} + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:edit')") + @Log(title = "${functionName}", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody ${ClassName} ${className}) + { + return toAjax(${className}Service.update${ClassName}(${className})); + } + + /** + * 删除${functionName} + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:remove')") + @Log(title = "${functionName}", businessType = BusinessType.DELETE) + @DeleteMapping("/{${pkColumn.javaField}s}") + public AjaxResult remove(@PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s) + { + return toAjax(${className}Service.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s)); + } +} diff --git a/ruoyi-generator/src/main/resources/vm/java/domain.java.vm b/ruoyi-generator/src/main/resources/vm/java/domain.java.vm new file mode 100644 index 0000000..bd51c17 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/java/domain.java.vm @@ -0,0 +1,105 @@ +package ${packageName}.domain; + +#foreach ($import in $importList) +import ${import}; +#end +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +#if($table.crud || $table.sub) +import com.ruoyi.common.core.domain.BaseEntity; +#elseif($table.tree) +import com.ruoyi.common.core.domain.TreeEntity; +#end + +/** + * ${functionName}对象 ${tableName} + * + * @author ${author} + * @date ${datetime} + */ +#if($table.crud || $table.sub) +#set($Entity="BaseEntity") +#elseif($table.tree) +#set($Entity="TreeEntity") +#end +public class ${ClassName} extends ${Entity} +{ + private static final long serialVersionUID = 1L; + +#foreach ($column in $columns) +#if(!$table.isSuperColumn($column.javaField)) + /** $column.columnComment */ +#if($column.list) +#set($parentheseIndex=$column.columnComment.indexOf("(")) +#if($parentheseIndex != -1) +#set($comment=$column.columnComment.substring(0, $parentheseIndex)) +#else +#set($comment=$column.columnComment) +#end +#if($parentheseIndex != -1) + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") +#elseif($column.javaType == 'Date') + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") +#else + @Excel(name = "${comment}") +#end +#end + private $column.javaType $column.javaField; + +#end +#end +#if($table.sub) + /** $table.subTable.functionName信息 */ + private List<${subClassName}> ${subclassName}List; + +#end +#foreach ($column in $columns) +#if(!$table.isSuperColumn($column.javaField)) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + public void set${AttrName}($column.javaType $column.javaField) + { + this.$column.javaField = $column.javaField; + } + + public $column.javaType get${AttrName}() + { + return $column.javaField; + } +#end +#end + +#if($table.sub) + public List<${subClassName}> get${subClassName}List() + { + return ${subclassName}List; + } + + public void set${subClassName}List(List<${subClassName}> ${subclassName}List) + { + this.${subclassName}List = ${subclassName}List; + } + +#end + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) +#foreach ($column in $columns) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + .append("${column.javaField}", get${AttrName}()) +#end +#if($table.sub) + .append("${subclassName}List", get${subClassName}List()) +#end + .toString(); + } +} diff --git a/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm b/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm new file mode 100644 index 0000000..7e7d7c2 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm @@ -0,0 +1,91 @@ +package ${packageName}.mapper; + +import java.util.List; +import ${packageName}.domain.${ClassName}; +#if($table.sub) +import ${packageName}.domain.${subClassName}; +#end + +/** + * ${functionName}Mapper接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface ${ClassName}Mapper +{ + /** + * 查询${functionName} + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return ${functionName} + */ + public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); + + /** + * 查询${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName}集合 + */ + public List<${ClassName}> select${ClassName}List(${ClassName} ${className}); + + /** + * 新增${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int insert${ClassName}(${ClassName} ${className}); + + /** + * 修改${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int update${ClassName}(${ClassName} ${className}); + + /** + * 删除${functionName} + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return 结果 + */ + public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); + + /** + * 批量删除${functionName} + * + * @param ${pkColumn.javaField}s 需要删除的数据主键集合 + * @return 结果 + */ + public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s); +#if($table.sub) + + /** + * 批量删除${subTable.functionName} + * + * @param ${pkColumn.javaField}s 需要删除的数据主键集合 + * @return 结果 + */ + public int delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaType}[] ${pkColumn.javaField}s); + + /** + * 批量新增${subTable.functionName} + * + * @param ${subclassName}List ${subTable.functionName}列表 + * @return 结果 + */ + public int batch${subClassName}(List<${subClassName}> ${subclassName}List); + + + /** + * 通过${functionName}主键删除${subTable.functionName}信息 + * + * @param ${pkColumn.javaField} ${functionName}ID + * @return 结果 + */ + public int delete${subClassName}By${subTableFkClassName}(${pkColumn.javaType} ${pkColumn.javaField}); +#end +} diff --git a/ruoyi-generator/src/main/resources/vm/java/service.java.vm b/ruoyi-generator/src/main/resources/vm/java/service.java.vm new file mode 100644 index 0000000..264882b --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/java/service.java.vm @@ -0,0 +1,61 @@ +package ${packageName}.service; + +import java.util.List; +import ${packageName}.domain.${ClassName}; + +/** + * ${functionName}Service接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface I${ClassName}Service +{ + /** + * 查询${functionName} + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return ${functionName} + */ + public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); + + /** + * 查询${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName}集合 + */ + public List<${ClassName}> select${ClassName}List(${ClassName} ${className}); + + /** + * 新增${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int insert${ClassName}(${ClassName} ${className}); + + /** + * 修改${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int update${ClassName}(${ClassName} ${className}); + + /** + * 批量删除${functionName} + * + * @param ${pkColumn.javaField}s 需要删除的${functionName}主键集合 + * @return 结果 + */ + public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s); + + /** + * 删除${functionName}信息 + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return 结果 + */ + public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); +} diff --git a/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm b/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm new file mode 100644 index 0000000..14746e1 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm @@ -0,0 +1,169 @@ +package ${packageName}.service.impl; + +import java.util.List; +#foreach ($column in $columns) +#if($column.javaField == 'createTime' || $column.javaField == 'updateTime') +import com.ruoyi.common.utils.DateUtils; +#break +#end +#end +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +#if($table.sub) +import java.util.ArrayList; +import com.ruoyi.common.utils.StringUtils; +import org.springframework.transaction.annotation.Transactional; +import ${packageName}.domain.${subClassName}; +#end +import ${packageName}.mapper.${ClassName}Mapper; +import ${packageName}.domain.${ClassName}; +import ${packageName}.service.I${ClassName}Service; + +/** + * ${functionName}Service业务层处理 + * + * @author ${author} + * @date ${datetime} + */ +@Service +public class ${ClassName}ServiceImpl implements I${ClassName}Service +{ + @Autowired + private ${ClassName}Mapper ${className}Mapper; + + /** + * 查询${functionName} + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return ${functionName} + */ + @Override + public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}) + { + return ${className}Mapper.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField}); + } + + /** + * 查询${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName} + */ + @Override + public List<${ClassName}> select${ClassName}List(${ClassName} ${className}) + { + return ${className}Mapper.select${ClassName}List(${className}); + } + + /** + * 新增${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int insert${ClassName}(${ClassName} ${className}) + { +#foreach ($column in $columns) +#if($column.javaField == 'createTime') + ${className}.setCreateTime(DateUtils.getNowDate()); +#end +#end +#if($table.sub) + int rows = ${className}Mapper.insert${ClassName}(${className}); + insert${subClassName}(${className}); + return rows; +#else + return ${className}Mapper.insert${ClassName}(${className}); +#end + } + + /** + * 修改${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int update${ClassName}(${ClassName} ${className}) + { +#foreach ($column in $columns) +#if($column.javaField == 'updateTime') + ${className}.setUpdateTime(DateUtils.getNowDate()); +#end +#end +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}(${className}.get${pkColumn.capJavaField}()); + insert${subClassName}(${className}); +#end + return ${className}Mapper.update${ClassName}(${className}); + } + + /** + * 批量删除${functionName} + * + * @param ${pkColumn.javaField}s 需要删除的${functionName}主键 + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s) + { +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaField}s); +#end + return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s); + } + + /** + * 删除${functionName}信息 + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}) + { +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}(${pkColumn.javaField}); +#end + return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField}); + } +#if($table.sub) + + /** + * 新增${subTable.functionName}信息 + * + * @param ${className} ${functionName}对象 + */ + public void insert${subClassName}(${ClassName} ${className}) + { + List<${subClassName}> ${subclassName}List = ${className}.get${subClassName}List(); + ${pkColumn.javaType} ${pkColumn.javaField} = ${className}.get${pkColumn.capJavaField}(); + if (StringUtils.isNotNull(${subclassName}List)) + { + List<${subClassName}> list = new ArrayList<${subClassName}>(); + for (${subClassName} ${subclassName} : ${subclassName}List) + { + ${subclassName}.set${subTableFkClassName}(${pkColumn.javaField}); + list.add(${subclassName}); + } + if (list.size() > 0) + { + ${className}Mapper.batch${subClassName}(list); + } + } + } +#end +} diff --git a/ruoyi-generator/src/main/resources/vm/java/sub-domain.java.vm b/ruoyi-generator/src/main/resources/vm/java/sub-domain.java.vm new file mode 100644 index 0000000..a3f53eb --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/java/sub-domain.java.vm @@ -0,0 +1,76 @@ +package ${packageName}.domain; + +#foreach ($import in $subImportList) +import ${import}; +#end +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * ${subTable.functionName}对象 ${subTableName} + * + * @author ${author} + * @date ${datetime} + */ +public class ${subClassName} extends BaseEntity +{ + private static final long serialVersionUID = 1L; + +#foreach ($column in $subTable.columns) +#if(!$table.isSuperColumn($column.javaField)) + /** $column.columnComment */ +#if($column.list) +#set($parentheseIndex=$column.columnComment.indexOf("(")) +#if($parentheseIndex != -1) +#set($comment=$column.columnComment.substring(0, $parentheseIndex)) +#else +#set($comment=$column.columnComment) +#end +#if($parentheseIndex != -1) + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") +#elseif($column.javaType == 'Date') + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") +#else + @Excel(name = "${comment}") +#end +#end + private $column.javaType $column.javaField; + +#end +#end +#foreach ($column in $subTable.columns) +#if(!$table.isSuperColumn($column.javaField)) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + public void set${AttrName}($column.javaType $column.javaField) + { + this.$column.javaField = $column.javaField; + } + + public $column.javaType get${AttrName}() + { + return $column.javaField; + } +#end +#end + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) +#foreach ($column in $subTable.columns) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + .append("${column.javaField}", get${AttrName}()) +#end + .toString(); + } +} diff --git a/ruoyi-generator/src/main/resources/vm/js/api.js.vm b/ruoyi-generator/src/main/resources/vm/js/api.js.vm new file mode 100644 index 0000000..9295524 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/js/api.js.vm @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询${functionName}列表 +export function list${BusinessName}(query) { + return request({ + url: '/${moduleName}/${businessName}/list', + method: 'get', + params: query + }) +} + +// 查询${functionName}详细 +export function get${BusinessName}(${pkColumn.javaField}) { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'get' + }) +} + +// 新增${functionName} +export function add${BusinessName}(data) { + return request({ + url: '/${moduleName}/${businessName}', + method: 'post', + data: data + }) +} + +// 修改${functionName} +export function update${BusinessName}(data) { + return request({ + url: '/${moduleName}/${businessName}', + method: 'put', + data: data + }) +} + +// 删除${functionName} +export function del${BusinessName}(${pkColumn.javaField}) { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'delete' + }) +} diff --git a/ruoyi-generator/src/main/resources/vm/sql/sql.vm b/ruoyi-generator/src/main/resources/vm/sql/sql.vm new file mode 100644 index 0000000..0575583 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/sql/sql.vm @@ -0,0 +1,22 @@ +-- 菜单 SQL +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', sysdate(), '', null, '${functionName}菜单'); + +-- 按钮父菜单ID +SELECT @parentId := LAST_INSERT_ID(); + +-- 按钮 SQL +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}查询', @parentId, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}新增', @parentId, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}修改', @parentId, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}删除', @parentId, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}导出', @parentId, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 'admin', sysdate(), '', null, ''); \ No newline at end of file diff --git a/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm new file mode 100644 index 0000000..4819c2a --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm @@ -0,0 +1,505 @@ +<template> + <div class="app-container"> + <el-form :model="queryParams" ref="queryForm" size="small" :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 + @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> + <el-option + v-for="dict in dict.type.${dictType}" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </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> + <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 + 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}" + 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> + <el-col :span="1.5"> + <el-button + type="info" + plain + icon="el-icon-sort" + size="mini" + @click="toggleExpandAll" + >展开/折叠</el-button> + </el-col> + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + + <el-table + v-if="refreshTable" + v-loading="loading" + :data="${businessName}List" + row-key="${treeCode}" + :default-expand-all="isExpandAll" + :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.htmlType == "imageUpload") + <el-table-column label="${comment}" align="center" prop="${javaField}" width="100"> + <template slot-scope="scope"> + <image-preview :src="scope.row.${javaField}" :width="50" :height="50"/> + </template> + </el-table-column> +#elseif($column.list && "" != $column.dictType) + <el-table-column label="${comment}" align="center" prop="${javaField}"> + <template slot-scope="scope"> +#if($column.htmlType == "checkbox") + <dict-tag :options="dict.type.${column.dictType}" :value="scope.row.${javaField} ? scope.row.${javaField}.split(',') : []"/> +#else + <dict-tag :options="dict.type.${column.dictType}" :value="scope.row.${javaField}"/> +#end + </template> + </el-table-column> +#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}" prop="${field}"> + <image-upload v-model="form.${field}"/> + </el-form-item> +#elseif($column.htmlType == "fileUpload") + <el-form-item label="${comment}" prop="${field}"> + <file-upload 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 dict.type.${dictType}" + :key="dict.value" + :label="dict.label" +#if($column.javaType == "Integer" || $column.javaType == "Long") + :value="parseInt(dict.value)" +#else + :value="dict.value" +#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}" prop="${field}"> + <el-checkbox-group v-model="form.${field}"> + <el-checkbox + v-for="dict in dict.type.${dictType}" + :key="dict.value" + :label="dict.value"> + {{dict.label}} + </el-checkbox> + </el-checkbox-group> + </el-form-item> +#elseif($column.htmlType == "checkbox" && $dictType) + <el-form-item label="${comment}" prop="${field}"> + <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}" prop="${field}"> + <el-radio-group v-model="form.${field}"> + <el-radio + v-for="dict in dict.type.${dictType}" + :key="dict.value" +#if($column.javaType == "Integer" || $column.javaType == "Long") + :label="parseInt(dict.value)" +#else + :label="dict.value" +#end + >{{dict.label}}</el-radio> + </el-radio-group> + </el-form-item> +#elseif($column.htmlType == "radio" && $dictType) + <el-form-item label="${comment}" prop="${field}"> + <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 + v-model="form.${field}" + type="date" + value-format="yyyy-MM-dd" + 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 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} } from "@/api/${moduleName}/${businessName}"; +import Treeselect from "@riophae/vue-treeselect"; +import "@riophae/vue-treeselect/dist/vue-treeselect.css"; + +export default { + name: "${BusinessName}", +#if(${dicts} != '') + dicts: [${dicts}], +#end + components: { + Treeselect + }, + data() { + return { + // 遮罩层 + loading: true, + // 显示搜索条件 + showSearch: true, + // ${functionName}表格数据 + ${businessName}List: [], + // ${functionName}树选项 + ${businessName}Options: [], + // 弹出层标题 + title: "", + // 是否显示弹出层 + open: false, + // 是否展开,默认全部展开 + isExpandAll: true, + // 重新渲染表格状态 + refreshTable: true, +#foreach ($column in $columns) +#if($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($foreach.count != $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" || $column.htmlType == "radio")"change"#else"blur"#end } + ]#if($foreach.count != $columns.size()),#end +#end +#end + } + }; + }, + created() { + this.getList(); + }, + 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); + }); + }, + // 取消按钮 + cancel() { + this.open = false; + this.reset(); + }, + // 表单重置 + reset() { + this.form = { +#foreach ($column in $columns) +#if($column.htmlType == "checkbox") + $column.javaField: []#if($foreach.count != $columns.size()),#end +#else + $column.javaField: null#if($foreach.count != $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}"; + }, + /** 展开/折叠操作 */ + toggleExpandAll() { + this.refreshTable = false; + this.isExpandAll = !this.isExpandAll; + this.$nextTick(() => { + this.refreshTable = true; + }); + }, + /** 修改按钮操作 */ + handleUpdate(row) { + this.reset(); + this.getTreeselect(); + if (row != null) { + this.form.${treeParentCode} = row.${treeParentCode}; + } + get${BusinessName}(row.${pkColumn.javaField}).then(response => { + 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) { +#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.#[[$modal]]#.msgSuccess("修改成功"); + this.open = false; + this.getList(); + }); + } else { + add${BusinessName}(this.form).then(response => { + this.#[[$modal]]#.msgSuccess("新增成功"); + this.open = false; + this.getList(); + }); + } + } + }); + }, + /** 删除按钮操作 */ + handleDelete(row) { + this.#[[$modal]]#.confirm('是否确认删除${functionName}编号为"' + row.${pkColumn.javaField} + '"的数据项?').then(function() { + return del${BusinessName}(row.${pkColumn.javaField}); + }).then(() => { + this.getList(); + this.#[[$modal]]#.msgSuccess("删除成功"); + }).catch(() => {}); + } + } +}; +</script> diff --git a/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm new file mode 100644 index 0000000..6296014 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm @@ -0,0 +1,602 @@ +<template> + <div class="app-container"> + <el-form :model="queryParams" ref="queryForm" size="small" :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 + @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> + <el-option + v-for="dict in dict.type.${dictType}" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </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> + <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 + 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}" + 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> + <el-col :span="1.5"> + <el-button + type="success" + plain + icon="el-icon-edit" + size="mini" + :disabled="single" + @click="handleUpdate" + v-hasPermi="['${moduleName}:${businessName}:edit']" + >修改</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="danger" + plain + icon="el-icon-delete" + size="mini" + :disabled="multiple" + @click="handleDelete" + v-hasPermi="['${moduleName}:${businessName}:remove']" + >删除</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="warning" + plain + icon="el-icon-download" + size="mini" + @click="handleExport" + v-hasPermi="['${moduleName}:${businessName}:export']" + >导出</el-button> + </el-col> + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + + <el-table v-loading="loading" :data="${businessName}List" @selection-change="handleSelectionChange"> + <el-table-column type="selection" width="55" align="center" /> +#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) + <el-table-column label="${comment}" align="center" prop="${javaField}" /> +#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.htmlType == "imageUpload") + <el-table-column label="${comment}" align="center" prop="${javaField}" width="100"> + <template slot-scope="scope"> + <image-preview :src="scope.row.${javaField}" :width="50" :height="50"/> + </template> + </el-table-column> +#elseif($column.list && "" != $column.dictType) + <el-table-column label="${comment}" align="center" prop="${javaField}"> + <template slot-scope="scope"> +#if($column.htmlType == "checkbox") + <dict-tag :options="dict.type.${column.dictType}" :value="scope.row.${javaField} ? scope.row.${javaField}.split(',') : []"/> +#else + <dict-tag :options="dict.type.${column.dictType}" :value="scope.row.${javaField}"/> +#end + </template> + </el-table-column> +#elseif($column.list && "" != $javaField) + <el-table-column label="${comment}" align="center" prop="${javaField}" /> +#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-delete" + @click="handleDelete(scope.row)" + v-hasPermi="['${moduleName}:${businessName}:remove']" + >删除</el-button> + </template> + </el-table-column> + </el-table> + + <pagination + v-show="total>0" + :total="total" + :page.sync="queryParams.pageNum" + :limit.sync="queryParams.pageSize" + @pagination="getList" + /> + + <!-- 添加或修改${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($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}" prop="${field}"> + <image-upload v-model="form.${field}"/> + </el-form-item> +#elseif($column.htmlType == "fileUpload") + <el-form-item label="${comment}" prop="${field}"> + <file-upload 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 dict.type.${dictType}" + :key="dict.value" + :label="dict.label" +#if($column.javaType == "Integer" || $column.javaType == "Long") + :value="parseInt(dict.value)" +#else + :value="dict.value" +#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}" prop="${field}"> + <el-checkbox-group v-model="form.${field}"> + <el-checkbox + v-for="dict in dict.type.${dictType}" + :key="dict.value" + :label="dict.value"> + {{dict.label}} + </el-checkbox> + </el-checkbox-group> + </el-form-item> +#elseif($column.htmlType == "checkbox" && $dictType) + <el-form-item label="${comment}" prop="${field}"> + <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}" prop="${field}"> + <el-radio-group v-model="form.${field}"> + <el-radio + v-for="dict in dict.type.${dictType}" + :key="dict.value" +#if($column.javaType == "Integer" || $column.javaType == "Long") + :label="parseInt(dict.value)" +#else + :label="dict.value" +#end + >{{dict.label}}</el-radio> + </el-radio-group> + </el-form-item> +#elseif($column.htmlType == "radio" && $dictType) + <el-form-item label="${comment}" prop="${field}"> + <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 + v-model="form.${field}" + type="date" + value-format="yyyy-MM-dd" + 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 +#if($table.sub) + <el-divider content-position="center">${subTable.functionName}信息</el-divider> + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAdd${subClassName}">添加</el-button> + </el-col> + <el-col :span="1.5"> + <el-button type="danger" icon="el-icon-delete" size="mini" @click="handleDelete${subClassName}">删除</el-button> + </el-col> + </el-row> + <el-table :data="${subclassName}List" :row-class-name="row${subClassName}Index" @selection-change="handle${subClassName}SelectionChange" ref="${subclassName}"> + <el-table-column type="selection" width="50" align="center" /> + <el-table-column label="序号" align="center" prop="index" width="50"/> +#foreach($column in $subTable.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 || $javaField == ${subTableFkclassName}) +#elseif($column.list && $column.htmlType == "input") + <el-table-column label="$comment" prop="${javaField}" width="150"> + <template slot-scope="scope"> + <el-input v-model="scope.row.$javaField" placeholder="请输入$comment" /> + </template> + </el-table-column> +#elseif($column.list && $column.htmlType == "datetime") + <el-table-column label="$comment" prop="${javaField}" width="240"> + <template slot-scope="scope"> + <el-date-picker clearable v-model="scope.row.$javaField" type="date" value-format="yyyy-MM-dd" placeholder="请选择$comment" /> + </template> + </el-table-column> +#elseif($column.list && ($column.htmlType == "select" || $column.htmlType == "radio") && "" != $column.dictType) + <el-table-column label="$comment" prop="${javaField}" width="150"> + <template slot-scope="scope"> + <el-select v-model="scope.row.$javaField" placeholder="请选择$comment"> + <el-option + v-for="dict in dict.type.$column.dictType" + :key="dict.value" + :label="dict.label" + :value="dict.value" + ></el-option> + </el-select> + </template> + </el-table-column> +#elseif($column.list && ($column.htmlType == "select" || $column.htmlType == "radio") && "" == $column.dictType) + <el-table-column label="$comment" prop="${javaField}" width="150"> + <template slot-scope="scope"> + <el-select v-model="scope.row.$javaField" placeholder="请选择$comment"> + <el-option label="请选择字典生成" value="" /> + </el-select> + </template> + </el-table-column> +#end +#end + </el-table> +#end + </el-form> + <div slot="footer" class="dialog-footer"> + <el-button 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} } from "@/api/${moduleName}/${businessName}"; + +export default { + name: "${BusinessName}", +#if(${dicts} != '') + dicts: [${dicts}], +#end + data() { + return { + // 遮罩层 + loading: true, + // 选中数组 + ids: [], +#if($table.sub) + // 子表选中数据 + checked${subClassName}: [], +#end + // 非单个禁用 + single: true, + // 非多个禁用 + multiple: true, + // 显示搜索条件 + showSearch: true, + // 总条数 + total: 0, + // ${functionName}表格数据 + ${businessName}List: [], +#if($table.sub) + // ${subTable.functionName}表格数据 + ${subclassName}List: [], +#end + // 弹出层标题 + title: "", + // 是否显示弹出层 + open: false, +#foreach ($column in $columns) +#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN") +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) + // $comment时间范围 + daterange${AttrName}: [], +#end +#end + // 查询参数 + queryParams: { + pageNum: 1, + pageSize: 10, +#foreach ($column in $columns) +#if($column.query) + $column.javaField: null#if($foreach.count != $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" || $column.htmlType == "radio")"change"#else"blur"#end } + ]#if($foreach.count != $columns.size()),#end +#end +#end + } + }; + }, + created() { + this.getList(); + }, + 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 = response.rows; + this.total = response.total; + this.loading = false; + }); + }, + // 取消按钮 + cancel() { + this.open = false; + this.reset(); + }, + // 表单重置 + reset() { + this.form = { +#foreach ($column in $columns) +#if($column.htmlType == "checkbox") + $column.javaField: []#if($foreach.count != $columns.size()),#end +#else + $column.javaField: null#if($foreach.count != $columns.size()),#end +#end +#end + }; +#if($table.sub) + this.${subclassName}List = []; +#end + this.resetForm("form"); + }, + /** 搜索按钮操作 */ + handleQuery() { + this.queryParams.pageNum = 1; + 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(); + }, + // 多选框选中数据 + handleSelectionChange(selection) { + this.ids = selection.map(item => item.${pkColumn.javaField}) + this.single = selection.length!==1 + this.multiple = !selection.length + }, + /** 新增按钮操作 */ + handleAdd() { + this.reset(); + this.open = true; + this.title = "添加${functionName}"; + }, + /** 修改按钮操作 */ + handleUpdate(row) { + this.reset(); + const ${pkColumn.javaField} = row.${pkColumn.javaField} || this.ids + get${BusinessName}(${pkColumn.javaField}).then(response => { + this.form = response.data; +#foreach ($column in $columns) +#if($column.htmlType == "checkbox") + this.form.$column.javaField = this.form.${column.javaField}.split(","); +#end +#end +#if($table.sub) + this.${subclassName}List = response.data.${subclassName}List; +#end + this.open = true; + this.title = "修改${functionName}"; + }); + }, + /** 提交按钮 */ + submitForm() { + this.#[[$]]#refs["form"].validate(valid => { + if (valid) { +#foreach ($column in $columns) +#if($column.htmlType == "checkbox") + this.form.$column.javaField = this.form.${column.javaField}.join(","); +#end +#end +#if($table.sub) + this.form.${subclassName}List = this.${subclassName}List; +#end + if (this.form.${pkColumn.javaField} != null) { + update${BusinessName}(this.form).then(response => { + this.#[[$modal]]#.msgSuccess("修改成功"); + this.open = false; + this.getList(); + }); + } else { + add${BusinessName}(this.form).then(response => { + this.#[[$modal]]#.msgSuccess("新增成功"); + this.open = false; + this.getList(); + }); + } + } + }); + }, + /** 删除按钮操作 */ + handleDelete(row) { + const ${pkColumn.javaField}s = row.${pkColumn.javaField} || this.ids; + this.#[[$modal]]#.confirm('是否确认删除${functionName}编号为"' + ${pkColumn.javaField}s + '"的数据项?').then(function() { + return del${BusinessName}(${pkColumn.javaField}s); + }).then(() => { + this.getList(); + this.#[[$modal]]#.msgSuccess("删除成功"); + }).catch(() => {}); + }, +#if($table.sub) + /** ${subTable.functionName}序号 */ + row${subClassName}Index({ row, rowIndex }) { + row.index = rowIndex + 1; + }, + /** ${subTable.functionName}添加按钮操作 */ + handleAdd${subClassName}() { + let obj = {}; +#foreach($column in $subTable.columns) +#if($column.pk || $column.javaField == ${subTableFkclassName}) +#elseif($column.list && "" != $javaField) + obj.$column.javaField = ""; +#end +#end + this.${subclassName}List.push(obj); + }, + /** ${subTable.functionName}删除按钮操作 */ + handleDelete${subClassName}() { + if (this.checked${subClassName}.length == 0) { + this.#[[$modal]]#.msgError("请先选择要删除的${subTable.functionName}数据"); + } else { + const ${subclassName}List = this.${subclassName}List; + const checked${subClassName} = this.checked${subClassName}; + this.${subclassName}List = ${subclassName}List.filter(function(item) { + return checked${subClassName}.indexOf(item.index) == -1 + }); + } + }, + /** 复选框选中数据 */ + handle${subClassName}SelectionChange(selection) { + this.checked${subClassName} = selection.map(item => item.index) + }, +#end + /** 导出按钮操作 */ + handleExport() { + this.download('${moduleName}/${businessName}/export', { + ...this.queryParams + }, `${businessName}_#[[${new Date().getTime()}]]#.xlsx`) + } + } +}; +</script> diff --git a/ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm new file mode 100644 index 0000000..c54d62b --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm @@ -0,0 +1,474 @@ +<template> + <div class="app-container"> + <el-form :model="queryParams" ref="queryRef" :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 + @keyup.enter="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> + <el-option + v-for="dict in ${dictType}" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </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> + <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 + 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}" style="width: 308px"> + <el-date-picker + v-model="daterange${AttrName}" + 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="Search" @click="handleQuery">搜索</el-button> + <el-button icon="Refresh" @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="Plus" + @click="handleAdd" + v-hasPermi="['${moduleName}:${businessName}:add']" + >新增</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="info" + plain + icon="Sort" + @click="toggleExpandAll" + >展开/折叠</el-button> + </el-col> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + + <el-table + v-if="refreshTable" + v-loading="loading" + :data="${businessName}List" + row-key="${treeCode}" + :default-expand-all="isExpandAll" + :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 #default="scope"> + <span>{{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}</span> + </template> + </el-table-column> +#elseif($column.list && $column.htmlType == "imageUpload") + <el-table-column label="${comment}" align="center" prop="${javaField}" width="100"> + <template #default="scope"> + <image-preview :src="scope.row.${javaField}" :width="50" :height="50"/> + </template> + </el-table-column> +#elseif($column.list && "" != $column.dictType) + <el-table-column label="${comment}" align="center" prop="${javaField}"> + <template #default="scope"> +#if($column.htmlType == "checkbox") + <dict-tag :options="${column.dictType}" :value="scope.row.${javaField} ? scope.row.${javaField}.split(',') : []"/> +#else + <dict-tag :options="${column.dictType}" :value="scope.row.${javaField}"/> +#end + </template> + </el-table-column> +#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 #default="scope"> + <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['${moduleName}:${businessName}:edit']">修改</el-button> + <el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['${moduleName}:${businessName}:add']">新增</el-button> + <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['${moduleName}:${businessName}:remove']">删除</el-button> + </template> + </el-table-column> + </el-table> + + <!-- 添加或修改${functionName}对话框 --> + <el-dialog :title="title" v-model="open" width="500px" append-to-body> + <el-form ref="${businessName}Ref" :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}"> + <el-tree-select + v-model="form.${treeParentCode}" + :data="${businessName}Options" + :props="{ value: '${treeCode}', label: '${treeName}', children: 'children' }" + value-key="${treeCode}" + placeholder="请选择${comment}" + check-strictly + /> + </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}" prop="${field}"> + <image-upload v-model="form.${field}"/> + </el-form-item> +#elseif($column.htmlType == "fileUpload") + <el-form-item label="${comment}" prop="${field}"> + <file-upload 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 ${dictType}" + :key="dict.value" + :label="dict.label" +#if($column.javaType == "Integer" || $column.javaType == "Long") + :value="parseInt(dict.value)" +#else + :value="dict.value" +#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}" prop="${field}"> + <el-checkbox-group v-model="form.${field}"> + <el-checkbox + v-for="dict in ${dictType}" + :key="dict.value" + :label="dict.value"> + {{dict.label}} + </el-checkbox> + </el-checkbox-group> + </el-form-item> +#elseif($column.htmlType == "checkbox" && $dictType) + <el-form-item label="${comment}" prop="${field}"> + <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}" prop="${field}"> + <el-radio-group v-model="form.${field}"> + <el-radio + v-for="dict in ${dictType}" + :key="dict.value" +#if($column.javaType == "Integer" || $column.javaType == "Long") + :label="parseInt(dict.value)" +#else + :label="dict.value" +#end + >{{dict.label}}</el-radio> + </el-radio-group> + </el-form-item> +#elseif($column.htmlType == "radio" && $dictType) + <el-form-item label="${comment}" prop="${field}"> + <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 + v-model="form.${field}" + type="date" + value-format="YYYY-MM-DD" + 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> + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="submitForm">确 定</el-button> + <el-button @click="cancel">取 消</el-button> + </div> + </template> + </el-dialog> + </div> +</template> + +<script setup name="${BusinessName}"> +import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName} } from "@/api/${moduleName}/${businessName}"; + +const { proxy } = getCurrentInstance(); +#if(${dicts} != '') +#set($dictsNoSymbol=$dicts.replace("'", "")) +const { ${dictsNoSymbol} } = proxy.useDict(${dicts}); +#end + +const ${businessName}List = ref([]); +const ${businessName}Options = ref([]); +const open = ref(false); +const loading = ref(true); +const showSearch = ref(true); +const title = ref(""); +const isExpandAll = ref(true); +const refreshTable = ref(true); +#foreach ($column in $columns) +#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN") +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +const daterange${AttrName} = ref([]); +#end +#end + +const data = reactive({ + form: {}, + queryParams: { + #foreach ($column in $columns) +#if($column.query) + $column.javaField: null#if($foreach.count != $columns.size()),#end +#end +#end + }, + 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" || $column.htmlType == "radio")"change"#else"blur"#end } + ]#if($foreach.count != $columns.size()),#end +#end +#end + } +}); + +const { queryParams, form, rules } = toRefs(data); + +/** 查询${functionName}列表 */ +function getList() { + loading.value = true; +#foreach ($column in $columns) +#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN") + queryParams.value.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 != daterange${AttrName} && '' != daterange${AttrName}) { + queryParams.value.params["begin${AttrName}"] = daterange${AttrName}.value[0]; + queryParams.value.params["end${AttrName}"] = daterange${AttrName}.value[1]; + } +#end +#end + list${BusinessName}(queryParams.value).then(response => { + ${businessName}List.value = proxy.handleTree(response.data, "${treeCode}", "${treeParentCode}"); + loading.value = false; + }); +} + +/** 查询${functionName}下拉树结构 */ +function getTreeselect() { + list${BusinessName}().then(response => { + ${businessName}Options.value = []; + const data = { ${treeCode}: 0, ${treeName}: '顶级节点', children: [] }; + data.children = proxy.handleTree(response.data, "${treeCode}", "${treeParentCode}"); + ${businessName}Options.value.push(data); + }); +} + +// 取消按钮 +function cancel() { + open.value = false; + reset(); +} + +// 表单重置 +function reset() { + form.value = { +#foreach ($column in $columns) +#if($column.htmlType == "checkbox") + $column.javaField: []#if($foreach.count != $columns.size()),#end +#else + $column.javaField: null#if($foreach.count != $columns.size()),#end +#end +#end + }; + proxy.resetForm("${businessName}Ref"); +} + +/** 搜索按钮操作 */ +function handleQuery() { + getList(); +} + +/** 重置按钮操作 */ +function resetQuery() { +#foreach ($column in $columns) +#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN") +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) + daterange${AttrName}.value = []; +#end +#end + proxy.resetForm("queryRef"); + handleQuery(); +} + +/** 新增按钮操作 */ +function handleAdd(row) { + reset(); + getTreeselect(); + if (row != null && row.${treeCode}) { + form.value.${treeParentCode} = row.${treeCode}; + } else { + form.value.${treeParentCode} = 0; + } + open.value = true; + title.value = "添加${functionName}"; +} + +/** 展开/折叠操作 */ +function toggleExpandAll() { + refreshTable.value = false; + isExpandAll.value = !isExpandAll.value; + nextTick(() => { + refreshTable.value = true; + }); +} + +/** 修改按钮操作 */ +async function handleUpdate(row) { + reset(); + await getTreeselect(); + if (row != null) { + form.value.${treeParentCode} = row.${treeParentCode}; + } + get${BusinessName}(row.${pkColumn.javaField}).then(response => { + form.value = response.data; +#foreach ($column in $columns) +#if($column.htmlType == "checkbox") + form.value.$column.javaField = form.value.${column.javaField}.split(","); +#end +#end + open.value = true; + title.value = "修改${functionName}"; + }); +} + +/** 提交按钮 */ +function submitForm() { + proxy.#[[$]]#refs["${businessName}Ref"].validate(valid => { + if (valid) { +#foreach ($column in $columns) +#if($column.htmlType == "checkbox") + form.value.$column.javaField = form.value.${column.javaField}.join(","); +#end +#end + if (form.value.${pkColumn.javaField} != null) { + update${BusinessName}(form.value).then(response => { + proxy.#[[$modal]]#.msgSuccess("修改成功"); + open.value = false; + getList(); + }); + } else { + add${BusinessName}(form.value).then(response => { + proxy.#[[$modal]]#.msgSuccess("新增成功"); + open.value = false; + getList(); + }); + } + } + }); +} + +/** 删除按钮操作 */ +function handleDelete(row) { + proxy.#[[$modal]]#.confirm('是否确认删除${functionName}编号为"' + row.${pkColumn.javaField} + '"的数据项?').then(function() { + return del${BusinessName}(row.${pkColumn.javaField}); + }).then(() => { + getList(); + proxy.#[[$modal]]#.msgSuccess("删除成功"); + }).catch(() => {}); +} + +getList(); +</script> diff --git a/ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm new file mode 100644 index 0000000..8b25665 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm @@ -0,0 +1,590 @@ +<template> + <div class="app-container"> + <el-form :model="queryParams" ref="queryRef" :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 + @keyup.enter="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> + <el-option + v-for="dict in ${dictType}" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </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> + <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 + 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}" style="width: 308px"> + <el-date-picker + v-model="daterange${AttrName}" + 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="Search" @click="handleQuery">搜索</el-button> + <el-button icon="Refresh" @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="Plus" + @click="handleAdd" + v-hasPermi="['${moduleName}:${businessName}:add']" + >新增</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="success" + plain + icon="Edit" + :disabled="single" + @click="handleUpdate" + v-hasPermi="['${moduleName}:${businessName}:edit']" + >修改</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="danger" + plain + icon="Delete" + :disabled="multiple" + @click="handleDelete" + v-hasPermi="['${moduleName}:${businessName}:remove']" + >删除</el-button> + </el-col> + <el-col :span="1.5"> + <el-button + type="warning" + plain + icon="Download" + @click="handleExport" + v-hasPermi="['${moduleName}:${businessName}:export']" + >导出</el-button> + </el-col> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + + <el-table v-loading="loading" :data="${businessName}List" @selection-change="handleSelectionChange"> + <el-table-column type="selection" width="55" align="center" /> +#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) + <el-table-column label="${comment}" align="center" prop="${javaField}" /> +#elseif($column.list && $column.htmlType == "datetime") + <el-table-column label="${comment}" align="center" prop="${javaField}" width="180"> + <template #default="scope"> + <span>{{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}</span> + </template> + </el-table-column> +#elseif($column.list && $column.htmlType == "imageUpload") + <el-table-column label="${comment}" align="center" prop="${javaField}" width="100"> + <template #default="scope"> + <image-preview :src="scope.row.${javaField}" :width="50" :height="50"/> + </template> + </el-table-column> +#elseif($column.list && "" != $column.dictType) + <el-table-column label="${comment}" align="center" prop="${javaField}"> + <template #default="scope"> +#if($column.htmlType == "checkbox") + <dict-tag :options="${column.dictType}" :value="scope.row.${javaField} ? scope.row.${javaField}.split(',') : []"/> +#else + <dict-tag :options="${column.dictType}" :value="scope.row.${javaField}"/> +#end + </template> + </el-table-column> +#elseif($column.list && "" != $javaField) + <el-table-column label="${comment}" align="center" prop="${javaField}" /> +#end +#end + <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['${moduleName}:${businessName}:edit']">修改</el-button> + <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['${moduleName}:${businessName}:remove']">删除</el-button> + </template> + </el-table-column> + </el-table> + + <pagination + v-show="total>0" + :total="total" + v-model:page="queryParams.pageNum" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + + <!-- 添加或修改${functionName}对话框 --> + <el-dialog :title="title" v-model="open" width="500px" append-to-body> + <el-form ref="${businessName}Ref" :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($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}" prop="${field}"> + <image-upload v-model="form.${field}"/> + </el-form-item> +#elseif($column.htmlType == "fileUpload") + <el-form-item label="${comment}" prop="${field}"> + <file-upload 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 ${dictType}" + :key="dict.value" + :label="dict.label" +#if($column.javaType == "Integer" || $column.javaType == "Long") + :value="parseInt(dict.value)" +#else + :value="dict.value" +#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}" prop="${field}"> + <el-checkbox-group v-model="form.${field}"> + <el-checkbox + v-for="dict in ${dictType}" + :key="dict.value" + :label="dict.value"> + {{dict.label}} + </el-checkbox> + </el-checkbox-group> + </el-form-item> +#elseif($column.htmlType == "checkbox" && $dictType) + <el-form-item label="${comment}" prop="${field}"> + <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}" prop="${field}"> + <el-radio-group v-model="form.${field}"> + <el-radio + v-for="dict in ${dictType}" + :key="dict.value" +#if($column.javaType == "Integer" || $column.javaType == "Long") + :label="parseInt(dict.value)" +#else + :label="dict.value" +#end + >{{dict.label}}</el-radio> + </el-radio-group> + </el-form-item> +#elseif($column.htmlType == "radio" && $dictType) + <el-form-item label="${comment}" prop="${field}"> + <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 + v-model="form.${field}" + type="date" + value-format="YYYY-MM-DD" + 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 +#if($table.sub) + <el-divider content-position="center">${subTable.functionName}信息</el-divider> + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button type="primary" icon="Plus" @click="handleAdd${subClassName}">添加</el-button> + </el-col> + <el-col :span="1.5"> + <el-button type="danger" icon="Delete" @click="handleDelete${subClassName}">删除</el-button> + </el-col> + </el-row> + <el-table :data="${subclassName}List" :row-class-name="row${subClassName}Index" @selection-change="handle${subClassName}SelectionChange" ref="${subclassName}"> + <el-table-column type="selection" width="50" align="center" /> + <el-table-column label="序号" align="center" prop="index" width="50"/> +#foreach($column in $subTable.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 || $javaField == ${subTableFkclassName}) +#elseif($column.list && $column.htmlType == "input") + <el-table-column label="$comment" prop="${javaField}" width="150"> + <template #default="scope"> + <el-input v-model="scope.row.$javaField" placeholder="请输入$comment" /> + </template> + </el-table-column> +#elseif($column.list && $column.htmlType == "datetime") + <el-table-column label="$comment" prop="${javaField}" width="240"> + <template #default="scope"> + <el-date-picker clearable + v-model="scope.row.$javaField" + type="date" + value-format="YYYY-MM-DD" + placeholder="请选择$comment"> + </el-date-picker> + </template> + </el-table-column> +#elseif($column.list && ($column.htmlType == "select" || $column.htmlType == "radio") && "" != $column.dictType) + <el-table-column label="$comment" prop="${javaField}" width="150"> + <template #default="scope"> + <el-select v-model="scope.row.$javaField" placeholder="请选择$comment"> + <el-option + v-for="dict in $column.dictType" + :key="dict.value" + :label="dict.label" + :value="dict.value" + ></el-option> + </el-select> + </template> + </el-table-column> +#elseif($column.list && ($column.htmlType == "select" || $column.htmlType == "radio") && "" == $column.dictType) + <el-table-column label="$comment" prop="${javaField}" width="150"> + <template #default="scope"> + <el-select v-model="scope.row.$javaField" placeholder="请选择$comment"> + <el-option label="请选择字典生成" value="" /> + </el-select> + </template> + </el-table-column> +#end +#end + </el-table> +#end + </el-form> + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="submitForm">确 定</el-button> + <el-button @click="cancel">取 消</el-button> + </div> + </template> + </el-dialog> + </div> +</template> + +<script setup name="${BusinessName}"> +import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName} } from "@/api/${moduleName}/${businessName}"; + +const { proxy } = getCurrentInstance(); +#if(${dicts} != '') +#set($dictsNoSymbol=$dicts.replace("'", "")) +const { ${dictsNoSymbol} } = proxy.useDict(${dicts}); +#end + +const ${businessName}List = ref([]); +#if($table.sub) +const ${subclassName}List = ref([]); +#end +const open = ref(false); +const loading = ref(true); +const showSearch = ref(true); +const ids = ref([]); +#if($table.sub) +const checked${subClassName} = ref([]); +#end +const single = ref(true); +const multiple = ref(true); +const total = ref(0); +const title = ref(""); +#foreach ($column in $columns) +#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN") +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +const daterange${AttrName} = ref([]); +#end +#end + +const data = reactive({ + form: {}, + queryParams: { + pageNum: 1, + pageSize: 10, + #foreach ($column in $columns) +#if($column.query) + $column.javaField: null#if($foreach.count != $columns.size()),#end +#end +#end + }, + 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" || $column.htmlType == "radio")"change"#else"blur"#end } + ]#if($foreach.count != $columns.size()),#end +#end +#end + } +}); + +const { queryParams, form, rules } = toRefs(data); + +/** 查询${functionName}列表 */ +function getList() { + loading.value = true; +#foreach ($column in $columns) +#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN") + queryParams.value.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 != daterange${AttrName} && '' != daterange${AttrName}) { + queryParams.value.params["begin${AttrName}"] = daterange${AttrName}.value[0]; + queryParams.value.params["end${AttrName}"] = daterange${AttrName}.value[1]; + } +#end +#end + list${BusinessName}(queryParams.value).then(response => { + ${businessName}List.value = response.rows; + total.value = response.total; + loading.value = false; + }); +} + +// 取消按钮 +function cancel() { + open.value = false; + reset(); +} + +// 表单重置 +function reset() { + form.value = { +#foreach ($column in $columns) +#if($column.htmlType == "checkbox") + $column.javaField: []#if($foreach.count != $columns.size()),#end +#else + $column.javaField: null#if($foreach.count != $columns.size()),#end +#end +#end + }; +#if($table.sub) + ${subclassName}List.value = []; +#end + proxy.resetForm("${businessName}Ref"); +} + +/** 搜索按钮操作 */ +function handleQuery() { + queryParams.value.pageNum = 1; + getList(); +} + +/** 重置按钮操作 */ +function resetQuery() { +#foreach ($column in $columns) +#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN") +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) + daterange${AttrName}.value = []; +#end +#end + proxy.resetForm("queryRef"); + handleQuery(); +} + +// 多选框选中数据 +function handleSelectionChange(selection) { + ids.value = selection.map(item => item.${pkColumn.javaField}); + single.value = selection.length != 1; + multiple.value = !selection.length; +} + +/** 新增按钮操作 */ +function handleAdd() { + reset(); + open.value = true; + title.value = "添加${functionName}"; +} + +/** 修改按钮操作 */ +function handleUpdate(row) { + reset(); + const _${pkColumn.javaField} = row.${pkColumn.javaField} || ids.value + get${BusinessName}(_${pkColumn.javaField}).then(response => { + form.value = response.data; +#foreach ($column in $columns) +#if($column.htmlType == "checkbox") + form.value.$column.javaField = form.value.${column.javaField}.split(","); +#end +#end +#if($table.sub) + ${subclassName}List.value = response.data.${subclassName}List; +#end + open.value = true; + title.value = "修改${functionName}"; + }); +} + +/** 提交按钮 */ +function submitForm() { + proxy.#[[$]]#refs["${businessName}Ref"].validate(valid => { + if (valid) { +#foreach ($column in $columns) +#if($column.htmlType == "checkbox") + form.value.$column.javaField = form.value.${column.javaField}.join(","); +#end +#end +#if($table.sub) + form.value.${subclassName}List = ${subclassName}List.value; +#end + if (form.value.${pkColumn.javaField} != null) { + update${BusinessName}(form.value).then(response => { + proxy.#[[$modal]]#.msgSuccess("修改成功"); + open.value = false; + getList(); + }); + } else { + add${BusinessName}(form.value).then(response => { + proxy.#[[$modal]]#.msgSuccess("新增成功"); + open.value = false; + getList(); + }); + } + } + }); +} + +/** 删除按钮操作 */ +function handleDelete(row) { + const _${pkColumn.javaField}s = row.${pkColumn.javaField} || ids.value; + proxy.#[[$modal]]#.confirm('是否确认删除${functionName}编号为"' + _${pkColumn.javaField}s + '"的数据项?').then(function() { + return del${BusinessName}(_${pkColumn.javaField}s); + }).then(() => { + getList(); + proxy.#[[$modal]]#.msgSuccess("删除成功"); + }).catch(() => {}); +} + +#if($table.sub) +/** ${subTable.functionName}序号 */ +function row${subClassName}Index({ row, rowIndex }) { + row.index = rowIndex + 1; +} + +/** ${subTable.functionName}添加按钮操作 */ +function handleAdd${subClassName}() { + let obj = {}; +#foreach($column in $subTable.columns) +#if($column.pk || $column.javaField == ${subTableFkclassName}) +#elseif($column.list && "" != $javaField) + obj.$column.javaField = ""; +#end +#end + ${subclassName}List.value.push(obj); +} + +/** ${subTable.functionName}删除按钮操作 */ +function handleDelete${subClassName}() { + if (checked${subClassName}.value.length == 0) { + proxy.#[[$modal]]#.msgError("请先选择要删除的${subTable.functionName}数据"); + } else { + const ${subclassName}s = ${subclassName}List.value; + const checked${subClassName}s = checked${subClassName}.value; + ${subclassName}List.value = ${subclassName}s.filter(function(item) { + return checked${subClassName}s.indexOf(item.index) == -1 + }); + } +} + +/** 复选框选中数据 */ +function handle${subClassName}SelectionChange(selection) { + checked${subClassName}.value = selection.map(item => item.index) +} + +#end +/** 导出按钮操作 */ +function handleExport() { + proxy.download('${moduleName}/${businessName}/export', { + ...queryParams.value + }, `${businessName}_#[[${new Date().getTime()}]]#.xlsx`) +} + +getList(); +</script> diff --git a/ruoyi-generator/src/main/resources/vm/vue/v3/readme.txt b/ruoyi-generator/src/main/resources/vm/vue/v3/readme.txt new file mode 100644 index 0000000..99239bb --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/vue/v3/readme.txt @@ -0,0 +1 @@ +���ʹ�õ���RuoYi-Vue3ǰ�ˣ���ô��Ҫ����һ�´�Ŀ¼��ģ��index.vue.vm��index-tree.vue.vm�ļ����ϼ�vueĿ¼�� \ No newline at end of file diff --git a/ruoyi-generator/src/main/resources/vm/xml/mapper.xml.vm b/ruoyi-generator/src/main/resources/vm/xml/mapper.xml.vm new file mode 100644 index 0000000..0ceb3d8 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/xml/mapper.xml.vm @@ -0,0 +1,135 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="${packageName}.mapper.${ClassName}Mapper"> + + <resultMap type="${ClassName}" id="${ClassName}Result"> +#foreach ($column in $columns) + <result property="${column.javaField}" column="${column.columnName}" /> +#end + </resultMap> +#if($table.sub) + + <resultMap id="${ClassName}${subClassName}Result" type="${ClassName}" extends="${ClassName}Result"> + <collection property="${subclassName}List" notNullColumn="sub_${subTable.pkColumn.columnName}" javaType="java.util.List" resultMap="${subClassName}Result" /> + </resultMap> + + <resultMap type="${subClassName}" id="${subClassName}Result"> +#foreach ($column in $subTable.columns) + <result property="${column.javaField}" column="sub_${column.columnName}" /> +#end + </resultMap> +#end + + <sql id="select${ClassName}Vo"> + select#foreach($column in $columns) $column.columnName#if($foreach.count != $columns.size()),#end#end from ${tableName} + </sql> + + <select id="select${ClassName}List" parameterType="${ClassName}" resultMap="${ClassName}Result"> + <include refid="select${ClassName}Vo"/> + <where> +#foreach($column in $columns) +#set($queryType=$column.queryType) +#set($javaField=$column.javaField) +#set($javaType=$column.javaType) +#set($columnName=$column.columnName) +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#if($column.query) +#if($column.queryType == "EQ") + <if test="$javaField != null #if($javaType == 'String' ) and $javaField.trim() != ''#end"> and $columnName = #{$javaField}</if> +#elseif($queryType == "NE") + <if test="$javaField != null #if($javaType == 'String' ) and $javaField.trim() != ''#end"> and $columnName != #{$javaField}</if> +#elseif($queryType == "GT") + <if test="$javaField != null #if($javaType == 'String' ) and $javaField.trim() != ''#end"> and $columnName > #{$javaField}</if> +#elseif($queryType == "GTE") + <if test="$javaField != null #if($javaType == 'String' ) and $javaField.trim() != ''#end"> and $columnName >= #{$javaField}</if> +#elseif($queryType == "LT") + <if test="$javaField != null #if($javaType == 'String' ) and $javaField.trim() != ''#end"> and $columnName < #{$javaField}</if> +#elseif($queryType == "LTE") + <if test="$javaField != null #if($javaType == 'String' ) and $javaField.trim() != ''#end"> and $columnName <= #{$javaField}</if> +#elseif($queryType == "LIKE") + <if test="$javaField != null #if($javaType == 'String' ) and $javaField.trim() != ''#end"> and $columnName like concat('%', #{$javaField}, '%')</if> +#elseif($queryType == "BETWEEN") + <if test="params.begin$AttrName != null and params.begin$AttrName != '' and params.end$AttrName != null and params.end$AttrName != ''"> and $columnName between #{params.begin$AttrName} and #{params.end$AttrName}</if> +#end +#end +#end + </where> + </select> + + <select id="select${ClassName}By${pkColumn.capJavaField}" parameterType="${pkColumn.javaType}" resultMap="#if($table.sub)${ClassName}${subClassName}Result#else${ClassName}Result#end"> +#if($table.crud || $table.tree) + <include refid="select${ClassName}Vo"/> + where ${pkColumn.columnName} = #{${pkColumn.javaField}} +#elseif($table.sub) + select#foreach($column in $columns) a.$column.columnName#if($foreach.count != $columns.size()),#end#end, + #foreach($column in $subTable.columns) b.$column.columnName as sub_$column.columnName#if($foreach.count != $subTable.columns.size()),#end#end + + from ${tableName} a + left join ${subTableName} b on b.${subTableFkName} = a.${pkColumn.columnName} + where a.${pkColumn.columnName} = #{${pkColumn.javaField}} +#end + </select> + + <insert id="insert${ClassName}" parameterType="${ClassName}"#if($pkColumn.increment) useGeneratedKeys="true" keyProperty="$pkColumn.javaField"#end> + insert into ${tableName} + <trim prefix="(" suffix=")" suffixOverrides=","> +#foreach($column in $columns) +#if($column.columnName != $pkColumn.columnName || !$pkColumn.increment) + <if test="$column.javaField != null#if($column.javaType == 'String' && $column.required) and $column.javaField != ''#end">$column.columnName,</if> +#end +#end + </trim> + <trim prefix="values (" suffix=")" suffixOverrides=","> +#foreach($column in $columns) +#if($column.columnName != $pkColumn.columnName || !$pkColumn.increment) + <if test="$column.javaField != null#if($column.javaType == 'String' && $column.required) and $column.javaField != ''#end">#{$column.javaField},</if> +#end +#end + </trim> + </insert> + + <update id="update${ClassName}" parameterType="${ClassName}"> + update ${tableName} + <trim prefix="SET" suffixOverrides=","> +#foreach($column in $columns) +#if($column.columnName != $pkColumn.columnName) + <if test="$column.javaField != null#if($column.javaType == 'String' && $column.required) and $column.javaField != ''#end">$column.columnName = #{$column.javaField},</if> +#end +#end + </trim> + where ${pkColumn.columnName} = #{${pkColumn.javaField}} + </update> + + <delete id="delete${ClassName}By${pkColumn.capJavaField}" parameterType="${pkColumn.javaType}"> + delete from ${tableName} where ${pkColumn.columnName} = #{${pkColumn.javaField}} + </delete> + + <delete id="delete${ClassName}By${pkColumn.capJavaField}s" parameterType="String"> + delete from ${tableName} where ${pkColumn.columnName} in + <foreach item="${pkColumn.javaField}" collection="array" open="(" separator="," close=")"> + #{${pkColumn.javaField}} + </foreach> + </delete> +#if($table.sub) + + <delete id="delete${subClassName}By${subTableFkClassName}s" parameterType="String"> + delete from ${subTableName} where ${subTableFkName} in + <foreach item="${subTableFkclassName}" collection="array" open="(" separator="," close=")"> + #{${subTableFkclassName}} + </foreach> + </delete> + + <delete id="delete${subClassName}By${subTableFkClassName}" parameterType="${pkColumn.javaType}"> + delete from ${subTableName} where ${subTableFkName} = #{${subTableFkclassName}} + </delete> + + <insert id="batch${subClassName}"> + insert into ${subTableName}(#foreach($column in $subTable.columns) $column.columnName#if($foreach.count != $subTable.columns.size()),#end#end) values + <foreach item="item" index="index" collection="list" separator=","> + (#foreach($column in $subTable.columns) #{item.$column.javaField}#if($foreach.count != $subTable.columns.size()),#end#end) + </foreach> + </insert> +#end +</mapper> \ No newline at end of file diff --git a/ruoyi-quartz/pom.xml b/ruoyi-quartz/pom.xml new file mode 100644 index 0000000..91365cb --- /dev/null +++ b/ruoyi-quartz/pom.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>zhengshengxin</artifactId> + <groupId>com.ruoyi</groupId> + <version>3.8.6</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>ruoyi-quartz</artifactId> + + <description> + quartz定时任务 + </description> + + <dependencies> + + <!-- 定时任务 --> + <dependency> + <groupId>org.quartz-scheduler</groupId> + <artifactId>quartz</artifactId> + <exclusions> + <exclusion> + <groupId>com.mchange</groupId> + <artifactId>c3p0</artifactId> + </exclusion> + </exclusions> + </dependency> + + <!-- 通用工具--> + <dependency> + <groupId>com.ruoyi</groupId> + <artifactId>ruoyi-common</artifactId> + </dependency> + + </dependencies> + +</project> \ No newline at end of file diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/config/ScheduleConfig.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/config/ScheduleConfig.java new file mode 100644 index 0000000..a558170 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/config/ScheduleConfig.java @@ -0,0 +1,57 @@ +//package com.ruoyi.quartz.config; +// +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import org.springframework.scheduling.quartz.SchedulerFactoryBean; +//import javax.sql.DataSource; +//import java.util.Properties; +// +///** +// * 定时任务配置(单机部署建议删除此类和qrtz数据库表,默认走内存会最高效) +// * +// * @author ruoyi +// */ +//@Configuration +//public class ScheduleConfig +//{ +// @Bean +// public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) +// { +// SchedulerFactoryBean factory = new SchedulerFactoryBean(); +// factory.setDataSource(dataSource); +// +// // quartz参数 +// Properties prop = new Properties(); +// prop.put("org.quartz.scheduler.instanceName", "RuoyiScheduler"); +// prop.put("org.quartz.scheduler.instanceId", "AUTO"); +// // 线程池配置 +// prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool"); +// prop.put("org.quartz.threadPool.threadCount", "20"); +// prop.put("org.quartz.threadPool.threadPriority", "5"); +// // JobStore配置 +// prop.put("org.quartz.jobStore.class", "org.springframework.scheduling.quartz.LocalDataSourceJobStore"); +// // 集群配置 +// prop.put("org.quartz.jobStore.isClustered", "true"); +// prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000"); +// prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1"); +// prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true"); +// +// // sqlserver 启用 +// // prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?"); +// prop.put("org.quartz.jobStore.misfireThreshold", "12000"); +// prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_"); +// factory.setQuartzProperties(prop); +// +// factory.setSchedulerName("RuoyiScheduler"); +// // 延时启动 +// factory.setStartupDelay(1); +// factory.setApplicationContextSchedulerContextKey("applicationContextKey"); +// // 可选,QuartzScheduler +// // 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了 +// factory.setOverwriteExistingJobs(true); +// // 设置自动启动,默认为true +// factory.setAutoStartup(true); +// +// return factory; +// } +//} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java new file mode 100644 index 0000000..38380d5 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java @@ -0,0 +1,185 @@ +package com.ruoyi.quartz.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.quartz.SchedulerException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +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.StringUtils; +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 com.ruoyi.quartz.util.ScheduleUtils; + +/** + * 调度任务信息操作处理 + * + * @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) + { +// startPage(); + List<SysJob> list = jobService.selectJobList(sysJob); + return getDataTable(list); + } + + /** + * 导出定时任务列表 + */ +// @PreAuthorize("@ss.hasPermi('monitor:job:export')") +// @Log(title = "定时任务", businessType = BusinessType.EXPORT) +// @PostMapping("/export") +// public void export(HttpServletResponse response, SysJob sysJob) +// { +// List<SysJob> list = jobService.selectJobList(sysJob); +// ExcelUtil<SysJob> util = new ExcelUtil<SysJob>(SysJob.class); +// util.exportExcel(response, list, "定时任务"); +// } + + /** + * 获取定时任务详细信息 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:query')") + @GetMapping(value = "/{jobId}") + public AjaxResult getInfo(@PathVariable("jobId") Long jobId) + { + return success(jobService.selectJobById(jobId)); + } + + /** + * 新增定时任务 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:add')") + @Log(title = "定时任务", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody SysJob job) throws SchedulerException, TaskException + { + if (!CronUtils.isValid(job.getCronExpression())) + { + return error("新增任务'" + job.getJobName() + "'失败,Cron表达式不正确"); + } + else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI)) + { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS })) + { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap(s)'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS })) + { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR)) + { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串存在违规"); + } + else if (!ScheduleUtils.whiteList(job.getInvokeTarget())) + { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不在白名单内"); + } + job.setCreateBy(getUsername()); + return toAjax(jobService.insertJob(job)); + } + + /** + * 修改定时任务 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:edit')") + @Log(title = "定时任务", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody SysJob job) throws SchedulerException, TaskException + { + if (!CronUtils.isValid(job.getCronExpression())) + { + return error("修改任务'" + job.getJobName() + "'失败,Cron表达式不正确"); + } + else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI)) + { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS })) + { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap(s)'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS })) + { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR)) + { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串存在违规"); + } + else if (!ScheduleUtils.whiteList(job.getInvokeTarget())) + { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串不在白名单内"); + } + job.setUpdateBy(getUsername()); + return toAjax(jobService.updateJob(job)); + } + + /** + * 定时任务状态修改 + */ + @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 + { + boolean result = jobService.run(job); + return result ? success() : error("任务不存在或已过期!"); + } + + /** + * 删除定时任务 + */ + @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 success(); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobLogController.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobLogController.java new file mode 100644 index 0000000..d9d1f71 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobLogController.java @@ -0,0 +1,92 @@ +package com.ruoyi.quartz.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +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.utils.poi.ExcelUtil; +import com.ruoyi.quartz.domain.SysJobLog; +import com.ruoyi.quartz.service.ISysJobLogService; + +/** + * 调度日志操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/jobLog") +public class SysJobLogController extends BaseController +{ + @Autowired + private ISysJobLogService jobLogService; + + /** + * 查询定时任务调度日志列表 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:list')") + @GetMapping("/list") + public TableDataInfo list(SysJobLog sysJobLog) + { +// startPage(); + List<SysJobLog> list = jobLogService.selectJobLogList(sysJobLog); + return getDataTable(list); + } + + /** + * 导出定时任务调度日志列表 + */ +// @PreAuthorize("@ss.hasPermi('monitor:job:export')") +// @Log(title = "任务调度日志", businessType = BusinessType.EXPORT) +// @PostMapping("/export") +// public void export(HttpServletResponse response, SysJobLog sysJobLog) +// { +// List<SysJobLog> list = jobLogService.selectJobLogList(sysJobLog); +// ExcelUtil<SysJobLog> util = new ExcelUtil<SysJobLog>(SysJobLog.class); +// util.exportExcel(response, list, "调度日志"); +// } + + /** + * 根据调度编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:query')") + @GetMapping(value = "/{jobLogId}") + public AjaxResult getInfo(@PathVariable Long jobLogId) + { + return success(jobLogService.selectJobLogById(jobLogId)); + } + + + /** + * 删除定时任务调度日志 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:remove')") + @Log(title = "定时任务调度日志", businessType = BusinessType.DELETE) + @DeleteMapping("/{jobLogIds}") + public AjaxResult remove(@PathVariable Long[] jobLogIds) + { + return toAjax(jobLogService.deleteJobLogByIds(jobLogIds)); + } + + /** + * 清空定时任务调度日志 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:remove')") + @Log(title = "调度日志", businessType = BusinessType.CLEAN) + @DeleteMapping("/clean") + public AjaxResult clean() + { + jobLogService.cleanJobLog(); + return success(); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJob.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJob.java new file mode 100644 index 0000000..277aa60 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJob.java @@ -0,0 +1,169 @@ +package com.ruoyi.quartz.domain; + +import java.util.Date; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.constant.ScheduleConstants; +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.quartz.util.CronUtils; + +/** + * 定时任务调度表 sys_job + * + * @author ruoyi + */ +public class SysJob extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 任务ID */ + //@Excel(name = "任务序号", cellType = ColumnType.NUMERIC) + private Long jobId; + + /** 任务名称 */ + //@Excel(name = "任务名称") + private String jobName; + + /** 任务组名 */ + //@Excel(name = "任务组名") + private String jobGroup; + + /** 调用目标字符串 */ + //@Excel(name = "调用目标字符串") + private String invokeTarget; + + /** cron执行表达式 */ + //@Excel(name = "执行表达式 ") + private String cronExpression; + + /** cron计划策略 */ + //@Excel(name = "计划策略 ", readConverterExp = "0=默认,1=立即触发执行,2=触发一次执行,3=不触发立即执行") + private String misfirePolicy = ScheduleConstants.MISFIRE_DEFAULT; + + /** 是否并发执行(0允许 1禁止) */ + //@Excel(name = "并发执行", readConverterExp = "0=允许,1=禁止") + private String concurrent; + + /** 任务状态(0正常 1暂停) */ + //@Excel(name = "任务状态", readConverterExp = "0=正常,1=暂停") + private String status; + + public Long getJobId() + { + return jobId; + } + + public void setJobId(Long jobId) + { + this.jobId = jobId; + } + + @NotBlank(message = "任务名称不能为空") + @Size(min = 0, max = 64, message = "任务名称不能超过64个字符") + public String getJobName() + { + return jobName; + } + + public void setJobName(String jobName) + { + this.jobName = jobName; + } + + public String getJobGroup() + { + return jobGroup; + } + + public void setJobGroup(String jobGroup) + { + this.jobGroup = jobGroup; + } + + @NotBlank(message = "调用目标字符串不能为空") + @Size(min = 0, max = 500, message = "调用目标字符串长度不能超过500个字符") + public String getInvokeTarget() + { + return invokeTarget; + } + + public void setInvokeTarget(String invokeTarget) + { + this.invokeTarget = invokeTarget; + } + + @NotBlank(message = "Cron执行表达式不能为空") + @Size(min = 0, max = 255, message = "Cron执行表达式不能超过255个字符") + public String getCronExpression() + { + return cronExpression; + } + + public void setCronExpression(String cronExpression) + { + this.cronExpression = cronExpression; + } + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + public Date getNextValidTime() + { + if (StringUtils.isNotEmpty(cronExpression)) + { + return CronUtils.getNextExecution(cronExpression); + } + return null; + } + + public String getMisfirePolicy() + { + return misfirePolicy; + } + + public void setMisfirePolicy(String misfirePolicy) + { + this.misfirePolicy = misfirePolicy; + } + + public String getConcurrent() + { + return concurrent; + } + + public void setConcurrent(String concurrent) + { + this.concurrent = concurrent; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("jobId", getJobId()) + .append("jobName", getJobName()) + .append("jobGroup", getJobGroup()) + .append("cronExpression", getCronExpression()) + .append("nextValidTime", getNextValidTime()) + .append("misfirePolicy", getMisfirePolicy()) + .append("concurrent", getConcurrent()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJobLog.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJobLog.java new file mode 100644 index 0000000..c5418b9 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJobLog.java @@ -0,0 +1,154 @@ +package com.ruoyi.quartz.domain; + +import java.util.Date; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 定时任务调度日志表 sys_job_log + * + * @author ruoyi + */ +public class SysJobLog extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** ID */ + //@Excel(name = "日志序号") + private Long jobLogId; + + /** 任务名称 */ + //@Excel(name = "任务名称") + private String jobName; + + /** 任务组名 */ + //@Excel(name = "任务组名") + private String jobGroup; + + /** 调用目标字符串 */ + //@Excel(name = "调用目标字符串") + private String invokeTarget; + + /** 日志信息 */ + //@Excel(name = "日志信息") + private String jobMessage; + + /** 执行状态(0正常 1失败) */ + //@Excel(name = "执行状态", readConverterExp = "0=正常,1=失败") + private String status; + + /** 异常信息 */ + //@Excel(name = "异常信息") + private String exceptionInfo; + + /** 开始时间 */ + private Date startTime; + + /** 停止时间 */ + private Date stopTime; + + public Long getJobLogId() + { + return jobLogId; + } + + public void setJobLogId(Long jobLogId) + { + this.jobLogId = jobLogId; + } + + public String getJobName() + { + return jobName; + } + + public void setJobName(String jobName) + { + this.jobName = jobName; + } + + public String getJobGroup() + { + return jobGroup; + } + + public void setJobGroup(String jobGroup) + { + this.jobGroup = jobGroup; + } + + public String getInvokeTarget() + { + return invokeTarget; + } + + public void setInvokeTarget(String invokeTarget) + { + this.invokeTarget = invokeTarget; + } + + public String getJobMessage() + { + return jobMessage; + } + + public void setJobMessage(String jobMessage) + { + this.jobMessage = jobMessage; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getExceptionInfo() + { + return exceptionInfo; + } + + public void setExceptionInfo(String exceptionInfo) + { + this.exceptionInfo = exceptionInfo; + } + + public Date getStartTime() + { + return startTime; + } + + public void setStartTime(Date startTime) + { + this.startTime = startTime; + } + + public Date getStopTime() + { + return stopTime; + } + + public void setStopTime(Date stopTime) + { + this.stopTime = stopTime; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("jobLogId", getJobLogId()) + .append("jobName", getJobName()) + .append("jobGroup", getJobGroup()) + .append("jobMessage", getJobMessage()) + .append("status", getStatus()) + .append("exceptionInfo", getExceptionInfo()) + .append("startTime", getStartTime()) + .append("stopTime", getStopTime()) + .toString(); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobLogMapper.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobLogMapper.java new file mode 100644 index 0000000..727d916 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobLogMapper.java @@ -0,0 +1,64 @@ +package com.ruoyi.quartz.mapper; + +import java.util.List; +import com.ruoyi.quartz.domain.SysJobLog; + +/** + * 调度任务日志信息 数据层 + * + * @author ruoyi + */ +public interface SysJobLogMapper +{ + /** + * 获取quartz调度器日志的计划任务 + * + * @param jobLog 调度日志信息 + * @return 调度任务日志集合 + */ + public List<SysJobLog> selectJobLogList(SysJobLog jobLog); + + /** + * 查询所有调度任务日志 + * + * @return 调度任务日志列表 + */ + public List<SysJobLog> selectJobLogAll(); + + /** + * 通过调度任务日志ID查询调度信息 + * + * @param jobLogId 调度任务日志ID + * @return 调度任务日志对象信息 + */ + public SysJobLog selectJobLogById(Long jobLogId); + + /** + * 新增任务日志 + * + * @param jobLog 调度日志信息 + * @return 结果 + */ + public int insertJobLog(SysJobLog jobLog); + + /** + * 批量删除调度日志信息 + * + * @param logIds 需要删除的数据ID + * @return 结果 + */ + public int deleteJobLogByIds(Long[] logIds); + + /** + * 删除任务日志 + * + * @param jobId 调度日志ID + * @return 结果 + */ + public int deleteJobLogById(Long jobId); + + /** + * 清空任务日志 + */ + public void cleanJobLog(); +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobMapper.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobMapper.java new file mode 100644 index 0000000..20f45db --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobMapper.java @@ -0,0 +1,67 @@ +package com.ruoyi.quartz.mapper; + +import java.util.List; +import com.ruoyi.quartz.domain.SysJob; + +/** + * 调度任务信息 数据层 + * + * @author ruoyi + */ +public interface SysJobMapper +{ + /** + * 查询调度任务日志集合 + * + * @param job 调度信息 + * @return 操作日志集合 + */ + public List<SysJob> selectJobList(SysJob job); + + /** + * 查询所有调度任务 + * + * @return 调度任务列表 + */ + public List<SysJob> selectJobAll(); + + /** + * 通过调度ID查询调度任务信息 + * + * @param jobId 调度ID + * @return 角色对象信息 + */ + public SysJob selectJobById(Long jobId); + + /** + * 通过调度ID删除调度任务信息 + * + * @param jobId 调度ID + * @return 结果 + */ + public int deleteJobById(Long jobId); + + /** + * 批量删除调度任务信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteJobByIds(Long[] ids); + + /** + * 修改调度任务信息 + * + * @param job 调度任务信息 + * @return 结果 + */ + public int updateJob(SysJob job); + + /** + * 新增调度任务信息 + * + * @param job 调度任务信息 + * @return 结果 + */ + public int insertJob(SysJob job); +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobLogService.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobLogService.java new file mode 100644 index 0000000..8546792 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobLogService.java @@ -0,0 +1,56 @@ +package com.ruoyi.quartz.service; + +import java.util.List; +import com.ruoyi.quartz.domain.SysJobLog; + +/** + * 定时任务调度日志信息信息 服务层 + * + * @author ruoyi + */ +public interface ISysJobLogService +{ + /** + * 获取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(); +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobService.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobService.java new file mode 100644 index 0000000..437ade8 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobService.java @@ -0,0 +1,102 @@ +package com.ruoyi.quartz.service; + +import java.util.List; +import org.quartz.SchedulerException; +import com.ruoyi.common.exception.job.TaskException; +import com.ruoyi.quartz.domain.SysJob; + +/** + * 定时任务调度信息信息 服务层 + * + * @author ruoyi + */ +public interface ISysJobService +{ + /** + * 获取quartz调度器的计划任务 + * + * @param job 调度信息 + * @return 调度任务集合 + */ + 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 boolean 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); +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobLogServiceImpl.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobLogServiceImpl.java new file mode 100644 index 0000000..812eed7 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobLogServiceImpl.java @@ -0,0 +1,87 @@ +package com.ruoyi.quartz.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.quartz.domain.SysJobLog; +import com.ruoyi.quartz.mapper.SysJobLogMapper; +import com.ruoyi.quartz.service.ISysJobLogService; + +/** + * 定时任务调度日志信息 服务层 + * + * @author ruoyi + */ +@Service +public class SysJobLogServiceImpl implements ISysJobLogService +{ + @Autowired + private SysJobLogMapper jobLogMapper; + + /** + * 获取quartz调度器日志的计划任务 + * + * @param jobLog 调度日志信息 + * @return 调度任务日志集合 + */ + @Override + public List<SysJobLog> selectJobLogList(SysJobLog jobLog) + { + return jobLogMapper.selectJobLogList(jobLog); + } + + /** + * 通过调度任务日志ID查询调度信息 + * + * @param jobLogId 调度任务日志ID + * @return 调度任务日志对象信息 + */ + @Override + public SysJobLog selectJobLogById(Long jobLogId) + { + return jobLogMapper.selectJobLogById(jobLogId); + } + + /** + * 新增任务日志 + * + * @param jobLog 调度日志信息 + */ + @Override + public void addJobLog(SysJobLog jobLog) + { + jobLogMapper.insertJobLog(jobLog); + } + + /** + * 批量删除调度日志信息 + * + * @param logIds 需要删除的数据ID + * @return 结果 + */ + @Override + public int deleteJobLogByIds(Long[] logIds) + { + return jobLogMapper.deleteJobLogByIds(logIds); + } + + /** + * 删除任务日志 + * + * @param jobId 调度日志ID + */ + @Override + public int deleteJobLogById(Long jobId) + { + return jobLogMapper.deleteJobLogById(jobId); + } + + /** + * 清空任务日志 + */ + @Override + public void cleanJobLog() + { + jobLogMapper.cleanJobLog(); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java new file mode 100644 index 0000000..77fdbb5 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java @@ -0,0 +1,261 @@ +package com.ruoyi.quartz.service.impl; + +import java.util.List; +import javax.annotation.PostConstruct; +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 com.ruoyi.common.constant.ScheduleConstants; +import com.ruoyi.common.exception.job.TaskException; +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; + +/** + * 定时任务调度信息 服务层 + * + * @author ruoyi + */ +@Service +public class SysJobServiceImpl implements ISysJobService +{ + @Autowired + private Scheduler scheduler; + + @Autowired + private SysJobMapper jobMapper; + + /** + * 项目启动时,初始化定时器 主要是防止手动修改数据库导致未同步到定时任务处理(注:不能手动修改数据库ID和任务组名,否则会导致脏数据) + */ + @PostConstruct + public void init() throws SchedulerException, TaskException + { + scheduler.clear(); + List<SysJob> jobList = jobMapper.selectJobAll(); + for (SysJob job : jobList) + { + ScheduleUtils.createScheduleJob(scheduler, job); + } + } + + /** + * 获取quartz调度器的计划任务列表 + * + * @param job 调度信息 + * @return + */ + @Override + public List<SysJob> selectJobList(SysJob job) + { + return jobMapper.selectJobList(job); + } + + /** + * 通过调度任务ID查询调度信息 + * + * @param jobId 调度任务ID + * @return 调度任务对象信息 + */ + @Override + public SysJob selectJobById(Long jobId) + { + return jobMapper.selectJobById(jobId); + } + + /** + * 暂停任务 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int pauseJob(SysJob job) throws SchedulerException + { + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + job.setStatus(ScheduleConstants.Status.PAUSE.getValue()); + int rows = jobMapper.updateJob(job); + if (rows > 0) + { + scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup)); + } + return rows; + } + + /** + * 恢复任务 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int resumeJob(SysJob job) throws SchedulerException + { + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + job.setStatus(ScheduleConstants.Status.NORMAL.getValue()); + int rows = jobMapper.updateJob(job); + if (rows > 0) + { + scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup)); + } + return rows; + } + + /** + * 删除任务后,所对应的trigger也将被删除 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteJob(SysJob job) throws SchedulerException + { + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + int rows = jobMapper.deleteJobById(jobId); + if (rows > 0) + { + scheduler.deleteJob(ScheduleUtils.getJobKey(jobId, jobGroup)); + } + return rows; + } + + /** + * 批量删除调度信息 + * + * @param jobIds 需要删除的任务ID + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteJobByIds(Long[] jobIds) throws SchedulerException + { + for (Long jobId : jobIds) + { + SysJob job = jobMapper.selectJobById(jobId); + deleteJob(job); + } + } + + /** + * 任务调度状态修改 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + 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(rollbackFor = Exception.class) + public boolean run(SysJob job) throws SchedulerException + { + boolean result = false; + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + SysJob properties = selectJobById(job.getJobId()); + // 参数 + JobDataMap dataMap = new JobDataMap(); + dataMap.put(ScheduleConstants.TASK_PROPERTIES, properties); + JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup); + if (scheduler.checkExists(jobKey)) + { + result = true; + scheduler.triggerJob(jobKey, dataMap); + } + return result; + } + + /** + * 新增任务 + * + * @param job 调度信息 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int insertJob(SysJob job) throws SchedulerException, TaskException + { + job.setStatus(ScheduleConstants.Status.PAUSE.getValue()); + int rows = jobMapper.insertJob(job); + if (rows > 0) + { + ScheduleUtils.createScheduleJob(scheduler, job); + } + return rows; + } + + /** + * 更新任务的时间表达式 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int updateJob(SysJob job) throws SchedulerException, TaskException + { + SysJob properties = selectJobById(job.getJobId()); + int rows = jobMapper.updateJob(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); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java new file mode 100644 index 0000000..b03ebb3 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java @@ -0,0 +1,34 @@ +package com.ruoyi.quartz.task; + +import org.springframework.stereotype.Component; +import com.ruoyi.common.utils.StringUtils; + +/** + * 定时任务调度测试 + * + * @author ruoyi + */ +@Component("ryTask") +public class RyTask +{ + public void ryMultipleParams(String s, Boolean b, Long l, Double d, Integer i) + { + System.out.println(StringUtils.format("执行多参方法: 字符串类型{},布尔类型{},长整型{},浮点型{},整形{}", s, b, l, d, i)); + } + + public void ryParams(String params) + { + System.out.println("执行有参方法:" + params); + } + + public void ryNoParams() + { + System.out.println("执行无参方法"); + } + + + + + + +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/AbstractQuartzJob.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/AbstractQuartzJob.java new file mode 100644 index 0000000..731a5eb --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/AbstractQuartzJob.java @@ -0,0 +1,107 @@ +package com.ruoyi.quartz.util; + +import java.util.Date; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.ScheduleConstants; +import com.ruoyi.common.utils.ExceptionUtil; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.bean.BeanUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.quartz.domain.SysJob; +import com.ruoyi.quartz.domain.SysJobLog; +import com.ruoyi.quartz.service.ISysJobLogService; + +/** + * 抽象quartz调用 + * + * @author ruoyi + */ +public abstract class AbstractQuartzJob implements Job +{ + private static final Logger log = LoggerFactory.getLogger(AbstractQuartzJob.class); + + /** + * 线程本地变量 + */ + private static ThreadLocal<Date> threadLocal = new ThreadLocal<>(); + + @Override + public void execute(JobExecutionContext context) throws JobExecutionException + { + SysJob sysJob = new SysJob(); + BeanUtils.copyBeanProp(sysJob, context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES)); + try + { + before(context, sysJob); + if (sysJob != null) + { + doExecute(context, sysJob); + } + after(context, sysJob, null); + } + catch (Exception e) + { + log.error("任务执行异常 - :", e); + after(context, sysJob, e); + } + } + + /** + * 执行前 + * + * @param context 工作执行上下文对象 + * @param sysJob 系统计划任务 + */ + protected void before(JobExecutionContext context, SysJob sysJob) + { + threadLocal.set(new Date()); + } + + /** + * 执行后 + * + * @param context 工作执行上下文对象 + * @param sysJob 系统计划任务 + */ + protected void after(JobExecutionContext context, SysJob sysJob, Exception e) + { + Date startTime = threadLocal.get(); + threadLocal.remove(); + + final SysJobLog sysJobLog = new SysJobLog(); + sysJobLog.setJobName(sysJob.getJobName()); + sysJobLog.setJobGroup(sysJob.getJobGroup()); + sysJobLog.setInvokeTarget(sysJob.getInvokeTarget()); + sysJobLog.setStartTime(startTime); + sysJobLog.setStopTime(new Date()); + long runMs = sysJobLog.getStopTime().getTime() - sysJobLog.getStartTime().getTime(); + sysJobLog.setJobMessage(sysJobLog.getJobName() + " 总共耗时:" + runMs + "毫秒"); + if (e != null) + { + sysJobLog.setStatus(Constants.FAIL); + String errorMsg = StringUtils.substring(ExceptionUtil.getExceptionMessage(e), 0, 2000); + sysJobLog.setExceptionInfo(errorMsg); + } + else + { + sysJobLog.setStatus(Constants.SUCCESS); + } + + // 写入数据库当中 + SpringUtils.getBean(ISysJobLogService.class).addJobLog(sysJobLog); + } + + /** + * 执行方法,由子类重载 + * + * @param context 工作执行上下文对象 + * @param sysJob 系统计划任务 + * @throws Exception 执行过程中的异常 + */ + protected abstract void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception; +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/CronUtils.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/CronUtils.java new file mode 100644 index 0000000..dd53839 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/CronUtils.java @@ -0,0 +1,63 @@ +package com.ruoyi.quartz.util; + +import java.text.ParseException; +import java.util.Date; +import org.quartz.CronExpression; + +/** + * cron表达式工具类 + * + * @author ruoyi + * + */ +public class CronUtils +{ + /** + * 返回一个布尔值代表一个给定的Cron表达式的有效性 + * + * @param cronExpression Cron表达式 + * @return boolean 表达式是否有效 + */ + public static boolean isValid(String cronExpression) + { + return CronExpression.isValidExpression(cronExpression); + } + + /** + * 返回一个字符串值,表示该消息无效Cron表达式给出有效性 + * + * @param cronExpression Cron表达式 + * @return String 无效时返回表达式错误描述,如果有效返回null + */ + public static String getInvalidMessage(String cronExpression) + { + try + { + new CronExpression(cronExpression); + return null; + } + catch (ParseException pe) + { + return pe.getMessage(); + } + } + + /** + * 返回下一个执行时间根据给定的Cron表达式 + * + * @param cronExpression Cron表达式 + * @return Date 下次Cron表达式执行时间 + */ + public static Date getNextExecution(String cronExpression) + { + try + { + CronExpression cron = new CronExpression(cronExpression); + return cron.getNextValidTimeAfter(new Date(System.currentTimeMillis())); + } + catch (ParseException e) + { + throw new IllegalArgumentException(e.getMessage()); + } + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/JobInvokeUtil.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/JobInvokeUtil.java new file mode 100644 index 0000000..e3dc62c --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/JobInvokeUtil.java @@ -0,0 +1,182 @@ +package com.ruoyi.quartz.util; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.LinkedList; +import java.util.List; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.quartz.domain.SysJob; + +/** + * 任务执行工具 + * + * @author ruoyi + */ +public class JobInvokeUtil +{ + /** + * 执行方法 + * + * @param sysJob 系统任务 + */ + public static void invokeMethod(SysJob sysJob) throws Exception + { + String invokeTarget = sysJob.getInvokeTarget(); + String beanName = getBeanName(invokeTarget); + String methodName = getMethodName(invokeTarget); + List<Object[]> methodParams = getMethodParams(invokeTarget); + + if (!isValidClassName(beanName)) + { + Object bean = SpringUtils.getBean(beanName); + invokeMethod(bean, methodName, methodParams); + } + else + { + Object bean = Class.forName(beanName).getDeclaredConstructor().newInstance(); + invokeMethod(bean, methodName, methodParams); + } + } + + /** + * 调用任务方法 + * + * @param bean 目标对象 + * @param methodName 方法名称 + * @param methodParams 方法参数 + */ + private static void invokeMethod(Object bean, String methodName, List<Object[]> methodParams) + throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, + InvocationTargetException + { + if (StringUtils.isNotNull(methodParams) && methodParams.size() > 0) + { + Method method = bean.getClass().getMethod(methodName, getMethodParamsType(methodParams)); + method.invoke(bean, getMethodParamsValue(methodParams)); + } + else + { + Method method = bean.getClass().getMethod(methodName); + method.invoke(bean); + } + } + + /** + * 校验是否为为class包名 + * + * @param invokeTarget 名称 + * @return true是 false否 + */ + public static boolean isValidClassName(String invokeTarget) + { + return StringUtils.countMatches(invokeTarget, ".") > 1; + } + + /** + * 获取bean名称 + * + * @param invokeTarget 目标字符串 + * @return bean名称 + */ + public static String getBeanName(String invokeTarget) + { + String beanName = StringUtils.substringBefore(invokeTarget, "("); + return StringUtils.substringBeforeLast(beanName, "."); + } + + /** + * 获取bean方法 + * + * @param invokeTarget 目标字符串 + * @return method方法 + */ + public static String getMethodName(String invokeTarget) + { + String methodName = StringUtils.substringBefore(invokeTarget, "("); + return StringUtils.substringAfterLast(methodName, "."); + } + + /** + * 获取method方法参数相关列表 + * + * @param invokeTarget 目标字符串 + * @return method方法相关参数列表 + */ + public static List<Object[]> getMethodParams(String invokeTarget) + { + String methodStr = StringUtils.substringBetween(invokeTarget, "(", ")"); + if (StringUtils.isEmpty(methodStr)) + { + return null; + } + String[] methodParams = methodStr.split(",(?=([^\"']*[\"'][^\"']*[\"'])*[^\"']*$)"); + List<Object[]> classs = new LinkedList<>(); + for (int i = 0; i < methodParams.length; i++) + { + String str = StringUtils.trimToEmpty(methodParams[i]); + // String字符串类型,以'或"开头 + if (StringUtils.startsWithAny(str, "'", "\"")) + { + classs.add(new Object[] { StringUtils.substring(str, 1, str.length() - 1), String.class }); + } + // boolean布尔类型,等于true或者false + else if ("true".equalsIgnoreCase(str) || "false".equalsIgnoreCase(str)) + { + classs.add(new Object[] { Boolean.valueOf(str), Boolean.class }); + } + // long长整形,以L结尾 + else if (StringUtils.endsWith(str, "L")) + { + classs.add(new Object[] { Long.valueOf(StringUtils.substring(str, 0, str.length() - 1)), Long.class }); + } + // double浮点类型,以D结尾 + else if (StringUtils.endsWith(str, "D")) + { + classs.add(new Object[] { Double.valueOf(StringUtils.substring(str, 0, str.length() - 1)), Double.class }); + } + // 其他类型归类为整形 + else + { + classs.add(new Object[] { Integer.valueOf(str), Integer.class }); + } + } + return classs; + } + + /** + * 获取参数类型 + * + * @param methodParams 参数相关列表 + * @return 参数类型列表 + */ + public static Class<?>[] getMethodParamsType(List<Object[]> methodParams) + { + Class<?>[] classs = new Class<?>[methodParams.size()]; + int index = 0; + for (Object[] os : methodParams) + { + classs[index] = (Class<?>) os[1]; + index++; + } + return classs; + } + + /** + * 获取参数值 + * + * @param methodParams 参数相关列表 + * @return 参数值列表 + */ + public static Object[] getMethodParamsValue(List<Object[]> methodParams) + { + Object[] classs = new Object[methodParams.size()]; + int index = 0; + for (Object[] os : methodParams) + { + classs[index] = (Object) os[0]; + index++; + } + return classs; + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzDisallowConcurrentExecution.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzDisallowConcurrentExecution.java new file mode 100644 index 0000000..5e13558 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzDisallowConcurrentExecution.java @@ -0,0 +1,21 @@ +package com.ruoyi.quartz.util; + +import org.quartz.DisallowConcurrentExecution; +import org.quartz.JobExecutionContext; +import com.ruoyi.quartz.domain.SysJob; + +/** + * 定时任务处理(禁止并发执行) + * + * @author ruoyi + * + */ +@DisallowConcurrentExecution +public class QuartzDisallowConcurrentExecution extends AbstractQuartzJob +{ + @Override + protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception + { + JobInvokeUtil.invokeMethod(sysJob); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzJobExecution.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzJobExecution.java new file mode 100644 index 0000000..e975326 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzJobExecution.java @@ -0,0 +1,19 @@ +package com.ruoyi.quartz.util; + +import org.quartz.JobExecutionContext; +import com.ruoyi.quartz.domain.SysJob; + +/** + * 定时任务处理(允许并发执行) + * + * @author ruoyi + * + */ +public class QuartzJobExecution extends AbstractQuartzJob +{ + @Override + protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception + { + JobInvokeUtil.invokeMethod(sysJob); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleUtils.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleUtils.java new file mode 100644 index 0000000..21fedae --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleUtils.java @@ -0,0 +1,141 @@ +package com.ruoyi.quartz.util; + +import org.quartz.CronScheduleBuilder; +import org.quartz.CronTrigger; +import org.quartz.Job; +import org.quartz.JobBuilder; +import org.quartz.JobDetail; +import org.quartz.JobKey; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.TriggerBuilder; +import org.quartz.TriggerKey; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.ScheduleConstants; +import com.ruoyi.common.exception.job.TaskException; +import com.ruoyi.common.exception.job.TaskException.Code; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.quartz.domain.SysJob; + +/** + * 定时任务工具类 + * + * @author ruoyi + * + */ +public class ScheduleUtils +{ + /** + * 得到quartz任务类 + * + * @param sysJob 执行计划 + * @return 具体执行任务类 + */ + private static Class<? extends Job> getQuartzJobClass(SysJob sysJob) + { + boolean isConcurrent = "0".equals(sysJob.getConcurrent()); + return isConcurrent ? QuartzJobExecution.class : QuartzDisallowConcurrentExecution.class; + } + + /** + * 构建任务触发对象 + */ + public static TriggerKey getTriggerKey(Long jobId, String jobGroup) + { + return TriggerKey.triggerKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup); + } + + /** + * 构建任务键对象 + */ + public static JobKey getJobKey(Long jobId, String jobGroup) + { + return JobKey.jobKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup); + } + + /** + * 创建定时任务 + */ + public static void createScheduleJob(Scheduler scheduler, SysJob job) throws SchedulerException, TaskException + { + Class<? extends Job> jobClass = getQuartzJobClass(job); + // 构建job信息 + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(getJobKey(jobId, jobGroup)).build(); + + // 表达式调度构建器 + CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression()); + cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder); + + // 按新的cronExpression表达式构建一个新的trigger + CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobId, jobGroup)) + .withSchedule(cronScheduleBuilder).build(); + + // 放入参数,运行时的方法可以获取 + jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job); + + // 判断是否存在 + if (scheduler.checkExists(getJobKey(jobId, jobGroup))) + { + // 防止创建时存在数据问题 先移除,然后在执行创建操作 + scheduler.deleteJob(getJobKey(jobId, jobGroup)); + } + + // 判断任务是否过期 + if (StringUtils.isNotNull(CronUtils.getNextExecution(job.getCronExpression()))) + { + // 执行调度任务 + scheduler.scheduleJob(jobDetail, trigger); + } + + // 暂停任务 + if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())) + { + scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup)); + } + } + + /** + * 设置定时任务策略 + */ + public static CronScheduleBuilder handleCronScheduleMisfirePolicy(SysJob job, CronScheduleBuilder cb) + throws TaskException + { + switch (job.getMisfirePolicy()) + { + case ScheduleConstants.MISFIRE_DEFAULT: + return cb; + case ScheduleConstants.MISFIRE_IGNORE_MISFIRES: + return cb.withMisfireHandlingInstructionIgnoreMisfires(); + case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED: + return cb.withMisfireHandlingInstructionFireAndProceed(); + case ScheduleConstants.MISFIRE_DO_NOTHING: + return cb.withMisfireHandlingInstructionDoNothing(); + default: + throw new TaskException("The task misfire policy '" + job.getMisfirePolicy() + + "' cannot be used in cron schedule tasks", Code.CONFIG_ERROR); + } + } + + /** + * 检查包名是否为白名单配置 + * + * @param invokeTarget 目标字符串 + * @return 结果 + */ + public static boolean whiteList(String invokeTarget) + { + String packageName = StringUtils.substringBefore(invokeTarget, "("); + int count = StringUtils.countMatches(packageName, "."); + if (count > 1) + { + return StringUtils.containsAnyIgnoreCase(invokeTarget, Constants.JOB_WHITELIST_STR); + } + Object obj = SpringUtils.getBean(StringUtils.split(invokeTarget, ".")[0]); + String beanPackageName = obj.getClass().getPackage().getName(); + return StringUtils.containsAnyIgnoreCase(beanPackageName, Constants.JOB_WHITELIST_STR) + && !StringUtils.containsAnyIgnoreCase(beanPackageName, Constants.JOB_ERROR_STR); + } +} diff --git a/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobLogMapper.xml b/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobLogMapper.xml new file mode 100644 index 0000000..e608e42 --- /dev/null +++ b/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobLogMapper.xml @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.quartz.mapper.SysJobLogMapper"> + + <resultMap type="SysJobLog" id="SysJobLogResult"> + <id property="jobLogId" column="job_log_id" /> + <result property="jobName" column="job_name" /> + <result property="jobGroup" column="job_group" /> + <result property="invokeTarget" column="invoke_target" /> + <result property="jobMessage" column="job_message" /> + <result property="status" column="status" /> + <result property="exceptionInfo" column="exception_info" /> + <result property="createTime" column="create_time" /> + </resultMap> + + <sql id="selectJobLogVo"> + select job_log_id, job_name, job_group, invoke_target, job_message, status, exception_info, create_time + from sys_job_log + </sql> + + <select id="selectJobLogList" parameterType="SysJobLog" resultMap="SysJobLogResult"> + <include refid="selectJobLogVo"/> + <where> + <if test="jobName != null and jobName != ''"> + AND job_name like concat('%', #{jobName}, '%') + </if> + <if test="jobGroup != null and jobGroup != ''"> + AND job_group = #{jobGroup} + </if> + <if test="status != null and status != ''"> + AND status = #{status} + </if> + <if test="invokeTarget != null and invokeTarget != ''"> + AND invoke_target like concat('%', #{invokeTarget}, '%') + </if> + <if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 --> + and date_format(create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d') + </if> + <if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 --> + and date_format(create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d') + </if> + </where> + </select> + + <select id="selectJobLogAll" resultMap="SysJobLogResult"> + <include refid="selectJobLogVo"/> + </select> + + <select id="selectJobLogById" parameterType="Long" resultMap="SysJobLogResult"> + <include refid="selectJobLogVo"/> + where job_log_id = #{jobLogId} + </select> + + <delete id="deleteJobLogById" parameterType="Long"> + delete from sys_job_log where job_log_id = #{jobLogId} + </delete> + + <delete id="deleteJobLogByIds" parameterType="Long"> + delete from sys_job_log where job_log_id in + <foreach collection="array" item="jobLogId" open="(" separator="," close=")"> + #{jobLogId} + </foreach> + </delete> + + <update id="cleanJobLog"> + truncate table sys_job_log + </update> + + <insert id="insertJobLog" parameterType="SysJobLog"> + insert into sys_job_log( + <if test="jobLogId != null and jobLogId != 0">job_log_id,</if> + <if test="jobName != null and jobName != ''">job_name,</if> + <if test="jobGroup != null and jobGroup != ''">job_group,</if> + <if test="invokeTarget != null and invokeTarget != ''">invoke_target,</if> + <if test="jobMessage != null and jobMessage != ''">job_message,</if> + <if test="status != null and status != ''">status,</if> + <if test="exceptionInfo != null and exceptionInfo != ''">exception_info,</if> + create_time + )values( + <if test="jobLogId != null and jobLogId != 0">#{jobLogId},</if> + <if test="jobName != null and jobName != ''">#{jobName},</if> + <if test="jobGroup != null and jobGroup != ''">#{jobGroup},</if> + <if test="invokeTarget != null and invokeTarget != ''">#{invokeTarget},</if> + <if test="jobMessage != null and jobMessage != ''">#{jobMessage},</if> + <if test="status != null and status != ''">#{status},</if> + <if test="exceptionInfo != null and exceptionInfo != ''">#{exceptionInfo},</if> + sysdate() + ) + </insert> + +</mapper> \ No newline at end of file diff --git a/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobMapper.xml b/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobMapper.xml new file mode 100644 index 0000000..5605c44 --- /dev/null +++ b/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobMapper.xml @@ -0,0 +1,111 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.quartz.mapper.SysJobMapper"> + + <resultMap type="SysJob" id="SysJobResult"> + <id property="jobId" column="job_id" /> + <result property="jobName" column="job_name" /> + <result property="jobGroup" column="job_group" /> + <result property="invokeTarget" column="invoke_target" /> + <result property="cronExpression" column="cron_expression" /> + <result property="misfirePolicy" column="misfire_policy" /> + <result property="concurrent" column="concurrent" /> + <result property="status" column="status" /> + <result property="createBy" column="create_by" /> + <result property="createTime" column="create_time" /> + <result property="updateBy" column="update_by" /> + <result property="updateTime" column="update_time" /> + <result property="remark" column="remark" /> + </resultMap> + + <sql id="selectJobVo"> + select job_id, job_name, job_group, invoke_target, cron_expression, misfire_policy, concurrent, status, create_by, create_time, remark + from sys_job + </sql> + + <select id="selectJobList" parameterType="SysJob" resultMap="SysJobResult"> + <include refid="selectJobVo"/> + <where> + <if test="jobName != null and jobName != ''"> + AND job_name like concat('%', #{jobName}, '%') + </if> + <if test="jobGroup != null and jobGroup != ''"> + AND job_group = #{jobGroup} + </if> + <if test="status != null and status != ''"> + AND status = #{status} + </if> + <if test="invokeTarget != null and invokeTarget != ''"> + AND invoke_target like concat('%', #{invokeTarget}, '%') + </if> + </where> + </select> + + <select id="selectJobAll" resultMap="SysJobResult"> + <include refid="selectJobVo"/> + </select> + + <select id="selectJobById" parameterType="Long" resultMap="SysJobResult"> + <include refid="selectJobVo"/> + where job_id = #{jobId} + </select> + + <delete id="deleteJobById" parameterType="Long"> + delete from sys_job where job_id = #{jobId} + </delete> + + <delete id="deleteJobByIds" parameterType="Long"> + delete from sys_job where job_id in + <foreach collection="array" item="jobId" open="(" separator="," close=")"> + #{jobId} + </foreach> + </delete> + + <update id="updateJob" parameterType="SysJob"> + update sys_job + <set> + <if test="jobName != null and jobName != ''">job_name = #{jobName},</if> + <if test="jobGroup != null and jobGroup != ''">job_group = #{jobGroup},</if> + <if test="invokeTarget != null and invokeTarget != ''">invoke_target = #{invokeTarget},</if> + <if test="cronExpression != null and cronExpression != ''">cron_expression = #{cronExpression},</if> + <if test="misfirePolicy != null and misfirePolicy != ''">misfire_policy = #{misfirePolicy},</if> + <if test="concurrent != null and concurrent != ''">concurrent = #{concurrent},</if> + <if test="status !=null">status = #{status},</if> + <if test="remark != null and remark != ''">remark = #{remark},</if> + <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if> + update_time = sysdate() + </set> + where job_id = #{jobId} + </update> + + <insert id="insertJob" parameterType="SysJob" useGeneratedKeys="true" keyProperty="jobId"> + insert into sys_job( + <if test="jobId != null and jobId != 0">job_id,</if> + <if test="jobName != null and jobName != ''">job_name,</if> + <if test="jobGroup != null and jobGroup != ''">job_group,</if> + <if test="invokeTarget != null and invokeTarget != ''">invoke_target,</if> + <if test="cronExpression != null and cronExpression != ''">cron_expression,</if> + <if test="misfirePolicy != null and misfirePolicy != ''">misfire_policy,</if> + <if test="concurrent != null and concurrent != ''">concurrent,</if> + <if test="status != null and status != ''">status,</if> + <if test="remark != null and remark != ''">remark,</if> + <if test="createBy != null and createBy != ''">create_by,</if> + create_time + )values( + <if test="jobId != null and jobId != 0">#{jobId},</if> + <if test="jobName != null and jobName != ''">#{jobName},</if> + <if test="jobGroup != null and jobGroup != ''">#{jobGroup},</if> + <if test="invokeTarget != null and invokeTarget != ''">#{invokeTarget},</if> + <if test="cronExpression != null and cronExpression != ''">#{cronExpression},</if> + <if test="misfirePolicy != null and misfirePolicy != ''">#{misfirePolicy},</if> + <if test="concurrent != null and concurrent != ''">#{concurrent},</if> + <if test="status != null and status != ''">#{status},</if> + <if test="remark != null and remark != ''">#{remark},</if> + <if test="createBy != null and createBy != ''">#{createBy},</if> + sysdate() + ) + </insert> + +</mapper> \ No newline at end of file diff --git a/ruoyi-system/pom.xml b/ruoyi-system/pom.xml new file mode 100644 index 0000000..380aded --- /dev/null +++ b/ruoyi-system/pom.xml @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>zhengshengxin</artifactId> + <groupId>com.ruoyi</groupId> + <version>3.8.6</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>ruoyi-system</artifactId> + + <description> + system系统模块 + </description> + + <dependencies> + <dependency> + <groupId>com.qcloud</groupId> + <artifactId>cos_api</artifactId> + <version>5.6.227</version> + </dependency> + <!-- 通用工具--> + <dependency> + <groupId>com.ruoyi</groupId> + <artifactId>ruoyi-common</artifactId> +<!-- <exclusions>--> +<!-- <exclusion>--> +<!-- <groupId>com.github.pagehelper</groupId>--> +<!-- <artifactId>pagehelper-spring-boot-starter</artifactId>--> +<!-- </exclusion>--> +<!-- </exclusions>--> + </dependency> + <dependency> + <groupId>com.ruoyi</groupId> + <artifactId>ruoyi-quartz</artifactId> + </dependency> + + <dependency> + <groupId>cn.afterturn</groupId> + <artifactId>easypoi-spring-boot-starter</artifactId> + <version>4.0.0</version> + <exclusions> + <exclusion> + <artifactId>guava</artifactId> + <groupId>com.google.guava</groupId> + </exclusion> + </exclusions> + </dependency> + + <!--mybatis-plus--> + <dependency> + <groupId>com.baomidou</groupId> + <artifactId>mybatis-plus-boot-starter</artifactId> + <version>3.5.2</version> + </dependency> + <!--集成EasyExcel --> + <dependency> + <groupId>com.alibaba</groupId> + <artifactId>easyexcel</artifactId> + <version>2.1.6</version> + </dependency> + + +<!-- <dependency>--> +<!-- <groupId>org.apache.httpcomponents</groupId>--> +<!-- <artifactId>httpcore</artifactId>--> +<!-- <version>4.3.2</version>--> +<!-- </dependency>--> + +<!-- <dependency>--> +<!-- <groupId>org.springframework</groupId>--> +<!-- <artifactId>spring-test</artifactId>--> +<!-- <version>5.1.3.RELEASE</version>--> +<!-- </dependency>--> + + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-test</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.tomcat.embed</groupId> + <artifactId>tomcat-embed-core</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.datatype</groupId> + <artifactId>jackson-datatype-jsr310</artifactId> + </dependency> + </dependencies> + +</project> \ No newline at end of file diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/bo/DeployBO.java b/ruoyi-system/src/main/java/com/ruoyi/system/bo/DeployBO.java new file mode 100644 index 0000000..7adf52f --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/bo/DeployBO.java @@ -0,0 +1,15 @@ +package com.ruoyi.system.bo; + +import com.aizuda.bpm.engine.model.NodeModel; +import lombok.Data; + +@Data +public class DeployBO { + private String key; + private String name; + private String instanceUrl; + /** + * 流程定义 + */ + private NodeModel nodeConfig; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessAgreeBO.java b/ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessAgreeBO.java new file mode 100644 index 0000000..b52551e --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessAgreeBO.java @@ -0,0 +1,36 @@ +package com.ruoyi.system.bo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 流程审批请求参数 同意 + * + * <p> + * 尊重知识产权,不允许非法使用,后果自负 + * </p> + * + */ +@Data +public class ProcessAgreeBO { + + /** + * 任务id + */ + private String taskId; + + /** + * 理由 + */ + private String remark; + /** + * 图片 + */ + private String pictures; + /** + * 审批用户id + */ + @ApiModelProperty(value = "前端忽略") + private Long userId; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessCreateBO.java b/ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessCreateBO.java new file mode 100644 index 0000000..ed40541 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessCreateBO.java @@ -0,0 +1,21 @@ +package com.ruoyi.system.bo; + +import lombok.Data; + +@Data +public class ProcessCreateBO { + + + /** + * 模版名称 + */ + private String templateName; + + /** + * json流程模版 + */ + private String process; + + private String remark; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessModuleUpdateBO.java b/ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessModuleUpdateBO.java new file mode 100644 index 0000000..6510129 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessModuleUpdateBO.java @@ -0,0 +1,19 @@ +package com.ruoyi.system.bo; + +import lombok.Data; + +@Data +public class ProcessModuleUpdateBO { + + /** + * 模块id + */ + private String id; + + /** + * 流程id + */ + private String templateId; + private String remark; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessRefuseBO.java b/ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessRefuseBO.java new file mode 100644 index 0000000..6b0c34e --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessRefuseBO.java @@ -0,0 +1,30 @@ +package com.ruoyi.system.bo; + +import lombok.Data; + +/** + * 流程审批请求参数 拒绝 + * + * <p> + * 尊重知识产权,不允许非法使用,后果自负 + * </p> + * + */ +@Data +public class ProcessRefuseBO { + + /** + * 任务id + */ + private String taskId; + + /** + * 理由 + */ + private String remark; + /** + * 图片 + */ + private String pictures; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessStartBO.java b/ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessStartBO.java new file mode 100644 index 0000000..6bd43bc --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessStartBO.java @@ -0,0 +1,57 @@ +package com.ruoyi.system.bo; + +import lombok.Data; + +import java.util.Map; + +@Data +public class ProcessStartBO { + + /** + * 阶段类型 + * 1入户调查 + * 2价格评估 + * 3协议签订 + * 4资金管理-预算资金 + * 5住宅临时安置补助费 + * 6停产停业经济损失补助费 + * 7安置情况 + */ + private String category; + + /** + * 任务中心-项目名称 + */ + private String name; + + /** + * 模块名称 + */ + private String moduleName; + + /** + * 系统摘要 + * 入户调查摘要:【镇/街】【征收实施单位】【调查户数】 + * 价格评估摘要:【镇/街】【征收实施单位】【价格评估合计】 + * 协议签订摘要:【镇/街】【征收实施单位】【权利人】【协议类型】 + * 预算资金摘要:【镇/街】【征收实施单位】【预算金额】 + * 住宅临时安置补助费摘要:【镇/街】【征收实施单位】【开始时间-截止时间】【申请金额】 + * 停产停业经济损失补助费摘要:【镇/街】【征收实施单位】【开始时间-截止时间】【申请金额】 + * 安置情况摘要:【镇/街】【征收实施单位】【批次名称】【安置类型】 + */ + private String remark; + + + /** + * 类型 1集体 2国有 + */ + private Integer type = 2; + + /** + * 变量:流程完成后需要修改状态的表id信息 + * 例如: + * variable.put("objectId",12); + */ + private Map<String, Object> variable; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessTaskListBO.java b/ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessTaskListBO.java new file mode 100644 index 0000000..0649534 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessTaskListBO.java @@ -0,0 +1,129 @@ +package com.ruoyi.system.bo; + +import com.ruoyi.common.core.domain.BasePage; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +public class ProcessTaskListBO extends BasePage { + /** + * 任务名称 + */ + @ApiModelProperty(value = "任务名称") + private String name; + + /** + * 任务名称 + */ + @ApiModelProperty(value = "任务名称") + private String moduleName; +// /** +// * 流程实例状态( 0,审批中 1,审批通过 2,审批拒绝 3) +// */ +// private Integer instanceState; + /** + * 创建人 + */ + @ApiModelProperty(value = "创建人") + private String createBy; + + @ApiModelProperty(value = "乙方名称") + private String partyTwoName; + @ApiModelProperty(value = "合同编号") + private String contractNumber; + @ApiModelProperty(value = "合同名称") + private String contractName; + @ApiModelProperty(value = "合同状态") + private String status; + @ApiModelProperty(value = "前端忽略") + private List<String> instanceIds; + + @ApiModelProperty(value = "时间筛选 1=1天 2=7天 3=30天") + private Integer timeType; + + @ApiModelProperty(value = "排序 1=最早到达 2=最新到达") + private Integer sortBy=2; + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getModuleName() { + return moduleName; + } + + public void setModuleName(String moduleName) { + this.moduleName = moduleName; + } + + public String getCreateBy() { + return createBy; + } + + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public String getPartyTwoName() { + return partyTwoName; + } + + public void setPartyTwoName(String partyTwoName) { + this.partyTwoName = partyTwoName; + } + + public String getContractNumber() { + return contractNumber; + } + + public void setContractNumber(String contractNumber) { + this.contractNumber = contractNumber; + } + + public String getContractName() { + return contractName; + } + + public void setContractName(String contractName) { + this.contractName = contractName; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public List<String> getInstanceIds() { + return instanceIds; + } + + public void setInstanceIds(List<String> instanceIds) { + this.instanceIds = instanceIds; + } + + public Integer getTimeType() { + return timeType; + } + + public void setTimeType(Integer timeType) { + this.timeType = timeType; + } + + public Integer getSortBy() { + return sortBy; + } + + public void setSortBy(Integer sortBy) { + this.sortBy = sortBy; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessTemplatePageBO.java b/ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessTemplatePageBO.java new file mode 100644 index 0000000..9502e83 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessTemplatePageBO.java @@ -0,0 +1,12 @@ +package com.ruoyi.system.bo; + +import lombok.Data; + +@Data +public class ProcessTemplatePageBO { + + private Integer currentPage; + private Integer pageSize; + private String name; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessUpdateBO.java b/ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessUpdateBO.java new file mode 100644 index 0000000..09ac837 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/bo/ProcessUpdateBO.java @@ -0,0 +1,22 @@ +package com.ruoyi.system.bo; + +import lombok.Data; + +@Data +public class ProcessUpdateBO { + + private String id; + + /** + * 模版名称 + */ + private String templateName; + + /** + * json流程模版 + */ + private String process; + + private String remark; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/code/SubmitTemplateReg.java b/ruoyi-system/src/main/java/com/ruoyi/system/code/SubmitTemplateReg.java new file mode 100644 index 0000000..8987982 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/code/SubmitTemplateReg.java @@ -0,0 +1,40 @@ +package com.ruoyi.system.code; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +@Data +@ApiModel(value = "短信验证码实体") +public class SubmitTemplateReg implements Serializable { + + @ApiModelProperty(value = "企业名称") + private String ecName; + + @ApiModelProperty(value = "账号用户名") + private String apId; + + @ApiModelProperty(value = "模板ID") + private String templateId; + + @ApiModelProperty(value = "收信手机号码") + private String mobiles; + + @ApiModelProperty(value = "模板变量。格式:[“param1”,“param2”],无变量模板填['']") + private String params; + + @ApiModelProperty(value = "签名编码") + private String sign; + + @ApiModelProperty(value = "扩展码") + private String addSerial; + + @ApiModelProperty(value = "key值") + private String secretKey; + + @ApiModelProperty(value = "参数校验序列") + private String mac; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysCache.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysCache.java new file mode 100644 index 0000000..83f0703 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysCache.java @@ -0,0 +1,81 @@ +package com.ruoyi.system.domain; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 缓存信息 + * + * @author ruoyi + */ +public class SysCache +{ + /** 缓存名称 */ + private String cacheName = ""; + + /** 缓存键名 */ + private String cacheKey = ""; + + /** 缓存内容 */ + private String cacheValue = ""; + + /** 备注 */ + private String remark = ""; + + public SysCache() + { + + } + + public SysCache(String cacheName, String remark) + { + this.cacheName = cacheName; + this.remark = remark; + } + + public SysCache(String cacheName, String cacheKey, String cacheValue) + { + this.cacheName = StringUtils.replace(cacheName, ":", ""); + this.cacheKey = StringUtils.replace(cacheKey, cacheName, ""); + this.cacheValue = cacheValue; + } + + public String getCacheName() + { + return cacheName; + } + + public void setCacheName(String cacheName) + { + this.cacheName = cacheName; + } + + public String getCacheKey() + { + return cacheKey; + } + + public void setCacheKey(String cacheKey) + { + this.cacheKey = cacheKey; + } + + public String getCacheValue() + { + return cacheValue; + } + + public void setCacheValue(String cacheValue) + { + this.cacheValue = cacheValue; + } + + public String getRemark() + { + return remark; + } + + public void setRemark(String remark) + { + this.remark = remark; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysConfig.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysConfig.java new file mode 100644 index 0000000..146bcd6 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysConfig.java @@ -0,0 +1,111 @@ +package com.ruoyi.system.domain; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 参数配置表 sys_config + * + * @author ruoyi + */ +public class SysConfig extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 参数主键 */ + @Excel(name = "参数主键") + private Long configId; + + /** 参数名称 */ + @Excel(name = "参数名称") + private String configName; + + /** 参数键名 */ + @Excel(name = "参数键名") + private String configKey; + + /** 参数键值 */ + @Excel(name = "参数键值") + private String configValue; + + /** 系统内置(Y是 N否) */ + @Excel(name = "系统内置") + private String configType; + + public Long getConfigId() + { + return configId; + } + + public void setConfigId(Long configId) + { + this.configId = configId; + } + + @NotBlank(message = "参数名称不能为空") + @Size(min = 0, max = 100, message = "参数名称不能超过100个字符") + public String getConfigName() + { + return configName; + } + + public void setConfigName(String configName) + { + this.configName = configName; + } + + @NotBlank(message = "参数键名长度不能为空") + @Size(min = 0, max = 100, message = "参数键名长度不能超过100个字符") + public String getConfigKey() + { + return configKey; + } + + public void setConfigKey(String configKey) + { + this.configKey = configKey; + } + + @NotBlank(message = "参数键值不能为空") + @Size(min = 0, max = 500, message = "参数键值长度不能超过500个字符") + public String getConfigValue() + { + return configValue; + } + + public void setConfigValue(String configValue) + { + this.configValue = configValue; + } + + public String getConfigType() + { + return configType; + } + + public void setConfigType(String configType) + { + this.configType = configType; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("configId", getConfigId()) + .append("configName", getConfigName()) + .append("configKey", getConfigKey()) + .append("configValue", getConfigValue()) + .append("configType", getConfigType()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java new file mode 100644 index 0000000..89adb21 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java @@ -0,0 +1,144 @@ +package com.ruoyi.system.domain; + +import java.util.Date; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 系统访问记录表 sys_logininfor + * + * @author ruoyi + */ +public class SysLogininfor extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** ID */ + @Excel(name = "序号") + private Long infoId; + + /** 用户账号 */ + @Excel(name = "用户账号") + private String userName; + + /** 登录状态 0成功 1失败 */ + @Excel(name = "登录状态") + private String status; + + /** 登录IP地址 */ + @Excel(name = "登录地址") + private String ipaddr; + + /** 登录地点 */ + @Excel(name = "登录地点") + private String loginLocation; + + /** 浏览器类型 */ + @Excel(name = "浏览器") + private String browser; + + /** 操作系统 */ + @Excel(name = "操作系统") + private String os; + + /** 提示消息 */ + @Excel(name = "提示消息") + private String msg; + + /** 访问时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "访问时间", width = 30, exportFormat = "yyyy-MM-dd HH:mm:ss") + private Date loginTime; + + public Long getInfoId() + { + return infoId; + } + + public void setInfoId(Long infoId) + { + this.infoId = infoId; + } + + public String getUserName() + { + return userName; + } + + public void setUserName(String userName) + { + this.userName = userName; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getIpaddr() + { + return ipaddr; + } + + public void setIpaddr(String ipaddr) + { + this.ipaddr = ipaddr; + } + + public String getLoginLocation() + { + return loginLocation; + } + + public void setLoginLocation(String loginLocation) + { + this.loginLocation = loginLocation; + } + + public String getBrowser() + { + return browser; + } + + public void setBrowser(String browser) + { + this.browser = browser; + } + + public String getOs() + { + return os; + } + + public void setOs(String os) + { + this.os = os; + } + + public String getMsg() + { + return msg; + } + + public void setMsg(String msg) + { + this.msg = msg; + } + + public Date getLoginTime() + { + return loginTime; + } + + public void setLoginTime(Date loginTime) + { + this.loginTime = loginTime; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysNotice.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysNotice.java new file mode 100644 index 0000000..8c07a54 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysNotice.java @@ -0,0 +1,102 @@ +package com.ruoyi.system.domain; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.xss.Xss; + +/** + * 通知公告表 sys_notice + * + * @author ruoyi + */ +public class SysNotice extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 公告ID */ + private Long noticeId; + + /** 公告标题 */ + private String noticeTitle; + + /** 公告类型(1通知 2公告) */ + private String noticeType; + + /** 公告内容 */ + private String noticeContent; + + /** 公告状态(0正常 1关闭) */ + private String status; + + public Long getNoticeId() + { + return noticeId; + } + + public void setNoticeId(Long noticeId) + { + this.noticeId = noticeId; + } + + public void setNoticeTitle(String noticeTitle) + { + this.noticeTitle = noticeTitle; + } + + @Xss(message = "公告标题不能包含脚本字符") + @NotBlank(message = "公告标题不能为空") + @Size(min = 0, max = 50, message = "公告标题不能超过50个字符") + public String getNoticeTitle() + { + return noticeTitle; + } + + public void setNoticeType(String noticeType) + { + this.noticeType = noticeType; + } + + public String getNoticeType() + { + return noticeType; + } + + public void setNoticeContent(String noticeContent) + { + this.noticeContent = noticeContent; + } + + public String getNoticeContent() + { + return noticeContent; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getStatus() + { + return status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("noticeId", getNoticeId()) + .append("noticeTitle", getNoticeTitle()) + .append("noticeType", getNoticeType()) + .append("noticeContent", getNoticeContent()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java new file mode 100644 index 0000000..19b3ca0 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java @@ -0,0 +1,322 @@ +package com.ruoyi.system.domain; + +import java.io.Serializable; +import java.util.Date; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 操作日志记录表 oper_log + * + * @author ruoyi + */ +public class SysOperLog implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 日志主键 */ + @Excel(name = "操作序号") + private Long operId; + + /** 操作模块 */ + @Excel(name = "操作模块") + private String title; + + /** 业务类型(0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据) */ + @Excel(name = "业务类型") + private Integer businessType; + + /** 业务类型数组 */ + private Integer[] businessTypes; + + /** 请求方法 */ + @Excel(name = "请求方法") + private String method; + + /** 请求方式 */ + @Excel(name = "请求方式") + private String requestMethod; + + /** 操作类别(0其它 1后台用户 2手机端用户) */ + @Excel(name = "操作类别") + private Integer operatorType; + + /** 操作人员 */ + @Excel(name = "操作人员") + private String operName; + + /** 部门名称 */ + @Excel(name = "部门名称") + private String deptName; + + /** 请求url */ + @Excel(name = "请求地址") + private String operUrl; + + /** 操作地址 */ + @Excel(name = "操作地址") + private String operIp; + + /** 操作地点 */ + @Excel(name = "操作地点") + private String operLocation; + + /** 请求参数 */ + @Excel(name = "请求参数") + private String operParam; + + /** 返回参数 */ + @Excel(name = "返回参数") + private String jsonResult; + + /** 操作状态(0正常 1异常) */ + @Excel(name = "状态") + private Integer status; + + /** 错误消息 */ + @Excel(name = "错误消息") + private String errorMsg; + + /** 操作时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "操作时间", width = 30, exportFormat = "yyyy-MM-dd HH:mm:ss") + private Date operTime; + + /** 消耗时间 */ + @Excel(name = "消耗时间", suffix = "毫秒") + private Long costTime; + + /** 公司名称 */ + @Excel(name = "公司名称") + private String companyName; + @Excel(name = "角色名称") + private String roleName; + @Excel(name = "手机号") + private String phonenumber; + @Excel(name = "用户id") + private Long userId; + @Excel(name = "操作人员名称") + private String nickName; + + public String getNickName() { + return nickName; + } + + public void setNickName(String nickName) { + this.nickName = nickName; + } + + public String getCompanyName() { + return companyName; + } + + public void setCompanyName(String companyName) { + this.companyName = companyName; + } + + public String getRoleName() { + return roleName; + } + + public void setRoleName(String roleName) { + this.roleName = roleName; + } + + public String getPhonenumber() { + return phonenumber; + } + + public void setPhonenumber(String phonenumber) { + this.phonenumber = phonenumber; + } + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public Long getOperId() + { + return operId; + } + + public void setOperId(Long operId) + { + this.operId = operId; + } + + public String getTitle() + { + return title; + } + + public void setTitle(String title) + { + this.title = title; + } + + public Integer getBusinessType() + { + return businessType; + } + + public void setBusinessType(Integer businessType) + { + this.businessType = businessType; + } + + public Integer[] getBusinessTypes() + { + return businessTypes; + } + + public void setBusinessTypes(Integer[] businessTypes) + { + this.businessTypes = businessTypes; + } + + public String getMethod() + { + return method; + } + + public void setMethod(String method) + { + this.method = method; + } + + public String getRequestMethod() + { + return requestMethod; + } + + public void setRequestMethod(String requestMethod) + { + this.requestMethod = requestMethod; + } + + public Integer getOperatorType() + { + return operatorType; + } + + public void setOperatorType(Integer operatorType) + { + this.operatorType = operatorType; + } + + public String getOperName() + { + return operName; + } + + public void setOperName(String operName) + { + this.operName = operName; + } + + public String getDeptName() + { + return deptName; + } + + public void setDeptName(String deptName) + { + this.deptName = deptName; + } + + public String getOperUrl() + { + return operUrl; + } + + public void setOperUrl(String operUrl) + { + this.operUrl = operUrl; + } + + public String getOperIp() + { + return operIp; + } + + public void setOperIp(String operIp) + { + this.operIp = operIp; + } + + public String getOperLocation() + { + return operLocation; + } + + public void setOperLocation(String operLocation) + { + this.operLocation = operLocation; + } + + public String getOperParam() + { + return operParam; + } + + public void setOperParam(String operParam) + { + this.operParam = operParam; + } + + public String getJsonResult() + { + return jsonResult; + } + + public void setJsonResult(String jsonResult) + { + this.jsonResult = jsonResult; + } + + public Integer getStatus() + { + return status; + } + + public void setStatus(Integer status) + { + this.status = status; + } + + public String getErrorMsg() + { + return errorMsg; + } + + public void setErrorMsg(String errorMsg) + { + this.errorMsg = errorMsg; + } + + public Date getOperTime() + { + return operTime; + } + + public void setOperTime(Date operTime) + { + this.operTime = operTime; + } + + public Long getCostTime() + { + return costTime; + } + + public void setCostTime(Long costTime) + { + this.costTime = costTime; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysPost.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysPost.java new file mode 100644 index 0000000..4111991 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysPost.java @@ -0,0 +1,124 @@ +package com.ruoyi.system.domain; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 岗位表 sys_post + * + * @author ruoyi + */ +public class SysPost extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 岗位序号 */ + @Excel(name = "岗位序号") + private Long postId; + + /** 岗位编码 */ + @Excel(name = "岗位编码") + private String postCode; + + /** 岗位名称 */ + @Excel(name = "岗位名称") + private String postName; + + /** 岗位排序 */ + @Excel(name = "岗位排序") + private Integer postSort; + + /** 状态(0正常 1停用) */ + @Excel(name = "状态") + private String status; + + /** 用户是否存在此岗位标识 默认不存在 */ + private boolean flag = false; + + public Long getPostId() + { + return postId; + } + + public void setPostId(Long postId) + { + this.postId = postId; + } + + @NotBlank(message = "岗位编码不能为空") + @Size(min = 0, max = 64, message = "岗位编码长度不能超过64个字符") + public String getPostCode() + { + return postCode; + } + + public void setPostCode(String postCode) + { + this.postCode = postCode; + } + + @NotBlank(message = "岗位名称不能为空") + @Size(min = 0, max = 50, message = "岗位名称长度不能超过50个字符") + public String getPostName() + { + return postName; + } + + public void setPostName(String postName) + { + this.postName = postName; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getPostSort() + { + return postSort; + } + + public void setPostSort(Integer postSort) + { + this.postSort = postSort; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public boolean isFlag() + { + return flag; + } + + public void setFlag(boolean flag) + { + this.flag = flag; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("postId", getPostId()) + .append("postCode", getPostCode()) + .append("postName", getPostName()) + .append("postSort", getPostSort()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleDept.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleDept.java new file mode 100644 index 0000000..47b21bf --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleDept.java @@ -0,0 +1,46 @@ +package com.ruoyi.system.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 角色和部门关联 sys_role_dept + * + * @author ruoyi + */ +public class SysRoleDept +{ + /** 角色ID */ + private Long roleId; + + /** 部门ID */ + private Long deptId; + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("roleId", getRoleId()) + .append("deptId", getDeptId()) + .toString(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleMenu.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleMenu.java new file mode 100644 index 0000000..de10a74 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleMenu.java @@ -0,0 +1,46 @@ +package com.ruoyi.system.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 角色和菜单关联 sys_role_menu + * + * @author ruoyi + */ +public class SysRoleMenu +{ + /** 角色ID */ + private Long roleId; + + /** 菜单ID */ + private Long menuId; + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + public Long getMenuId() + { + return menuId; + } + + public void setMenuId(Long menuId) + { + this.menuId = menuId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("roleId", getRoleId()) + .append("menuId", getMenuId()) + .toString(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java new file mode 100644 index 0000000..2bbd318 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java @@ -0,0 +1,113 @@ +package com.ruoyi.system.domain; + +/** + * 当前在线会话 + * + * @author ruoyi + */ +public class SysUserOnline +{ + /** 会话编号 */ + private String tokenId; + + /** 部门名称 */ + private String deptName; + + /** 用户名称 */ + private String userName; + + /** 登录IP地址 */ + private String ipaddr; + + /** 登录地址 */ + private String loginLocation; + + /** 浏览器类型 */ + private String browser; + + /** 操作系统 */ + private String os; + + /** 登录时间 */ + private Long loginTime; + + public String getTokenId() + { + return tokenId; + } + + public void setTokenId(String tokenId) + { + this.tokenId = tokenId; + } + + public String getDeptName() + { + return deptName; + } + + public void setDeptName(String deptName) + { + this.deptName = deptName; + } + + public String getUserName() + { + return userName; + } + + public void setUserName(String userName) + { + this.userName = userName; + } + + public String getIpaddr() + { + return ipaddr; + } + + public void setIpaddr(String ipaddr) + { + this.ipaddr = ipaddr; + } + + public String getLoginLocation() + { + return loginLocation; + } + + public void setLoginLocation(String loginLocation) + { + this.loginLocation = loginLocation; + } + + public String getBrowser() + { + return browser; + } + + public void setBrowser(String browser) + { + this.browser = browser; + } + + public String getOs() + { + return os; + } + + public void setOs(String os) + { + this.os = os; + } + + public Long getLoginTime() + { + return loginTime; + } + + public void setLoginTime(Long loginTime) + { + this.loginTime = loginTime; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserPost.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserPost.java new file mode 100644 index 0000000..6e8c416 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserPost.java @@ -0,0 +1,46 @@ +package com.ruoyi.system.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 用户和岗位关联 sys_user_post + * + * @author ruoyi + */ +public class SysUserPost +{ + /** 用户ID */ + private Long userId; + + /** 岗位ID */ + private Long postId; + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public Long getPostId() + { + return postId; + } + + public void setPostId(Long postId) + { + this.postId = postId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("userId", getUserId()) + .append("postId", getPostId()) + .toString(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserRole.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserRole.java new file mode 100644 index 0000000..4d15810 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserRole.java @@ -0,0 +1,46 @@ +package com.ruoyi.system.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 用户和角色关联 sys_user_role + * + * @author ruoyi + */ +public class SysUserRole +{ + /** 用户ID */ + private Long userId; + + /** 角色ID */ + private Long roleId; + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("userId", getUserId()) + .append("roleId", getRoleId()) + .toString(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java new file mode 100644 index 0000000..a5d5fdc --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java @@ -0,0 +1,106 @@ +package com.ruoyi.system.domain.vo; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 路由显示信息 + * + * @author ruoyi + */ +public class MetaVo +{ + /** + * 设置该路由在侧边栏和面包屑中展示的名字 + */ + private String title; + + /** + * 设置该路由的图标,对应路径src/assets/icons/svg + */ + private String icon; + + /** + * 设置为true,则不会被 <keep-alive>缓存 + */ + private boolean noCache; + + /** + * 内链地址(http(s)://开头) + */ + private String link; + + public MetaVo() + { + } + + public MetaVo(String title, String icon) + { + this.title = title; + this.icon = icon; + } + + public MetaVo(String title, String icon, boolean noCache) + { + this.title = title; + this.icon = icon; + this.noCache = noCache; + } + + public MetaVo(String title, String icon, String link) + { + this.title = title; + this.icon = icon; + this.link = link; + } + + public MetaVo(String title, String icon, boolean noCache, String link) + { + this.title = title; + this.icon = icon; + this.noCache = noCache; + if (StringUtils.ishttp(link)) + { + this.link = link; + } + } + + public boolean isNoCache() + { + return noCache; + } + + public void setNoCache(boolean noCache) + { + this.noCache = noCache; + } + + public String getTitle() + { + return title; + } + + public void setTitle(String title) + { + this.title = title; + } + + public String getIcon() + { + return icon; + } + + public void setIcon(String icon) + { + this.icon = icon; + } + + public String getLink() + { + return link; + } + + public void setLink(String link) + { + this.link = link; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java new file mode 100644 index 0000000..afff8c9 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java @@ -0,0 +1,148 @@ +package com.ruoyi.system.domain.vo; + +import com.fasterxml.jackson.annotation.JsonInclude; +import java.util.List; + +/** + * 路由配置信息 + * + * @author ruoyi + */ +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class RouterVo +{ + /** + * 路由名字 + */ + private String name; + + /** + * 路由地址 + */ + private String path; + + /** + * 是否隐藏路由,当设置 true 的时候该路由不会再侧边栏出现 + */ + private boolean hidden; + + /** + * 重定向地址,当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 + */ + private String redirect; + + /** + * 组件地址 + */ + private String component; + + /** + * 路由参数:如 {"id": 1, "name": "ry"} + */ + private String query; + + /** + * 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 + */ + private Boolean alwaysShow; + + /** + * 其他元素 + */ + private MetaVo meta; + + /** + * 子路由 + */ + private List<RouterVo> children; + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getPath() + { + return path; + } + + public void setPath(String path) + { + this.path = path; + } + + public boolean getHidden() + { + return hidden; + } + + public void setHidden(boolean hidden) + { + this.hidden = hidden; + } + + public String getRedirect() + { + return redirect; + } + + public void setRedirect(String redirect) + { + this.redirect = redirect; + } + + public String getComponent() + { + return component; + } + + public void setComponent(String component) + { + this.component = component; + } + + public String getQuery() + { + return query; + } + + public void setQuery(String query) + { + this.query = query; + } + + public Boolean getAlwaysShow() + { + return alwaysShow; + } + + public void setAlwaysShow(Boolean alwaysShow) + { + this.alwaysShow = alwaysShow; + } + + public MetaVo getMeta() + { + return meta; + } + + public void setMeta(MetaVo meta) + { + this.meta = meta; + } + + public List<RouterVo> getChildren() + { + return children; + } + + public void setChildren(List<RouterVo> children) + { + this.children = children; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/dto/SaveUserBankDto.java b/ruoyi-system/src/main/java/com/ruoyi/system/dto/SaveUserBankDto.java new file mode 100644 index 0000000..62a8339 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/dto/SaveUserBankDto.java @@ -0,0 +1,20 @@ +package com.ruoyi.system.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +@Data +@ApiModel("保存银行卡Dto") +public class SaveUserBankDto { + + @ApiModelProperty(value = "银行卡号") + @NotBlank(message = "银行卡号不能为空") + private String cardNo; + + @ApiModelProperty(value = "银行卡姓名") + @NotBlank(message = "银行卡姓名不能为空") + private String username; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/dto/UserWithdrawalDto.java b/ruoyi-system/src/main/java/com/ruoyi/system/dto/UserWithdrawalDto.java new file mode 100644 index 0000000..ce94c9e --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/dto/UserWithdrawalDto.java @@ -0,0 +1,18 @@ +package com.ruoyi.system.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +@ApiModel("申请提现Dto") +@Data +public class UserWithdrawalDto { + @ApiModelProperty(value = "提现金额") + @NotNull(message = "提现金额不能为空") + private Double amount; + + + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/export/ContractExport.java b/ruoyi-system/src/main/java/com/ruoyi/system/export/ContractExport.java new file mode 100644 index 0000000..ac9c72f --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/export/ContractExport.java @@ -0,0 +1,51 @@ +package com.ruoyi.system.export; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import io.swagger.annotations.ApiModel; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.Date; + +@Data +@ApiModel(value = "合同导出excel") +public class ContractExport implements Serializable { + + @Excel(name = "合同编号",width = 30) + private String contractNumber; + + @Excel(name = "合同名称",width = 30) + private String contractName; + + @Excel(name = "甲方名称",width = 30) + private String partyOneName; + + @Excel(name = "乙方名称",width = 30) + private String partyTwoName; + + @Excel(name = "创建时间",width = 30) + private String createTime; + + + @Excel(name = "生效日期",width = 30) + private String startTime; + + @Excel(name = "终止日期",width = 30) + private String endTime; + + @Excel(name = "租金支付方式",width = 30) + private String payType; + + @Excel(name = "押金",width = 30) + private String deposit; + + @Excel(name = "状态",width = 30) + private String status; + +// @Excel(name = "计划周期",width = 30,replace = {"周计划_1","月计划_2"}) +// private Integer planCycle; + + + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/export/OpticalInspectionExport.java b/ruoyi-system/src/main/java/com/ruoyi/system/export/OpticalInspectionExport.java new file mode 100644 index 0000000..b197df9 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/export/OpticalInspectionExport.java @@ -0,0 +1,41 @@ +package com.ruoyi.system.export; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import io.swagger.annotations.ApiModel; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +@Data +@ApiModel(value = "光缆巡检导出excel") +public class OpticalInspectionExport implements Serializable { + + @Excel(name = "工单号",width = 20) + private Integer id; + + @Excel(name = "工单名称",width = 30) + private String inspectionName; + + @Excel(name = "开始时间",width = 30,exportFormat = "yyyy-MM-dd HH:mm") + private Date startTime; + + @Excel(name = "截止时间",width = 30,exportFormat = "yyyy-MM-dd HH:mm") + private Date endTime; + + @Excel(name = "计划周期",width = 30,replace = {"周计划_1","月计划_2"}) + private Integer planCycle; + + @Excel(name = "路线",width = 30) + private String address; + + @Excel(name = "巡检点位",width = 30) + private Integer inspectionCount; + + @Excel(name = "提交人",width = 30) + private String createBy; + + @Excel(name = "巡检状态",width = 30,replace = {"待巡检_1","未完成_2","已完成_3"}) + private Integer state; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/importExcel/TBankFlowImportExcel.java b/ruoyi-system/src/main/java/com/ruoyi/system/importExcel/TBankFlowImportExcel.java new file mode 100644 index 0000000..c61ae4d --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/importExcel/TBankFlowImportExcel.java @@ -0,0 +1,51 @@ +package com.ruoyi.system.importExcel; + +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.annotations.ApiModel; +import lombok.Data; +import org.apache.commons.lang3.StringUtils; + +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * @author 64502 + */ +@Data +@ApiModel(value="银行流水导入Excel") +public class TBankFlowImportExcel implements Serializable { + + + @ExcelProperty("银行流水号") + private String bankSerialNumber; + + @ExcelProperty("流水金额") + private BigDecimal flowMoney; + + @ExcelProperty("支付时间") + private String payTime; + + @ExcelProperty("付款人") + private String payer; + + @ExcelProperty("导入结果") + private String result; + + public boolean validate() { + if (StringUtils.isEmpty(bankSerialNumber)){ + result = "银行流水号不能为空"; + return false; + } + if (StringUtils.isEmpty(payTime)){ + result = "支付时间不能为空"; + return false; + } + if (flowMoney == null){ + result = "流水金额不能为空"; + return false; + } + return true; + } + + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/importExcel/TCheckImportExcel.java b/ruoyi-system/src/main/java/com/ruoyi/system/importExcel/TCheckImportExcel.java new file mode 100644 index 0000000..1a0aaff --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/importExcel/TCheckImportExcel.java @@ -0,0 +1,20 @@ +package com.ruoyi.system.importExcel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import io.swagger.annotations.ApiModel; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +@Data +@ApiModel(value = "检查项导入Excel") +public class TCheckImportExcel implements Serializable { + + @Excel(width = 30,name = "检查项") + private String checkTypeStr; + + @Excel(width = 30,name = "检查内容") + private String checkContent; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java new file mode 100644 index 0000000..bbf5be4 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java @@ -0,0 +1,78 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import com.ruoyi.system.domain.SysConfig; +import org.apache.ibatis.annotations.Mapper; + +/** + * 参数配置 数据层 + * + * @author ruoyi + */ +@Mapper +public interface SysConfigMapper +{ + /** + * 查询参数配置信息 + * + * @param config 参数配置信息 + * @return 参数配置信息 + */ + public SysConfig selectConfig(SysConfig config); + + /** + * 通过ID查询配置 + * + * @param configId 参数ID + * @return 参数配置信息 + */ + public SysConfig selectConfigById(Long configId); + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + public List<SysConfig> selectConfigList(SysConfig config); + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数键名 + * @return 参数配置信息 + */ + public SysConfig checkConfigKeyUnique(String configKey); + + /** + * 新增参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int insertConfig(SysConfig config); + + /** + * 修改参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int updateConfig(SysConfig config); + + /** + * 删除参数配置 + * + * @param configId 参数ID + * @return 结果 + */ + public int deleteConfigById(Long configId); + + /** + * 批量删除参数信息 + * + * @param configIds 需要删除的参数ID + * @return 结果 + */ + public int deleteConfigByIds(Long[] configIds); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java new file mode 100644 index 0000000..384a9b6 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java @@ -0,0 +1,118 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.common.core.domain.entity.SysDept; + +/** + * 部门管理 数据层 + * + * @author ruoyi + */ +public interface SysDeptMapper +{ + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + public List<SysDept> selectDeptList(SysDept dept); + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @param deptCheckStrictly 部门树选择项是否关联显示 + * @return 选中部门列表 + */ + public List<Long> selectDeptListByRoleId(@Param("roleId") Long roleId, @Param("deptCheckStrictly") boolean deptCheckStrictly); + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + public SysDept selectDeptById(Long deptId); + + /** + * 根据ID查询所有子部门 + * + * @param deptId 部门ID + * @return 部门列表 + */ + public List<SysDept> selectChildrenDeptById(Long deptId); + + /** + * 根据ID查询所有子部门(正常状态) + * + * @param deptId 部门ID + * @return 子部门数 + */ + public int selectNormalChildrenDeptById(Long deptId); + + /** + * 是否存在子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + public int hasChildByDeptId(Long deptId); + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 + */ + public int checkDeptExistUser(Long deptId); + + /** + * 校验部门名称是否唯一 + * + * @param deptName 部门名称 + * @param parentId 父部门ID + * @return 结果 + */ + public SysDept checkDeptNameUnique(@Param("deptName") String deptName, @Param("parentId") Long parentId); + + /** + * 新增部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int insertDept(SysDept dept); + + /** + * 修改部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int updateDept(SysDept dept); + + /** + * 修改所在部门正常状态 + * + * @param deptIds 部门ID组 + */ + public void updateDeptStatusNormal(Long[] deptIds); + + /** + * 修改子元素关系 + * + * @param depts 子元素 + * @return 结果 + */ + public int updateDeptChildren(@Param("depts") List<SysDept> depts); + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + public int deleteDeptById(Long deptId); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java new file mode 100644 index 0000000..c8bf9cf --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java @@ -0,0 +1,100 @@ +package com.ruoyi.system.mapper; + +import java.util.List; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.common.core.domain.entity.SysDictData; + +/** + * 字典表 数据层 + * + * @author ruoyi + */ +@Mapper +public interface SysDictDataMapper +{ + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + public List<SysDictData> selectDictDataList(SysDictData dictData); + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + public List<SysDictData> selectDictDataByType(String dictType); + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + public String selectDictLabel(@Param("dictType") String dictType, @Param("dictValue") String dictValue); + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + public SysDictData selectDictDataById(Long dictCode); + + /** + * 查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据 + */ + public int countDictDataByType(String dictType); + + /** + * 通过字典ID删除字典数据信息 + * + * @param dictCode 字典数据ID + * @return 结果 + */ + public int deleteDictDataById(Long dictCode); + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + * @return 结果 + */ + public int deleteDictDataByIds(Long[] dictCodes); + + /** + * 新增字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int insertDictData(SysDictData dictData); + + /** + * 修改字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int updateDictData(SysDictData dictData); + + /** + * 同步修改字典类型 + * + * @param oldDictType 旧字典类型 + * @param newDictType 新旧字典类型 + * @return 结果 + */ + public int updateDictDataType(@Param("oldDictType") String oldDictType, @Param("newDictType") String newDictType); + + public String selectDictDataByTypeAndValue(@Param("dictType")String dictType, @Param("dictValue")Integer dictValue); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java new file mode 100644 index 0000000..5fb48fb --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java @@ -0,0 +1,83 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import com.ruoyi.common.core.domain.entity.SysDictType; + +/** + * 字典表 数据层 + * + * @author ruoyi + */ +public interface SysDictTypeMapper +{ + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + public List<SysDictType> selectDictTypeList(SysDictType dictType); + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + public List<SysDictType> selectDictTypeAll(); + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + public SysDictType selectDictTypeById(Long dictId); + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + public SysDictType selectDictTypeByType(String dictType); + + /** + * 通过字典ID删除字典信息 + * + * @param dictId 字典ID + * @return 结果 + */ + public int deleteDictTypeById(Long dictId); + + /** + * 批量删除字典类型信息 + * + * @param dictIds 需要删除的字典ID + * @return 结果 + */ + public int deleteDictTypeByIds(Long[] dictIds); + + /** + * 新增字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int insertDictType(SysDictType dictType); + + /** + * 修改字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int updateDictType(SysDictType dictType); + + /** + * 校验字典类型称是否唯一 + * + * @param dictType 字典类型 + * @return 结果 + */ + public SysDictType checkDictTypeUnique(String dictType); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java new file mode 100644 index 0000000..629866f --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java @@ -0,0 +1,42 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import com.ruoyi.system.domain.SysLogininfor; + +/** + * 系统访问日志情况信息 数据层 + * + * @author ruoyi + */ +public interface SysLogininforMapper +{ + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + public void insertLogininfor(SysLogininfor logininfor); + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + public List<SysLogininfor> selectLogininforList(SysLogininfor logininfor); + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return 结果 + */ + public int deleteLogininforByIds(Long[] infoIds); + + /** + * 清空系统登录日志 + * + * @return 结果 + */ + public int cleanLogininfor(); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java new file mode 100644 index 0000000..5f34985 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java @@ -0,0 +1,135 @@ +package com.ruoyi.system.mapper; + +import java.util.List; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.common.core.domain.entity.SysMenu; + +/** + * 菜单表 数据层 + * + * @author ruoyi + */ +@Mapper +public interface SysMenuMapper +{ + /** + * 查询系统菜单列表 + * + * @param menu 菜单信息 + * @return 菜单列表 + */ + public List<SysMenu> selectMenuList(SysMenu menu); + + /** + * 根据用户所有权限 + * + * @return 权限列表 + */ + public List<String> selectMenuPerms(); + + /** + * 根据用户查询系统菜单列表 + * + * @param menu 菜单信息 + * @return 菜单列表 + */ + public List<SysMenu> selectMenuListByUserId(SysMenu menu); + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + public List<String> selectMenuPermsByRoleId(Long roleId); + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + public List<String> selectMenuPermsByUserId(Long userId); + + /** + * 根据用户ID查询菜单 + * + * @return 菜单列表 + */ + public List<SysMenu> selectMenuTreeAll(); + + /** + * 根据用户ID查询菜单 + * + * @param userId 用户ID + * @return 菜单列表 + */ + public List<SysMenu> selectMenuTreeByUserId(Long userId); + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @param menuCheckStrictly 菜单树选择项是否关联显示 + * @return 选中菜单列表 + */ + public List<Long> selectMenuListByRoleId(@Param("roleId") Long roleId, @Param("menuCheckStrictly") boolean menuCheckStrictly); + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + public SysMenu selectMenuById(Long menuId); + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int hasChildByMenuId(Long menuId); + + /** + * 新增菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int insertMenu(SysMenu menu); + + /** + * 修改菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int updateMenu(SysMenu menu); + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int deleteMenuById(Long menuId); + + /** + * 校验菜单名称是否唯一 + * + * @param menuName 菜单名称 + * @param parentId 父菜单ID + * @return 结果 + */ + public SysMenu checkMenuNameUnique(@Param("menuName") String menuName, @Param("parentId") Long parentId); + + List<SysMenu> selectListByRoleId(@Param("roleId")Long roleId); + + List<SysMenu> getAllInIds(@Param("menusId") List<Long> menusId); + + List<SysMenu> selectList(); + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java new file mode 100644 index 0000000..c34f0a2 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java @@ -0,0 +1,60 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import com.ruoyi.system.domain.SysNotice; + +/** + * 通知公告表 数据层 + * + * @author ruoyi + */ +public interface SysNoticeMapper +{ + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + public SysNotice selectNoticeById(Long noticeId); + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + public List<SysNotice> selectNoticeList(SysNotice notice); + + /** + * 新增公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int insertNotice(SysNotice notice); + + /** + * 修改公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int updateNotice(SysNotice notice); + + /** + * 批量删除公告 + * + * @param noticeId 公告ID + * @return 结果 + */ + public int deleteNoticeById(Long noticeId); + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * @return 结果 + */ + public int deleteNoticeByIds(Long[] noticeIds); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java new file mode 100644 index 0000000..bdade5f --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java @@ -0,0 +1,56 @@ +package com.ruoyi.system.mapper; + +import java.util.List; + +import com.ruoyi.common.basic.PageInfo; +import com.ruoyi.system.domain.SysOperLog; +import com.ruoyi.system.query.SysOperLogQuery; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * 操作日志 数据层 + * + * @author ruoyi + */ +@Mapper +public interface SysOperLogMapper +{ + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + public void insertOperlog(SysOperLog operLog); + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + public List<SysOperLog> selectOperLogList(SysOperLog operLog); + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + public int deleteOperLogByIds(@Param("operIds") List<Long> operIds); + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + public SysOperLog selectOperLogById(Long operId); + + /** + * 清空操作日志 + */ + public void cleanOperLog(); + + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java new file mode 100644 index 0000000..675a257 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java @@ -0,0 +1,101 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import com.ruoyi.system.domain.SysPost; +import org.apache.ibatis.annotations.Mapper; + +/** + * 岗位信息 数据层 + * + * @author ruoyi + */ +@Mapper +public interface SysPostMapper +{ + /** + * 查询岗位数据集合 + * + * @param post 岗位信息 + * @return 岗位数据集合 + */ + public List<SysPost> selectPostList(SysPost post); + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + public List<SysPost> selectPostAll(); + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + public SysPost selectPostById(Long postId); + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + public List<Long> selectPostListByUserId(Long userId); + + /** + * 查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + public List<SysPost> selectPostsByUserName(String userName); + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + public int deletePostById(Long postId); + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * @return 结果 + */ + public int deletePostByIds(Long[] postIds); + + /** + * 修改岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int updatePost(SysPost post); + + /** + * 新增岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int insertPost(SysPost post); + + /** + * 校验岗位名称 + * + * @param postName 岗位名称 + * @return 结果 + */ + public SysPost checkPostNameUnique(String postName); + + /** + * 校验岗位编码 + * + * @param postCode 岗位编码 + * @return 结果 + */ + public SysPost checkPostCodeUnique(String postCode); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java new file mode 100644 index 0000000..3ec58f0 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java @@ -0,0 +1,46 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import com.ruoyi.system.domain.SysRoleDept; +import org.apache.ibatis.annotations.Mapper; + +/** + * 角色与部门关联表 数据层 + * + * @author ruoyi + */ +@Mapper +public interface SysRoleDeptMapper +{ + /** + * 通过角色ID删除角色和部门关联 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleDeptByRoleId(Long roleId); + + /** + * 批量删除角色部门关联信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteRoleDept(Long[] ids); + + /** + * 查询部门使用数量 + * + * @param deptId 部门ID + * @return 结果 + */ + public int selectCountRoleDeptByDeptId(Long deptId); + + /** + * 批量新增角色部门信息 + * + * @param roleDeptList 角色部门列表 + * @return 结果 + */ + public int batchRoleDept(List<SysRoleDept> roleDeptList); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java new file mode 100644 index 0000000..ada62eb --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java @@ -0,0 +1,132 @@ +package com.ruoyi.system.mapper; + +import java.util.List; + +import com.ruoyi.common.basic.PageInfo; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.system.query.SysRoleQuery; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * 角色表 数据层 + * + * @author ruoyi + */ +@Mapper +public interface SysRoleMapper +{ + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + public List<SysRole> selectRoleList(SysRole role); + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * @return 角色列表 + */ + public List<SysRole> selectRolePermissionByUserId(Long userId); + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + public List<SysRole> selectRoleAll(); + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + public List<Long> selectRoleListByUserId(Long userId); + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + public SysRole selectRoleById(Long roleId); + + /** + * 根据用户ID查询角色 + * + * @param userName 用户名 + * @return 角色列表 + */ + public List<SysRole> selectRolesByUserName(String userName); + + /** + * 校验角色名称是否唯一 + * + * @param roleName 角色名称 + * @return 角色信息 + */ + public SysRole checkRoleNameUnique(String roleName); + + /** + * 校验角色权限是否唯一 + * + * @param roleKey 角色权限 + * @return 角色信息 + */ + public SysRole checkRoleKeyUnique(String roleKey); + + /** + * 修改角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int updateRole(SysRole role); + + /** + * 新增角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int insertRole(SysRole role); + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleById(Long roleId); + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * @return 结果 + */ + public int deleteRoleByIds(@Param("roleIds") List<Long> roleIds); + + int selectCountByRoleName(@Param("roleName") String roleName); + +// List<SysRole> selectList(@Param("query")SysRoleQuery query); + + int selectCount(@Param("status")Integer status); + + void updateStatus(SysRole role); + + List<SysRole> selectListByDelFlag(@Param("delFlag")Integer delFlag); + + SysRole selectRoleByUserId(@Param("userId")Long userId); + + String selectByUserId(@Param("userId") Long user_id); + + List<SysRole> selectPageList(@Param("query")SysRoleQuery query,@Param("pageInfo") PageInfo<SysRole> pageInfo); + + List<SysRole> selectRoleByUserIds(@Param("roleIds")List<String> roleIds); + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java new file mode 100644 index 0000000..8ae173a --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java @@ -0,0 +1,47 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import com.ruoyi.system.domain.SysRoleMenu; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * 角色与菜单关联表 数据层 + * + * @author ruoyi + */ +@Mapper +public interface SysRoleMenuMapper +{ + /** + * 查询菜单使用数量 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int checkMenuExistRole(Long menuId); + + /** + * 通过角色ID删除角色和菜单关联 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleMenuByRoleId(Long roleId); + + /** + * 批量删除角色菜单关联信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteRoleMenu(@Param("ids") List<Long> ids); + + /** + * 批量新增角色菜单信息 + * + * @param roleMenuList 角色菜单列表 + * @return 结果 + */ + public int batchRoleMenu(List<SysRoleMenu> roleMenuList); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java new file mode 100644 index 0000000..68be8d0 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java @@ -0,0 +1,177 @@ +package com.ruoyi.system.mapper; + +import java.util.List; + +import com.ruoyi.common.basic.PageInfo; +import com.ruoyi.system.query.SysUserQuery; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.common.core.domain.entity.SysUser; + +/** + * 用户表 数据层 + * + * @author ruoyi + */ +@Mapper +public interface SysUserMapper +{ + /** + * 根据条件分页查询用户列表 + * + * @param sysUser 用户信息 + * @return 用户信息集合信息 + */ + public List<SysUser> selectUserList(SysUser sysUser); + + /** + * 根据条件分页查询已配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List<SysUser> selectAllocatedList(SysUser user); + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List<SysUser> selectUnallocatedList(SysUser user); + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + public SysUser selectUserByUserName(String userName); + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + public SysUser selectUserById(Long userId); + + /** + * 新增用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int insertUser(SysUser user); + + /** + * 修改用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUser(SysUser user); + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * @return 结果 + */ + public int updateUserAvatar(@Param("userName") String userName, @Param("avatar") String avatar); + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * @return 结果 + */ + public int resetUserPwd(@Param("userName") String userName, @Param("password") String password); + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserById(Long userId); + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * @return 结果 + */ + public int deleteUserByIds(@Param("ids") List<Long> userIds); + + /** + * 校验用户名称是否唯一 + * + * @param userName 用户名称 + * @return 结果 + */ + public SysUser checkUserNameUnique(String userName); + + /** + * 校验手机号码是否唯一 + * + * @param phonenumber 手机号码 + * @return 结果 + */ + public SysUser checkPhoneUnique(String phonenumber); + + /** + * 校验email是否唯一 + * + * @param email 用户邮箱 + * @return 结果 + */ + public SysUser checkEmailUnique(String email); + + /** + * 查询用户集合通过用户id + * @param userIds + * @return + */ + List<SysUser> selectUserByIds(@Param("userIds") List<Long> userIds); + + List<SysUser> selectList(); + + Integer selectCount(@Param("status") Integer status); + + /** + * 获取用户列表 + * @param query + * @return + */ +// List<SysUserVO> selectUserPageList(@Param("query")SysUserQuery query); + + /** + * 获取用户黑名单列表 + * @param + * @return + */ +// List<SysUserVO> selectBlackPageList(@Param("query")SysUserQuery query); + + List<SysUser> selectListByNamePhone(@Param("name")String name); + + List<SysUser> selectUserByUserNameList(@Param("names")List<String> names); +// UserInfoVo userInfo(@Param("id") Long userId); + + SysUser selectByPhone(@Param("phonenumber") String phonenumber); + +// UserInfoVo getUserInfoBy(@Param("singleNum")String singleNum); + + Long getUserRole(@Param("userId") Long userId); + + int updateUserIfBlack(@Param("ids")List<Long> ids); + + List<SysUser> selectAllList(); + + + void updatePassword(@Param("id") Long id,@Param("s") String s); + + long selectIdByPhone(@Param("phonenumber") String phonenumber); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java new file mode 100644 index 0000000..a908a24 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java @@ -0,0 +1,46 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import com.ruoyi.system.domain.SysUserPost; +import org.apache.ibatis.annotations.Mapper; + +/** + * 用户与岗位关联表 数据层 + * + * @author ruoyi + */ +@Mapper +public interface SysUserPostMapper +{ + /** + * 通过用户ID删除用户和岗位关联 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserPostByUserId(Long userId); + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * @return 结果 + */ + public int countUserPostById(Long postId); + + /** + * 批量删除用户和岗位关联 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteUserPost(Long[] ids); + + /** + * 批量新增用户岗位信息 + * + * @param userPostList 用户角色列表 + * @return 结果 + */ + public int batchUserPost(List<SysUserPost> userPostList); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java new file mode 100644 index 0000000..5f01fbb --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java @@ -0,0 +1,74 @@ +package com.ruoyi.system.mapper; + +import java.util.List; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.system.domain.SysUserRole; + +/** + * 用户与角色关联表 数据层 + * + * @author ruoyi + */ +@Mapper +public interface SysUserRoleMapper +{ + /** + * 通过用户ID删除用户和角色关联 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserRoleByUserId(Long userId); + + /** + * 批量删除用户和角色关联 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteUserRole(@Param("ids") List<Long> ids); + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * @return 结果 + */ + public int countUserRoleByRoleId(Long roleId); + + /** + * 批量新增用户角色信息 + * + * @param userRoleList 用户角色列表 + * @return 结果 + */ + public int batchUserRole(List<SysUserRole> userRoleList); + + /** + * 批量新增用户角色信息 + * + * @param userRole 用户角色列表 + * @return 结果 + */ + public int insertUserRole(@Param("userRole") SysUserRole userRole); + + /** + * 删除用户和角色关联信息 + * + * @param userRole 用户和角色关联信息 + * @return 结果 + */ + public int deleteUserRoleInfo(SysUserRole userRole); + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要删除的用户数据ID + * @return 结果 + */ + public int deleteUserRoleInfos(@Param("roleId") Long roleId, @Param("userIds") Long[] userIds); + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbAccountDetailMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbAccountDetailMapper.java new file mode 100644 index 0000000..26dac6a --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbAccountDetailMapper.java @@ -0,0 +1,17 @@ +package com.ruoyi.system.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.system.model.TbAccountDetail; + +/** + * <p> + * 账户明细表 Mapper 接口 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +public interface TbAccountDetailMapper extends BaseMapper<TbAccountDetail> { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbAddressMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbAddressMapper.java new file mode 100644 index 0000000..8d2bdd9 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbAddressMapper.java @@ -0,0 +1,17 @@ +package com.ruoyi.system.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.system.model.TbAddress; + +/** + * <p> + * 用户地址表 Mapper 接口 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +public interface TbAddressMapper extends BaseMapper<TbAddress> { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbBankMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbBankMapper.java new file mode 100644 index 0000000..96c3ceb --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbBankMapper.java @@ -0,0 +1,17 @@ +package com.ruoyi.system.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.system.model.TbBank; + +/** + * <p> + * 用户卡表 Mapper 接口 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +public interface TbBankMapper extends BaseMapper<TbBank> { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbCompanyMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbCompanyMapper.java new file mode 100644 index 0000000..ba1a980 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbCompanyMapper.java @@ -0,0 +1,17 @@ +package com.ruoyi.system.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.system.model.TbCompany; + +/** + * <p> + * 公司消息表 Mapper 接口 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +public interface TbCompanyMapper extends BaseMapper<TbCompany> { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbMessageMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbMessageMapper.java new file mode 100644 index 0000000..fa6e766 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbMessageMapper.java @@ -0,0 +1,17 @@ +package com.ruoyi.system.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.system.model.TbMessage; + +/** + * <p> + * 消息表 Mapper 接口 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +public interface TbMessageMapper extends BaseMapper<TbMessage> { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbOpeningBankMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbOpeningBankMapper.java new file mode 100644 index 0000000..fd2087f --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbOpeningBankMapper.java @@ -0,0 +1,17 @@ +package com.ruoyi.system.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.system.model.TbOpeningBank; + +/** + * <p> + * 开户行表 Mapper 接口 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +public interface TbOpeningBankMapper extends BaseMapper<TbOpeningBank> { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbPermitMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbPermitMapper.java new file mode 100644 index 0000000..f6691e1 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbPermitMapper.java @@ -0,0 +1,17 @@ +package com.ruoyi.system.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.system.model.TbPermit; + +/** + * <p> + * 许可证表 Mapper 接口 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +public interface TbPermitMapper extends BaseMapper<TbPermit> { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbRegionMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbRegionMapper.java new file mode 100644 index 0000000..52611ff --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbRegionMapper.java @@ -0,0 +1,17 @@ +package com.ruoyi.system.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.system.model.TbRegion; + +/** + * <p> + * 省市区三级联动 Mapper 接口 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +public interface TbRegionMapper extends BaseMapper<TbRegion> { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbUserMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbUserMapper.java new file mode 100644 index 0000000..9580b46 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbUserMapper.java @@ -0,0 +1,17 @@ +package com.ruoyi.system.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.system.model.TbUser; + +/** + * <p> + * Mapper 接口 + * </p> + * + * @author administrator + * @since 2025-05-23 + */ +public interface TbUserMapper extends BaseMapper<TbUser> { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbWithdrawalMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbWithdrawalMapper.java new file mode 100644 index 0000000..5960f1b --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/TbWithdrawalMapper.java @@ -0,0 +1,17 @@ +package com.ruoyi.system.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ruoyi.system.model.TbWithdrawal; + +/** + * <p> + * 提现申请表 Mapper 接口 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +public interface TbWithdrawalMapper extends BaseMapper<TbWithdrawal> { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/model/TbAccountDetail.java b/ruoyi-system/src/main/java/com/ruoyi/system/model/TbAccountDetail.java new file mode 100644 index 0000000..cd1cdbc --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/model/TbAccountDetail.java @@ -0,0 +1,66 @@ +package com.ruoyi.system.model; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * <p> + * 账户明细表 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("tb_account_detail") +public class TbAccountDetail extends Model<TbAccountDetail> { + + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + /** + * 用户id + */ + @TableField("user_id") + private String userId; + /** + * 1+ 2- + */ + @ApiModelProperty("1+ 2-") + private Integer type; + /** + * 1提现 2售卖商品 3平台退款 4分佣 + */ + @ApiModelProperty("1提现 2售卖商品 3平台退款 4分佣") + private Integer category; + /** + * 1待审核 2通过(完成) 3拒绝 (取消) + */ + @ApiModelProperty("1待审核 2通过(完成) 3拒绝 (取消)") + private Integer status; + @ApiModelProperty("金额") + private BigDecimal money; + /** + * 创建时间 + */ + @ApiModelProperty("创建时间") + @TableField("create_time") + private Date createTime; + + + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/model/TbAddress.java b/ruoyi-system/src/main/java/com/ruoyi/system/model/TbAddress.java new file mode 100644 index 0000000..ab9364c --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/model/TbAddress.java @@ -0,0 +1,108 @@ +package com.ruoyi.system.model; + + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import javax.validation.constraints.NotBlank; +import java.io.Serializable; +import java.util.Date; + +/** + * <p> + * 用户地址表 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("tb_address") +public class TbAddress extends Model<TbAddress> { + + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + /** + * 用户id + */ + @TableField("user_id") + private String userId; + /** + * 城市 + */ + @ApiModelProperty("城市") + @NotBlank(message = "城市不能为空") + private String city; + /** + * 城市code + */ + @TableField("city_code") + @ApiModelProperty("城市code") + @NotBlank(message = "城市code不能为空") + private String cityCode; + /** + * 省 + */ + @ApiModelProperty("省") + @NotBlank(message = "省不能为空") + private String province; + /** + * 省code + */ + @TableField("province_code") + @ApiModelProperty("省code") + @NotBlank(message = "省code不能为空") + private String provinceCode; + /** + * 区 + */ + @ApiModelProperty("区") + @NotBlank(message = "区不能为空") + private String area; + /** + * 区code + */ + @TableField("area_code") + @ApiModelProperty("区code") + @NotBlank(message = "区code不能为空") + private String areaCode; + /** + * 姓名 + */ + @ApiModelProperty("姓名") + @NotBlank(message = "姓名不能为空") + private String username; + /** + * 手机号 + */ + @ApiModelProperty("手机号") + @NotBlank(message = "手机号不能为空") + private String phone; + /** + * 详情地址 + */ + @ApiModelProperty("详情地址") + @NotBlank(message = "详情地址不能为空") + private String detail; + @TableField("create_time") + private Date createTime; + /** + * 0正常1删除 + */ + @TableField("is_delete") + private Integer isDelete; + + + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/model/TbBank.java b/ruoyi-system/src/main/java/com/ruoyi/system/model/TbBank.java new file mode 100644 index 0000000..cd486c2 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/model/TbBank.java @@ -0,0 +1,58 @@ +package com.ruoyi.system.model; + + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +/** + * <p> + * 用户卡表 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("tb_bank") +public class TbBank extends Model<TbBank> { + + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + /** + * 用户id、 + */ + @TableField("user_id") + private String userId; + /** + * 卡号 + */ + @ApiModelProperty("卡号") + @TableField("card_no") + private String cardNo; + /** + * 姓名 + */ + @ApiModelProperty("姓名") + private String username; + @TableField("create_time") + private Date createTime; + @TableField("is_delete") + private Integer isDelete; + + + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/model/TbCompany.java b/ruoyi-system/src/main/java/com/ruoyi/system/model/TbCompany.java new file mode 100644 index 0000000..3ab15a7 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/model/TbCompany.java @@ -0,0 +1,220 @@ +package com.ruoyi.system.model; + + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * <p> + * 公司消息表 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("tb_company") +public class TbCompany extends Model<TbCompany> { + + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + /** + * 发布用户 + */ + @TableField("user_id") + private String userId; + /** + * 公司名称 + */ + @TableField("company_name") + private String companyName; + /** + * 所在省 + */ + private String city; + /** + * 所在省code + */ + @TableField("city_code") + private String cityCode; + /** + * 所在市 + */ + private String province; + /** + * 所在市code + */ + @TableField("province_code") + private String provinceCode; + /** + * 所在区 + */ + private String area; + /** + * 所在区code + */ + @TableField("area_code") + private String areaCode; + /** + * 成立时间 + */ + @TableField("establish_time") + private String establishTime; + /** + * 1个体工商户 + */ + @TableField("company_category") + private Integer companyCategory; + /** + * 行业id + */ + @TableField("company_industry_id") + private Integer companyIndustryId; + /** + * 1一般纳税人 2小规模纳税人 3税务未登记 + */ + @TableField("taxpayer_type") + private Integer taxpayerType; + /** + * 纳税信用等级 + */ + @TableField("tax_credit") + private String taxCredit; + /** + * 预估天数 + */ + @TableField("estimated_days") + private Integer estimatedDays; + /** + * 公章数 + */ + @TableField("official_seal_num") + private Integer officialSealNum; + /** + * 实缴资金 + */ + @TableField("paid_in_funds") + private String paidInFunds; + /** + * 征信报告链接 + */ + private String link; + /** + * 开票额度 + */ + @TableField("invoice_limit") + private String invoiceLimit; + /** + * 注册资本 + */ + @TableField("registered_capital") + private String registeredCapital; + /** + * 高新技术企业 + */ + @TableField("high_tech_enterprise_technology") + private Integer highTechEnterpriseTechnology; + /** + * 社保缴纳0无1有 + */ + @TableField("social_security") + private Integer socialSecurity; + /** + * 招投标0无1有 + */ + private Integer tendering; + /** + * 商标0无 1有 + */ + @TableField("have_trademark") + private Integer haveTrademark; + /** + * 商标数 + */ + @TableField("trademark_num") + private Integer trademarkNum; + /** + * 专利0无 1有 + */ + @TableField("have_patent") + private Integer havePatent; + /** + * 专利数 + */ + @TableField("patent_num") + private Integer patentNum; + /** + * 软著0无1有 + */ + @TableField("have_soft_works") + private Integer haveSoftWorks; + /** + * 软著数 + */ + @TableField("soft_works_num") + private Integer softWorksNum; + /** + * 企业改名费 + */ + @TableField("rename_money") + private BigDecimal renameMoney; + /** + * 预计增加时间 + */ + @TableField("rename_day") + private Integer renameDay; + /** + * 迁区费用 + */ + @TableField("relocation_area_money") + private BigDecimal relocationAreaMoney; + /** + * 迁区预计增加时间 + */ + @TableField("relocation_area_day") + private Integer relocationAreaDay; + /** + * 售卖价格 + */ + @TableField("sale_money") + private BigDecimal saleMoney; + /** + * 联系电话 + */ + private String phone; + /** + * 收件人 + */ + private String recipient; + /** + * 收件人地址 + */ + @TableField("recipient_address") + private String recipientAddress; + /** + * 所需资料 + */ + private String information; + /** + * 备注 + */ + private String remark; + @TableField("create_time") + private Date createTime; + + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/model/TbMessage.java b/ruoyi-system/src/main/java/com/ruoyi/system/model/TbMessage.java new file mode 100644 index 0000000..5c8e7d1 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/model/TbMessage.java @@ -0,0 +1,54 @@ +package com.ruoyi.system.model; + + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +/** + * <p> + * 消息表 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("tb_message") +public class TbMessage extends Model<TbMessage> { + + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + @TableField("user_id") + private String userId; + /** + * 消息信息 + */ + @ApiModelProperty("消息信息") + private String message; + /** + * 0未读 1已读 + */ + @ApiModelProperty("0未读 1已读") + @TableField("is_read") + private Integer isRead; + @TableField("create_time") + @ApiModelProperty("时间") + private Date createTime; + + + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/model/TbOpeningBank.java b/ruoyi-system/src/main/java/com/ruoyi/system/model/TbOpeningBank.java new file mode 100644 index 0000000..bac0bd1 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/model/TbOpeningBank.java @@ -0,0 +1,53 @@ +package com.ruoyi.system.model; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +/** + * <p> + * 开户行表 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("tb_opening_bank") +public class TbOpeningBank extends Model<TbOpeningBank> { + + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + @TableField("company_id") + private String companyId; + /** + * 开户行名称 + */ + private String name; + /** + * 开户类型 + */ + private String category; + /** + * 有无外币0无 1有 + */ + @TableField("have_foreign_currency") + private Integer haveForeignCurrency; + @TableField("create_time") + private Date createTime; + + + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/model/TbPermit.java b/ruoyi-system/src/main/java/com/ruoyi/system/model/TbPermit.java new file mode 100644 index 0000000..4353665 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/model/TbPermit.java @@ -0,0 +1,56 @@ +package com.ruoyi.system.model; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +/** + * <p> + * 许可证表 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("tb_permit") +public class TbPermit extends Model<TbPermit> { + + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + /** + * 公司id + */ + @TableField("company_id") + private String companyId; + /** + * 名称 + */ + private String name; + /** + * 等级 + */ + private String level; + /** + * 失效时间 + */ + @TableField("expire_time") + private String expireTime; + @TableField("create_time") + private Date createTime; + + + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/model/TbRegion.java b/ruoyi-system/src/main/java/com/ruoyi/system/model/TbRegion.java new file mode 100644 index 0000000..e118d5e --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/model/TbRegion.java @@ -0,0 +1,54 @@ +package com.ruoyi.system.model; + + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + * <p> + * 省市区三级联动 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("tb_region") +public class TbRegion extends Model<TbRegion> { + + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Integer id; + /** + * 城市名称 + */ + private String name; + private String code; + private String citycode; + /** + * 父级ID + */ + @TableField("parent_id") + private Integer parentId; + /** + * 英文名称 + */ + private String english; + + + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/model/TbUser.java b/ruoyi-system/src/main/java/com/ruoyi/system/model/TbUser.java new file mode 100644 index 0000000..0c58e02 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/model/TbUser.java @@ -0,0 +1,98 @@ +package com.ruoyi.system.model; + +import java.math.BigDecimal; +import java.util.Date; +import java.io.Serializable; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; + +import com.baomidou.mybatisplus.extension.activerecord.Model; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * <p> + * + * </p> + * + * @author administrator + * @since 2025-05-23 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("tb_user") +public class TbUser extends Model<TbUser> { + + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + /** + * 用户名称 + */ + @TableField("user_name") + @ApiModelProperty("用户名称") + private String userName; + /** + * 头像 + */ + @ApiModelProperty("头像") + private String avatar; + /** + * 绑定手机号 + */ + private String phone; + /** + * 微信登录id + */ + @TableField("open_id") + private String openId; + /** + * 状态:1正常 2冻结 + */ + private Integer status; + + @ApiModelProperty("余额") + private BigDecimal balance; + + private String inviteId; + @TableField("invite_num") + private Integer inviteNum; + /** + * 创建时间 + */ + @TableField("create_time") + private Date createTime; + /** + * 修改时间 + */ + @TableField("update_time") + private Date updateTime; + /** + * 创建者 + */ + @TableField("create_by") + private Long createBy; + /** + * 更新者 + */ + @TableField("update_by") + private Long updateBy; + /** + * 是否删除 + */ + @TableField("is_delete") + private Integer isDelete; + + + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/model/TbWithdrawal.java b/ruoyi-system/src/main/java/com/ruoyi/system/model/TbWithdrawal.java new file mode 100644 index 0000000..c1da333 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/model/TbWithdrawal.java @@ -0,0 +1,65 @@ +package com.ruoyi.system.model; + + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * <p> + * 提现申请表 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("tb_withdrawal") +public class TbWithdrawal extends Model<TbWithdrawal> { + + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.ASSIGN_ID) + private String id; + @TableField("user_id") + private String userId; + /** + * 提现金额 + */ + private BigDecimal money; + /** + * 0待审核 1通过 2拒绝 + */ + private Integer status; + /** + * 拒绝原因 + */ + private String remark; + /** + * 凭证 + */ + private String img; + + private String cardNo; + + private String username; + /** + * 发起时间 + */ + @TableField("create_time") + private Date createTime; + + + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/query/CompanyListQuery.java b/ruoyi-system/src/main/java/com/ruoyi/system/query/CompanyListQuery.java new file mode 100644 index 0000000..fde2efd --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/query/CompanyListQuery.java @@ -0,0 +1,45 @@ +package com.ruoyi.system.query; + +import com.baomidou.mybatisplus.annotation.TableField; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import java.util.List; + +@Data +@ApiModel("商城首页查询Query") +public class CompanyListQuery { + @ApiModelProperty("城市code") + @NotBlank(message = "城市code不能为空") + private String cityCode; + @ApiModelProperty("区code") + @NotBlank(message = "区code不能为空") + private String areaCode; + @ApiModelProperty("公司类型 1个体工商 2有限责任 3独资") + private List<Integer> companyCategorys; + @ApiModelProperty("注册资本") + private List<String> registeredCapitals; + @ApiModelProperty("资格证书名称") + private List<String> certificateNames; + @ApiModelProperty("所属行业") + private List<Integer> companyIndustryIds; + @ApiModelProperty("纳税人种类 1一般纳税人 2小规模纳税人 3税务未登记") + private List<Integer> taxpayerTypes; + @ApiModelProperty("纳税信用等级") + private List<String> taxCredits; + @ApiModelProperty("高新技术企业0否1是") + private Integer highTechEnterpriseTechnology; + @ApiModelProperty("社保缴纳0无1有") + private Integer socialSecurity; + @ApiModelProperty("招投标0无1有") + private Integer tendering; + @ApiModelProperty("商标0无 1有") + private Integer haveTrademark; + @ApiModelProperty("专利0无 1有") + private Integer havePatent; + @ApiModelProperty("软著0无1有") + private Integer haveSoftWorks; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/query/SysOperLogQuery.java b/ruoyi-system/src/main/java/com/ruoyi/system/query/SysOperLogQuery.java new file mode 100644 index 0000000..f316f75 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/query/SysOperLogQuery.java @@ -0,0 +1,13 @@ +package com.ruoyi.system.query; + +import com.ruoyi.common.core.domain.BasePage; +import io.swagger.annotations.ApiModel; +import lombok.Data; + +@Data +@ApiModel(value = "操作日志查询query") +public class SysOperLogQuery extends BasePage { + + + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/query/SysRoleQuery.java b/ruoyi-system/src/main/java/com/ruoyi/system/query/SysRoleQuery.java new file mode 100644 index 0000000..ecf60c9 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/query/SysRoleQuery.java @@ -0,0 +1,17 @@ +package com.ruoyi.system.query; + +import com.ruoyi.common.core.domain.BasePage; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +@ApiModel(value = "角色查询Query") +public class SysRoleQuery extends BasePage { + + @ApiModelProperty(value = "角色名称") + private String roleName; + + @ApiModelProperty(value = "角色状态 0=正常,1=停用") + private Integer status; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/query/SysUserQuery.java b/ruoyi-system/src/main/java/com/ruoyi/system/query/SysUserQuery.java new file mode 100644 index 0000000..3c7b3d4 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/query/SysUserQuery.java @@ -0,0 +1,26 @@ +package com.ruoyi.system.query; + +import com.ruoyi.common.core.domain.BasePage; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +@Data +@ApiModel(value = "账户列表query") +public class SysUserQuery extends BasePage { + + @ApiModelProperty(value = "姓名") + private String nickNameOrPhone; + + @ApiModelProperty(value = "角色id") + private List<Integer> roleIds; + + @ApiModelProperty(value = "部门id集合") + private List<String> deptIds; + + @ApiModelProperty(value = "状态 0=正常 1=停用") + private String status; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/query/UserAccountDetailQuery.java b/ruoyi-system/src/main/java/com/ruoyi/system/query/UserAccountDetailQuery.java new file mode 100644 index 0000000..d9bd0a6 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/query/UserAccountDetailQuery.java @@ -0,0 +1,13 @@ +package com.ruoyi.system.query; + +import com.ruoyi.common.core.domain.BasePage; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +public class UserAccountDetailQuery extends BasePage { + @ApiModelProperty(value = "开始时间") + private String startTime; + @ApiModelProperty(value = "结束时间") + private String endTime; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/query/UserMessageQuery.java b/ruoyi-system/src/main/java/com/ruoyi/system/query/UserMessageQuery.java new file mode 100644 index 0000000..9ddd77d --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/query/UserMessageQuery.java @@ -0,0 +1,13 @@ +package com.ruoyi.system.query; + +import com.ruoyi.common.core.domain.BasePage; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@ApiModel("用户消息Dto") +@Data +public class UserMessageQuery extends BasePage { + @ApiModelProperty("空全部 0未读 1已读") + private Integer isRead; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java new file mode 100644 index 0000000..b307776 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java @@ -0,0 +1,89 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.system.domain.SysConfig; + +/** + * 参数配置 服务层 + * + * @author ruoyi + */ +public interface ISysConfigService +{ + /** + * 查询参数配置信息 + * + * @param configId 参数配置ID + * @return 参数配置信息 + */ + public SysConfig selectConfigById(Long configId); + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数键名 + * @return 参数键值 + */ + public String selectConfigByKey(String configKey); + + /** + * 获取验证码开关 + * + * @return true开启,false关闭 + */ + public boolean selectCaptchaEnabled(); + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + public List<SysConfig> selectConfigList(SysConfig config); + + /** + * 新增参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int insertConfig(SysConfig config); + + /** + * 修改参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int updateConfig(SysConfig config); + + /** + * 批量删除参数信息 + * + * @param configIds 需要删除的参数ID + */ + public void deleteConfigByIds(Long[] configIds); + + /** + * 加载参数缓存数据 + */ + public void loadingConfigCache(); + + /** + * 清空参数缓存数据 + */ + public void clearConfigCache(); + + /** + * 重置参数缓存数据 + */ + public void resetConfigCache(); + + /** + * 校验参数键名是否唯一 + * + * @param config 参数信息 + * @return 结果 + */ + public boolean checkConfigKeyUnique(SysConfig config); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java new file mode 100644 index 0000000..f228208 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java @@ -0,0 +1,124 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.common.core.domain.TreeSelect; +import com.ruoyi.common.core.domain.entity.SysDept; + +/** + * 部门管理 服务层 + * + * @author ruoyi + */ +public interface ISysDeptService +{ + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + public List<SysDept> selectDeptList(SysDept dept); + + /** + * 查询部门树结构信息 + * + * @param dept 部门信息 + * @return 部门树信息集合 + */ + public List<TreeSelect> selectDeptTreeList(SysDept dept); + + /** + * 构建前端所需要树结构 + * + * @param depts 部门列表 + * @return 树结构列表 + */ + public List<SysDept> buildDeptTree(List<SysDept> depts); + + /** + * 构建前端所需要下拉树结构 + * + * @param depts 部门列表 + * @return 下拉树结构列表 + */ + public List<TreeSelect> buildDeptTreeSelect(List<SysDept> depts); + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @return 选中部门列表 + */ + public List<Long> selectDeptListByRoleId(Long roleId); + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + public SysDept selectDeptById(Long deptId); + + /** + * 根据ID查询所有子部门(正常状态) + * + * @param deptId 部门ID + * @return 子部门数 + */ + public int selectNormalChildrenDeptById(Long deptId); + + /** + * 是否存在部门子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + public boolean hasChildByDeptId(Long deptId); + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 true 存在 false 不存在 + */ + public boolean checkDeptExistUser(Long deptId); + + /** + * 校验部门名称是否唯一 + * + * @param dept 部门信息 + * @return 结果 + */ + public boolean checkDeptNameUnique(SysDept dept); + + /** + * 校验部门是否有数据权限 + * + * @param deptId 部门id + */ + public void checkDeptDataScope(Long deptId); + + /** + * 新增保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int insertDept(SysDept dept); + + /** + * 修改保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int updateDept(SysDept dept); + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + public int deleteDeptById(Long deptId); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java new file mode 100644 index 0000000..9bc4f13 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java @@ -0,0 +1,60 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.common.core.domain.entity.SysDictData; + +/** + * 字典 业务层 + * + * @author ruoyi + */ +public interface ISysDictDataService +{ + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + public List<SysDictData> selectDictDataList(SysDictData dictData); + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + public String selectDictLabel(String dictType, String dictValue); + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + public SysDictData selectDictDataById(Long dictCode); + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + */ + public void deleteDictDataByIds(Long[] dictCodes); + + /** + * 新增保存字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int insertDictData(SysDictData dictData); + + /** + * 修改保存字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int updateDictData(SysDictData dictData); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java new file mode 100644 index 0000000..01c1c1d --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java @@ -0,0 +1,98 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.common.core.domain.entity.SysDictData; +import com.ruoyi.common.core.domain.entity.SysDictType; + +/** + * 字典 业务层 + * + * @author ruoyi + */ +public interface ISysDictTypeService +{ + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + public List<SysDictType> selectDictTypeList(SysDictType dictType); + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + public List<SysDictType> selectDictTypeAll(); + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + public List<SysDictData> selectDictDataByType(String dictType); + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + public SysDictType selectDictTypeById(Long dictId); + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + public SysDictType selectDictTypeByType(String dictType); + + /** + * 批量删除字典信息 + * + * @param dictIds 需要删除的字典ID + */ + public void deleteDictTypeByIds(Long[] dictIds); + + /** + * 加载字典缓存数据 + */ + public void loadingDictCache(); + + /** + * 清空字典缓存数据 + */ + public void clearDictCache(); + + /** + * 重置字典缓存数据 + */ + public void resetDictCache(); + + /** + * 新增保存字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int insertDictType(SysDictType dictType); + + /** + * 修改保存字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int updateDictType(SysDictType dictType); + + /** + * 校验字典类型称是否唯一 + * + * @param dictType 字典类型 + * @return 结果 + */ + public boolean checkDictTypeUnique(SysDictType dictType); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java new file mode 100644 index 0000000..ce3151d --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java @@ -0,0 +1,40 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.system.domain.SysLogininfor; + +/** + * 系统访问日志情况信息 服务层 + * + * @author ruoyi + */ +public interface ISysLogininforService +{ + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + public void insertLogininfor(SysLogininfor logininfor); + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + public List<SysLogininfor> selectLogininforList(SysLogininfor logininfor); + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return 结果 + */ + public int deleteLogininforByIds(Long[] infoIds); + + /** + * 清空系统登录日志 + */ + public void cleanLogininfor(); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java new file mode 100644 index 0000000..af96e14 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java @@ -0,0 +1,166 @@ +package com.ruoyi.system.service; + +import java.util.List; +import java.util.Set; +import com.ruoyi.common.core.domain.TreeSelect; +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.system.domain.vo.RouterVo; + +/** + * 菜单 业务层 + * + * @author ruoyi + */ +public interface ISysMenuService +{ + /** + * 根据用户查询系统菜单列表 + * + * @param userId 用户ID + * @return 菜单列表 + */ + public List<SysMenu> selectMenuList(Long userId); + + /** + * 根据用户查询系统菜单列表 + * + * @param menu 菜单信息 + * @param userId 用户ID + * @return 菜单列表 + */ + public List<SysMenu> selectMenuList(SysMenu menu, Long userId); + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + public Set<String> selectMenuPermsByUserId(Long userId); + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + public Set<String> selectMenuPermsByRoleId(Long roleId); + + /** + * 根据用户ID查询菜单树信息 + * + * @param userId 用户ID + * @return 菜单列表 + */ + public List<SysMenu> selectMenuTreeByUserId(Long userId); + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @return 选中菜单列表 + */ + public List<Long> selectMenuListByRoleId(Long roleId); + + /** + * 构建前端路由所需要的菜单 + * + * @param menus 菜单列表 + * @return 路由列表 + */ + public List<RouterVo> buildMenus(List<SysMenu> menus); + + /** + * 构建前端所需要树结构 + * + * @param menus 菜单列表 + * @return 树结构列表 + */ + public List<SysMenu> buildMenuTree(List<SysMenu> menus); + + /** + * 构建前端所需要下拉树结构 + * + * @param menus 菜单列表 + * @return 下拉树结构列表 + */ + public List<TreeSelect> buildMenuTreeSelect(List<SysMenu> menus); + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + public SysMenu selectMenuById(Long menuId); + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 true 存在 false 不存在 + */ + public boolean hasChildByMenuId(Long menuId); + + /** + * 查询菜单是否存在角色 + * + * @param menuId 菜单ID + * @return 结果 true 存在 false 不存在 + */ + public boolean checkMenuExistRole(Long menuId); + + /** + * 新增保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int insertMenu(SysMenu menu); + + /** + * 修改保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int updateMenu(SysMenu menu); + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int deleteMenuById(Long menuId); + + /** + * 校验菜单名称是否唯一 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean checkMenuNameUnique(SysMenu menu); + + /** + * 获取当前角色的菜单列表 + * @param roleId + * @return + */ + List<SysMenu> selectListByRoleId(Long roleId); + + /** + * 通过所有菜单id查询菜单 + * @param menusId + * @return + */ + List<SysMenu> getAllInIds(List<Long> menusId); + + /** + * 查询所有菜单 + * @return + */ + List<SysMenu> selectList(); + + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java new file mode 100644 index 0000000..47ce1b7 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java @@ -0,0 +1,60 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.system.domain.SysNotice; + +/** + * 公告 服务层 + * + * @author ruoyi + */ +public interface ISysNoticeService +{ + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + public SysNotice selectNoticeById(Long noticeId); + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + public List<SysNotice> selectNoticeList(SysNotice notice); + + /** + * 新增公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int insertNotice(SysNotice notice); + + /** + * 修改公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int updateNotice(SysNotice notice); + + /** + * 删除公告信息 + * + * @param noticeId 公告ID + * @return 结果 + */ + public int deleteNoticeById(Long noticeId); + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * @return 结果 + */ + public int deleteNoticeByIds(Long[] noticeIds); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java new file mode 100644 index 0000000..cd4ef10 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java @@ -0,0 +1,64 @@ +package com.ruoyi.system.service; + +import com.ruoyi.system.domain.SysOperLog; + +import java.util.List; + +/** + * 操作日志 服务层 + * + * @author ruoyi + */ +public interface ISysOperLogService +{ + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + public void insertOperlog(SysOperLog operLog); + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + public List<SysOperLog> selectOperLogList(SysOperLog operLog); + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + public int deleteOperLogByIds(List<Long> operIds); + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + public SysOperLog selectOperLogById(Long operId); + + /** + * 清空操作日志 + */ + public void cleanOperLog(); + + /** + * 操作日志分页列表 + * @param query + * @return + */ + + /** + * 操作日志分页列表 + * @param query + * @return + */ +// List<SysOperLogVO> selectOperLogPageList(SysOperLogQuery query); + +// void getLogDetail(List<SysOperLogVO> sysOperLogVOS); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPostService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPostService.java new file mode 100644 index 0000000..84779bf --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPostService.java @@ -0,0 +1,99 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.system.domain.SysPost; + +/** + * 岗位信息 服务层 + * + * @author ruoyi + */ +public interface ISysPostService +{ + /** + * 查询岗位信息集合 + * + * @param post 岗位信息 + * @return 岗位列表 + */ + public List<SysPost> selectPostList(SysPost post); + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + public List<SysPost> selectPostAll(); + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + public SysPost selectPostById(Long postId); + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + public List<Long> selectPostListByUserId(Long userId); + + /** + * 校验岗位名称 + * + * @param post 岗位信息 + * @return 结果 + */ + public boolean checkPostNameUnique(SysPost post); + + /** + * 校验岗位编码 + * + * @param post 岗位信息 + * @return 结果 + */ + public boolean checkPostCodeUnique(SysPost post); + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * @return 结果 + */ + public int countUserPostById(Long postId); + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + public int deletePostById(Long postId); + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * @return 结果 + */ + public int deletePostByIds(Long[] postIds); + + /** + * 新增保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int insertPost(SysPost post); + + /** + * 修改保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int updatePost(SysPost post); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java new file mode 100644 index 0000000..97b7374 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java @@ -0,0 +1,228 @@ +package com.ruoyi.system.service; + +import java.util.List; +import java.util.Set; + +import com.ruoyi.common.basic.PageInfo; +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.system.domain.SysUserRole; +import com.ruoyi.system.query.SysRoleQuery; + +/** + * 角色业务层 + * + * @author ruoyi + */ +public interface ISysRoleService +{ + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + public List<SysRole> selectRoleList(SysRole role); + + /** + * 根据用户ID查询角色列表 + * + * @param userId 用户ID + * @return 角色列表 + */ + public List<SysRole> selectRolesByUserId(Long userId); + + /** + * 根据用户ID查询角色权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + public Set<String> selectRolePermissionByUserId(Long userId); + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + public List<SysRole> selectRoleAll(); + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + public List<Long> selectRoleListByUserId(Long userId); + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + public SysRole selectRoleById(Long roleId); + + /** + * 校验角色名称是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + public boolean checkRoleNameUnique(SysRole role); + + /** + * 校验角色权限是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + public boolean checkRoleKeyUnique(SysRole role); + + /** + * 校验角色是否允许操作 + * + * @param role 角色信息 + */ + public void checkRoleAllowed(SysRole role); + + /** + * 校验角色是否有数据权限 + * + * @param roleId 角色id + */ + public void checkRoleDataScope(Long roleId); + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * @return 结果 + */ + public int countUserRoleByRoleId(Long roleId); + + /** + * 新增保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int insertRole(SysRole role); + + /** + * 修改保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int updateRole(SysRole role); + + /** + * 修改保存角色信息 + * + * @param dto 角色信息 + * @return 结果 + */ +// public int editRole(SysRoleDTO dto); + + /** + * 修改角色状态 + * + * @param role 角色信息 + * @return 结果 + */ + public int updateRoleStatus(SysRole role); + + /** + * 修改数据权限信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int authDataScope(SysRole role); + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleById(Long roleId); + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * @return 结果 + */ + public int deleteRoleByIds(List<Long> roleIds); + + /** + * 取消授权用户角色 + * + * @param userRole 用户和角色关联信息 + * @return 结果 + */ + public int deleteAuthUser(SysUserRole userRole); + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要取消授权的用户数据ID + * @return 结果 + */ + public int deleteAuthUsers(Long roleId, Long[] userIds); + + /** + * 批量选择授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要删除的用户数据ID + * @return 结果 + */ + public int insertAuthUsers(Long roleId, Long[] userIds); + + int selectCountByRoleName(String roleName); + +// void saveRole(SysRoleDTO dto); + + /** + * 判断是否存在该角色 + * + * @return + */ + Boolean isExit(Long id, String roleName); + + /** + * 角色列表 + * @param + * @return + */ +// List<SysRole> selectList(SysRoleQuery query); + + int selectCount(Integer status); + + void updateStatus(SysRole role); + + List<SysRole> selectListByDelFlag(Integer delFlag); + + /** + * + * @param userId + * @return + */ + SysRole selectRoleByUserId(Long userId); + + List<SysMenu> getMenuLevelList(List<Long> menusId); + + List<SysMenu> roleInfoFromUserId(Long userId); + + String selectByUserId(Long user_id); + + + PageInfo<SysRole> selectPageList(SysRoleQuery query); + + + List<SysRole> selectRoleByUserIds(List<String> roleIds); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserOnlineService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserOnlineService.java new file mode 100644 index 0000000..8eb5448 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserOnlineService.java @@ -0,0 +1,48 @@ +package com.ruoyi.system.service; + +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.system.domain.SysUserOnline; + +/** + * 在线用户 服务层 + * + * @author ruoyi + */ +public interface ISysUserOnlineService +{ + /** + * 通过登录地址查询信息 + * + * @param ipaddr 登录地址 + * @param user 用户信息 + * @return 在线用户信息 + */ + public SysUserOnline selectOnlineByIpaddr(String ipaddr, LoginUser user); + + /** + * 通过用户名称查询信息 + * + * @param userName 用户名称 + * @param user 用户信息 + * @return 在线用户信息 + */ + public SysUserOnline selectOnlineByUserName(String userName, LoginUser user); + + /** + * 通过登录地址/用户名称查询信息 + * + * @param ipaddr 登录地址 + * @param userName 用户名称 + * @param user 用户信息 + * @return 在线用户信息 + */ + public SysUserOnline selectOnlineByInfo(String ipaddr, String userName, LoginUser user); + + /** + * 设置在线用户信息 + * + * @param user 用户信息 + * @return 在线用户 + */ + public SysUserOnline loginUserToUserOnline(LoginUser user); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java new file mode 100644 index 0000000..32663e9 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java @@ -0,0 +1,255 @@ +package com.ruoyi.system.service; + +import com.ruoyi.common.core.domain.entity.SysUser; + +import java.util.List; + +/** + * 用户 业务层 + * + * @author ruoyi + */ +public interface ISysUserService +{ + /** + * 根据条件分页查询用户列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List<SysUser> selectUserList(SysUser user); + + /** + * 根据条件分页查询已分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List<SysUser> selectAllocatedList(SysUser user); + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List<SysUser> selectUnallocatedList(SysUser user); + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + public SysUser selectUserByUserName(String userName); + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + public SysUser selectUserById(Long userId); + + /** + * 根据用户ID查询用户所属角色组 + * + * @param userName 用户名 + * @return 结果 + */ + public String selectUserRoleGroup(String userName); + + /** + * 根据用户ID查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + public String selectUserPostGroup(String userName); + + /** + * 校验用户名称是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + public boolean checkUserNameUnique(SysUser user); + + /** + * 校验手机号码是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + public boolean checkPhoneUnique(SysUser user); + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + public boolean checkEmailUnique(SysUser user); + + /** + * 校验用户是否允许操作 + * + * @param user 用户信息 + */ + public void checkUserAllowed(SysUser user); + + /** + * 校验用户是否有数据权限 + * + * @param userId 用户id + */ + public void checkUserDataScope(Long userId); + + /** + * 新增用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int insertUser(SysUser user); + + /** + * 注册用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public boolean registerUser(SysUser user); + + /** + * 修改用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUser(SysUser user); + + /** + * 用户授权角色 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + public void insertUserAuth(Long userId, Long[] roleIds); + + /** + * 修改用户状态 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUserStatus(SysUser user); + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUserProfile(SysUser user); + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * @return 结果 + */ + public boolean updateUserAvatar(String userName, String avatar); + + /** + * 重置用户密码 + * + * @param user 用户信息 + * @return 结果 + */ + public int resetPwd(SysUser user); + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * @return 结果 + */ + public int resetUserPwd(String userName, String password); + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserById(Long userId); + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * @return 结果 + */ + public int deleteUserByIds(List<Long> userIds); + + /** + * 导入用户数据 + * + * @param userList 用户数据列表 + * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据 + * @param operName 操作用户 + * @return 结果 + */ + public String importUser(List<SysUser> userList, Boolean isUpdateSupport, String operName); + + List<SysUser> selectList(); + + Integer selectCount(Integer status); + + /** + * 获取用户列表 + * @param query + * @return + */ +// List<SysUserVO> selectUserPageList(SysUserQuery query); + + /** + * 获取用户黑名单列表 + * @param + * @return + */ +// List<SysUserVO> selectBlackPageList(SysUserQuery query); + + List<SysUser> selectListByNamePhone(String name); + + +// UserInfoVo userInfo(Long userId); + + SysUser selectByPhone(String phonenumber); + /** + * 通过名字集合查询用户 + * @param names + * @return + */ + List<SysUser> selectUserByUserNameList(List<String> names); + +// UserInfoVo getUserInfoBy(String singleNum); + + Long getUserRole(Long userId); + + int updateUserIfBlack(List<Long> ids); + + /** + * 查询所有用户(包含删除的) + * @return + */ + List<SysUser> selectAllList(); + + + void updatePassword(Long id, String s); + + long selectIdByPhone(String phonenumber); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/TbAccountDetailService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/TbAccountDetailService.java new file mode 100644 index 0000000..bc18c96 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/TbAccountDetailService.java @@ -0,0 +1,18 @@ +package com.ruoyi.system.service; + + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.system.model.TbAccountDetail; + +/** + * <p> + * 账户明细表 服务类 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +public interface TbAccountDetailService extends IService<TbAccountDetail> { + + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/TbAddressService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/TbAddressService.java new file mode 100644 index 0000000..bf26d2f --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/TbAddressService.java @@ -0,0 +1,17 @@ +package com.ruoyi.system.service; + + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.system.model.TbAddress; + +/** + * <p> + * 用户地址表 服务类 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +public interface TbAddressService extends IService<TbAddress> { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/TbBankService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/TbBankService.java new file mode 100644 index 0000000..f976fb6 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/TbBankService.java @@ -0,0 +1,16 @@ +package com.ruoyi.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.system.model.TbBank; + +/** + * <p> + * 用户卡表 服务类 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +public interface TbBankService extends IService<TbBank> { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/TbCompanyService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/TbCompanyService.java new file mode 100644 index 0000000..bad2523 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/TbCompanyService.java @@ -0,0 +1,17 @@ +package com.ruoyi.system.service; + + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.system.model.TbCompany; + +/** + * <p> + * 公司消息表 服务类 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +public interface TbCompanyService extends IService<TbCompany> { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/TbMessageService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/TbMessageService.java new file mode 100644 index 0000000..d836fa2 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/TbMessageService.java @@ -0,0 +1,17 @@ +package com.ruoyi.system.service; + + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.system.model.TbMessage; + +/** + * <p> + * 消息表 服务类 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +public interface TbMessageService extends IService<TbMessage> { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/TbOpeningBankService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/TbOpeningBankService.java new file mode 100644 index 0000000..1c2f5e0 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/TbOpeningBankService.java @@ -0,0 +1,17 @@ +package com.ruoyi.system.service; + + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.system.model.TbOpeningBank; + +/** + * <p> + * 开户行表 服务类 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +public interface TbOpeningBankService extends IService<TbOpeningBank> { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/TbPermitService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/TbPermitService.java new file mode 100644 index 0000000..cd0b1c6 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/TbPermitService.java @@ -0,0 +1,17 @@ +package com.ruoyi.system.service; + + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.system.model.TbPermit; + +/** + * <p> + * 许可证表 服务类 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +public interface TbPermitService extends IService<TbPermit> { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/TbRegionService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/TbRegionService.java new file mode 100644 index 0000000..0d0b145 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/TbRegionService.java @@ -0,0 +1,25 @@ +package com.ruoyi.system.service; + + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.system.model.TbRegion; +import com.ruoyi.system.vo.RegionVo; + +import java.util.List; + +/** + * <p> + * 省市区三级联动 服务类 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +public interface TbRegionService extends IService<TbRegion> { + + List<RegionVo> listCityVo(); + + + + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/TbUserService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/TbUserService.java new file mode 100644 index 0000000..5a0a359 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/TbUserService.java @@ -0,0 +1,17 @@ +package com.ruoyi.system.service; + + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.system.model.TbUser; + +/** + * <p> + * 服务类 + * </p> + * + * @author administrator + * @since 2025-05-23 + */ +public interface TbUserService extends IService<TbUser> { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/TbWithdrawalService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/TbWithdrawalService.java new file mode 100644 index 0000000..a6a2c4b --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/TbWithdrawalService.java @@ -0,0 +1,17 @@ +package com.ruoyi.system.service; + + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ruoyi.system.model.TbWithdrawal; + +/** + * <p> + * 提现申请表 服务类 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +public interface TbWithdrawalService extends IService<TbWithdrawal> { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java new file mode 100644 index 0000000..fb6113c --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java @@ -0,0 +1,232 @@ +package com.ruoyi.system.service.impl; + +import java.util.Collection; +import java.util.List; +import javax.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.annotation.DataSource; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.enums.DataSourceType; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.SysConfig; +import com.ruoyi.system.mapper.SysConfigMapper; +import com.ruoyi.system.service.ISysConfigService; + +/** + * 参数配置 服务层实现 + * + * @author ruoyi + */ +@Service +public class SysConfigServiceImpl implements ISysConfigService +{ + @Autowired + private SysConfigMapper configMapper; + + @Autowired + private RedisCache redisCache; + + /** + * 项目启动时,初始化参数到缓存 + */ + @PostConstruct + public void init() + { + loadingConfigCache(); + } + + /** + * 查询参数配置信息 + * + * @param configId 参数配置ID + * @return 参数配置信息 + */ + @Override + @DataSource(DataSourceType.MASTER) + public SysConfig selectConfigById(Long configId) + { + SysConfig config = new SysConfig(); + config.setConfigId(configId); + return configMapper.selectConfig(config); + } + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数key + * @return 参数键值 + */ + @Override + public String selectConfigByKey(String configKey) + { + String configValue = Convert.toStr(redisCache.getCacheObject(getCacheKey(configKey))); + if (StringUtils.isNotEmpty(configValue)) + { + return configValue; + } + SysConfig config = new SysConfig(); + config.setConfigKey(configKey); + SysConfig retConfig = configMapper.selectConfig(config); + if (StringUtils.isNotNull(retConfig)) + { + redisCache.setCacheObject(getCacheKey(configKey), retConfig.getConfigValue()); + return retConfig.getConfigValue(); + } + return StringUtils.EMPTY; + } + + /** + * 获取验证码开关 + * + * @return true开启,false关闭 + */ + @Override + public boolean selectCaptchaEnabled() + { + String captchaEnabled = selectConfigByKey("sys.account.captchaEnabled"); + if (StringUtils.isEmpty(captchaEnabled)) + { + return true; + } + return Convert.toBool(captchaEnabled); + } + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + @Override + public List<SysConfig> selectConfigList(SysConfig config) + { + return configMapper.selectConfigList(config); + } + + /** + * 新增参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + @Override + public int insertConfig(SysConfig config) + { + int row = configMapper.insertConfig(config); + if (row > 0) + { + redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue()); + } + return row; + } + + /** + * 修改参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + @Override + public int updateConfig(SysConfig config) + { + SysConfig temp = configMapper.selectConfigById(config.getConfigId()); + if (!StringUtils.equals(temp.getConfigKey(), config.getConfigKey())) + { + redisCache.deleteObject(getCacheKey(temp.getConfigKey())); + } + + int row = configMapper.updateConfig(config); + if (row > 0) + { + redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue()); + } + return row; + } + + /** + * 批量删除参数信息 + * + * @param configIds 需要删除的参数ID + */ + @Override + public void deleteConfigByIds(Long[] configIds) + { + for (Long configId : configIds) + { + SysConfig config = selectConfigById(configId); + if (StringUtils.equals(UserConstants.YES, config.getConfigType())) + { + throw new ServiceException(String.format("内置参数【%1$s】不能删除 ", config.getConfigKey())); + } + configMapper.deleteConfigById(configId); + redisCache.deleteObject(getCacheKey(config.getConfigKey())); + } + } + + /** + * 加载参数缓存数据 + */ + @Override + public void loadingConfigCache() + { + List<SysConfig> configsList = configMapper.selectConfigList(new SysConfig()); +// for (SysConfig config : configsList) +// { +// redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue()); +// } + } + + /** + * 清空参数缓存数据 + */ + @Override + public void clearConfigCache() + { + Collection<String> keys = redisCache.keys(CacheConstants.SYS_CONFIG_KEY + "*"); + redisCache.deleteObject(keys); + } + + /** + * 重置参数缓存数据 + */ + @Override + public void resetConfigCache() + { + clearConfigCache(); + loadingConfigCache(); + } + + /** + * 校验参数键名是否唯一 + * + * @param config 参数配置信息 + * @return 结果 + */ + @Override + public boolean checkConfigKeyUnique(SysConfig config) + { + Long configId = StringUtils.isNull(config.getConfigId()) ? -1L : config.getConfigId(); + SysConfig info = configMapper.checkConfigKeyUnique(config.getConfigKey()); + if (StringUtils.isNotNull(info) && info.getConfigId().longValue() != configId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 设置cache key + * + * @param configKey 参数键 + * @return 缓存键key + */ + private String getCacheKey(String configKey) + { + return CacheConstants.SYS_CONFIG_KEY + configKey; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java new file mode 100644 index 0000000..d7449eb --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java @@ -0,0 +1,339 @@ +package com.ruoyi.system.service.impl; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; + +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.text.Convert; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.system.mapper.SysDeptMapper; +import com.ruoyi.system.mapper.SysRoleMapper; +import com.ruoyi.system.service.ISysDeptService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 部门管理 服务实现 + * + * @author ruoyi + */ +@Service +public class SysDeptServiceImpl implements ISysDeptService +{ + @Autowired + private SysDeptMapper deptMapper; + + @Autowired + private SysRoleMapper roleMapper; + + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + @Override + @DataScope(deptAlias = "d") + public List<SysDept> selectDeptList(SysDept dept) + { + return deptMapper.selectDeptList(dept); + } + + /** + * 查询部门树结构信息 + * + * @param dept 部门信息 + * @return 部门树信息集合 + */ + @Override + public List<TreeSelect> selectDeptTreeList(SysDept dept) + { + List<SysDept> depts = SpringUtils.getAopProxy(this).selectDeptList(dept); + return buildDeptTreeSelect(depts); + } + + /** + * 构建前端所需要树结构 + * + * @param depts 部门列表 + * @return 树结构列表 + */ + @Override + public List<SysDept> buildDeptTree(List<SysDept> depts) + { + List<SysDept> returnList = new ArrayList<SysDept>(); + List<Long> tempList = depts.stream().map(SysDept::getDeptId).collect(Collectors.toList()); + for (SysDept dept : depts) + { + // 如果是顶级节点, 遍历该父节点的所有子节点 + if (!tempList.contains(dept.getParentId())) + { + recursionFn(depts, dept); + returnList.add(dept); + } + } + if (returnList.isEmpty()) + { + returnList = depts; + } + return returnList; + } + + /** + * 构建前端所需要下拉树结构 + * + * @param depts 部门列表 + * @return 下拉树结构列表 + */ + @Override + public List<TreeSelect> buildDeptTreeSelect(List<SysDept> depts) + { + List<SysDept> deptTrees = buildDeptTree(depts); + return deptTrees.stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @return 选中部门列表 + */ + @Override + public List<Long> selectDeptListByRoleId(Long roleId) + { + SysRole role = roleMapper.selectRoleById(roleId); + return deptMapper.selectDeptListByRoleId(roleId, role.isDeptCheckStrictly()); + } + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + @Override + public SysDept selectDeptById(Long deptId) + { + return deptMapper.selectDeptById(deptId); + } + + /** + * 根据ID查询所有子部门(正常状态) + * + * @param deptId 部门ID + * @return 子部门数 + */ + @Override + public int selectNormalChildrenDeptById(Long deptId) + { + return deptMapper.selectNormalChildrenDeptById(deptId); + } + + /** + * 是否存在子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + @Override + public boolean hasChildByDeptId(Long deptId) + { + int result = deptMapper.hasChildByDeptId(deptId); + return result > 0; + } + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 true 存在 false 不存在 + */ + @Override + public boolean checkDeptExistUser(Long deptId) + { + int result = deptMapper.checkDeptExistUser(deptId); + return result > 0; + } + + /** + * 校验部门名称是否唯一 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public boolean checkDeptNameUnique(SysDept dept) + { + Long deptId = StringUtils.isNull(dept.getDeptId()) ? -1L : dept.getDeptId(); + SysDept info = deptMapper.checkDeptNameUnique(dept.getDeptName(), dept.getParentId()); + if (StringUtils.isNotNull(info) && info.getDeptId().longValue() != deptId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验部门是否有数据权限 + * + * @param deptId 部门id + */ + @Override + public void checkDeptDataScope(Long deptId) + { + if (!SysUser.isAdmin(SecurityUtils.getUserId())) + { + SysDept dept = new SysDept(); + dept.setDeptId(deptId); + List<SysDept> depts = SpringUtils.getAopProxy(this).selectDeptList(dept); + if (StringUtils.isEmpty(depts)) + { + throw new ServiceException("没有权限访问部门数据!"); + } + } + } + + /** + * 新增保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public int insertDept(SysDept dept) + { + SysDept info = deptMapper.selectDeptById(dept.getParentId()); + // 如果父节点不为正常状态,则不允许新增子节点 + if (!UserConstants.DEPT_NORMAL.equals(info.getStatus())) + { + throw new ServiceException("部门停用,不允许新增"); + } + dept.setAncestors(info.getAncestors() + "," + dept.getParentId()); + return deptMapper.insertDept(dept); + } + + /** + * 修改保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public int updateDept(SysDept dept) + { + SysDept newParentDept = deptMapper.selectDeptById(dept.getParentId()); + SysDept oldDept = deptMapper.selectDeptById(dept.getDeptId()); + if (StringUtils.isNotNull(newParentDept) && StringUtils.isNotNull(oldDept)) + { + String newAncestors = newParentDept.getAncestors() + "," + newParentDept.getDeptId(); + String oldAncestors = oldDept.getAncestors(); + dept.setAncestors(newAncestors); + updateDeptChildren(dept.getDeptId(), newAncestors, oldAncestors); + } + int result = deptMapper.updateDept(dept); + if (UserConstants.DEPT_NORMAL.equals(dept.getStatus()) && StringUtils.isNotEmpty(dept.getAncestors()) + && !StringUtils.equals("0", dept.getAncestors())) + { + // 如果该部门是启用状态,则启用该部门的所有上级部门 + updateParentDeptStatusNormal(dept); + } + return result; + } + + /** + * 修改该部门的父级部门状态 + * + * @param dept 当前部门 + */ + private void updateParentDeptStatusNormal(SysDept dept) + { + String ancestors = dept.getAncestors(); + Long[] deptIds = Convert.toLongArray(ancestors); + deptMapper.updateDeptStatusNormal(deptIds); + } + + /** + * 修改子元素关系 + * + * @param deptId 被修改的部门ID + * @param newAncestors 新的父ID集合 + * @param oldAncestors 旧的父ID集合 + */ + public void updateDeptChildren(Long deptId, String newAncestors, String oldAncestors) + { + List<SysDept> children = deptMapper.selectChildrenDeptById(deptId); + for (SysDept child : children) + { + child.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors)); + } + if (children.size() > 0) + { + deptMapper.updateDeptChildren(children); + } + } + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + @Override + public int deleteDeptById(Long deptId) + { + return deptMapper.deleteDeptById(deptId); + } + + /** + * 递归列表 + */ + private void recursionFn(List<SysDept> list, SysDept t) + { + // 得到子节点列表 + List<SysDept> childList = getChildList(list, t); + t.setChildren(childList); + for (SysDept tChild : childList) + { + if (hasChild(list, tChild)) + { + recursionFn(list, tChild); + } + } + } + + /** + * 得到子节点列表 + */ + 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 (StringUtils.isNotNull(n.getParentId()) && n.getParentId().longValue() == t.getDeptId().longValue()) + { + tlist.add(n); + } + } + return tlist; + } + + /** + * 判断是否有子节点 + */ + private boolean hasChild(List<SysDept> list, SysDept t) + { + return getChildList(list, t).size() > 0; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java new file mode 100644 index 0000000..fced569 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java @@ -0,0 +1,111 @@ +package com.ruoyi.system.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.core.domain.entity.SysDictData; +import com.ruoyi.common.utils.DictUtils; +import com.ruoyi.system.mapper.SysDictDataMapper; +import com.ruoyi.system.service.ISysDictDataService; + +/** + * 字典 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysDictDataServiceImpl implements ISysDictDataService +{ + @Autowired + private SysDictDataMapper dictDataMapper; + + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + @Override + public List<SysDictData> selectDictDataList(SysDictData dictData) + { + return dictDataMapper.selectDictDataList(dictData); + } + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + @Override + public String selectDictLabel(String dictType, String dictValue) + { + return dictDataMapper.selectDictLabel(dictType, dictValue); + } + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + @Override + public SysDictData selectDictDataById(Long dictCode) + { + return dictDataMapper.selectDictDataById(dictCode); + } + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + */ + @Override + public void deleteDictDataByIds(Long[] dictCodes) + { + for (Long dictCode : dictCodes) + { + SysDictData data = selectDictDataById(dictCode); + dictDataMapper.deleteDictDataById(dictCode); + List<SysDictData> dictDatas = dictDataMapper.selectDictDataByType(data.getDictType()); + DictUtils.setDictCache(data.getDictType(), dictDatas); + } + } + + /** + * 新增保存字典数据信息 + * + * @param data 字典数据信息 + * @return 结果 + */ + @Override + public int insertDictData(SysDictData data) + { + int row = dictDataMapper.insertDictData(data); + if (row > 0) + { + List<SysDictData> dictDatas = dictDataMapper.selectDictDataByType(data.getDictType()); + DictUtils.setDictCache(data.getDictType(), dictDatas); + } + return row; + } + + /** + * 修改保存字典数据信息 + * + * @param data 字典数据信息 + * @return 结果 + */ + @Override + public int updateDictData(SysDictData data) + { + int row = dictDataMapper.updateDictData(data); + if (row > 0) + { + List<SysDictData> dictDatas = dictDataMapper.selectDictDataByType(data.getDictType()); + DictUtils.setDictCache(data.getDictType(), dictDatas); + } + return row; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java new file mode 100644 index 0000000..7fd9654 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java @@ -0,0 +1,223 @@ +package com.ruoyi.system.service.impl; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import javax.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +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.exception.ServiceException; +import com.ruoyi.common.utils.DictUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.mapper.SysDictDataMapper; +import com.ruoyi.system.mapper.SysDictTypeMapper; +import com.ruoyi.system.service.ISysDictTypeService; + +/** + * 字典 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysDictTypeServiceImpl implements ISysDictTypeService +{ + @Autowired + private SysDictTypeMapper dictTypeMapper; + + @Autowired + private SysDictDataMapper dictDataMapper; + + /** + * 项目启动时,初始化字典到缓存 + */ + @PostConstruct + public void init() + { + loadingDictCache(); + } + + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + @Override + public List<SysDictType> selectDictTypeList(SysDictType dictType) + { + return dictTypeMapper.selectDictTypeList(dictType); + } + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + @Override + public List<SysDictType> selectDictTypeAll() + { + return dictTypeMapper.selectDictTypeAll(); + } + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + @Override + public List<SysDictData> selectDictDataByType(String dictType) + { + List<SysDictData> dictDatas = DictUtils.getDictCache(dictType); + if (StringUtils.isNotEmpty(dictDatas)) + { + return dictDatas; + } + dictDatas = dictDataMapper.selectDictDataByType(dictType); + if (StringUtils.isNotEmpty(dictDatas)) + { + DictUtils.setDictCache(dictType, dictDatas); + return dictDatas; + } + return null; + } + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + @Override + public SysDictType selectDictTypeById(Long dictId) + { + return dictTypeMapper.selectDictTypeById(dictId); + } + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + @Override + public SysDictType selectDictTypeByType(String dictType) + { + return dictTypeMapper.selectDictTypeByType(dictType); + } + + /** + * 批量删除字典类型信息 + * + * @param dictIds 需要删除的字典ID + */ + @Override + public void deleteDictTypeByIds(Long[] dictIds) + { + for (Long dictId : dictIds) + { + SysDictType dictType = selectDictTypeById(dictId); + if (dictDataMapper.countDictDataByType(dictType.getDictType()) > 0) + { + throw new ServiceException(String.format("%1$s已分配,不能删除", dictType.getDictName())); + } + dictTypeMapper.deleteDictTypeById(dictId); + DictUtils.removeDictCache(dictType.getDictType()); + } + } + + /** + * 加载字典缓存数据 + */ + @Override + public void loadingDictCache() + { + SysDictData dictData = new SysDictData(); + dictData.setStatus("0"); + Map<String, List<SysDictData>> dictDataMap = dictDataMapper.selectDictDataList(dictData).stream().collect(Collectors.groupingBy(SysDictData::getDictType)); + for (Map.Entry<String, List<SysDictData>> entry : dictDataMap.entrySet()) + { + DictUtils.setDictCache(entry.getKey(), entry.getValue().stream().sorted(Comparator.comparing(SysDictData::getDictSort)).collect(Collectors.toList())); + } + } + + /** + * 清空字典缓存数据 + */ + @Override + public void clearDictCache() + { + DictUtils.clearDictCache(); + } + + /** + * 重置字典缓存数据 + */ + @Override + public void resetDictCache() + { + clearDictCache(); + loadingDictCache(); + } + + /** + * 新增保存字典类型信息 + * + * @param dict 字典类型信息 + * @return 结果 + */ + @Override + public int insertDictType(SysDictType dict) + { + int row = dictTypeMapper.insertDictType(dict); + if (row > 0) + { + DictUtils.setDictCache(dict.getDictType(), null); + } + return row; + } + + /** + * 修改保存字典类型信息 + * + * @param dict 字典类型信息 + * @return 结果 + */ + @Override + @Transactional + public int updateDictType(SysDictType dict) + { + SysDictType oldDict = dictTypeMapper.selectDictTypeById(dict.getDictId()); + dictDataMapper.updateDictDataType(oldDict.getDictType(), dict.getDictType()); + int row = dictTypeMapper.updateDictType(dict); + if (row > 0) + { + List<SysDictData> dictDatas = dictDataMapper.selectDictDataByType(dict.getDictType()); + DictUtils.setDictCache(dict.getDictType(), dictDatas); + } + return row; + } + + /** + * 校验字典类型称是否唯一 + * + * @param dict 字典类型 + * @return 结果 + */ + @Override + public boolean checkDictTypeUnique(SysDictType dict) + { + Long dictId = StringUtils.isNull(dict.getDictId()) ? -1L : dict.getDictId(); + SysDictType dictType = dictTypeMapper.checkDictTypeUnique(dict.getDictType()); + if (StringUtils.isNotNull(dictType) && dictType.getDictId().longValue() != dictId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java new file mode 100644 index 0000000..216aecb --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java @@ -0,0 +1,65 @@ +package com.ruoyi.system.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.system.domain.SysLogininfor; +import com.ruoyi.system.mapper.SysLogininforMapper; +import com.ruoyi.system.service.ISysLogininforService; + +/** + * 系统访问日志情况信息 服务层处理 + * + * @author ruoyi + */ +@Service +public class SysLogininforServiceImpl implements ISysLogininforService +{ + + @Autowired + private SysLogininforMapper logininforMapper; + + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + @Override + public void insertLogininfor(SysLogininfor logininfor) + { + logininforMapper.insertLogininfor(logininfor); + } + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + @Override + public List<SysLogininfor> selectLogininforList(SysLogininfor logininfor) + { + return logininforMapper.selectLogininforList(logininfor); + } + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return 结果 + */ + @Override + public int deleteLogininforByIds(Long[] infoIds) + { + return logininforMapper.deleteLogininforByIds(infoIds); + } + + /** + * 清空系统登录日志 + */ + @Override + public void cleanLogininfor() + { + logininforMapper.cleanLogininfor(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java new file mode 100644 index 0000000..c627233 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java @@ -0,0 +1,546 @@ +package com.ruoyi.system.service.impl; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.TreeSelect; +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.vo.MetaVo; +import com.ruoyi.system.domain.vo.RouterVo; +import com.ruoyi.system.mapper.SysMenuMapper; +import com.ruoyi.system.mapper.SysRoleMapper; +import com.ruoyi.system.mapper.SysRoleMenuMapper; +import com.ruoyi.system.service.ISysMenuService; + +/** + * 菜单 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysMenuServiceImpl implements ISysMenuService +{ + public static final String PREMISSION_STRING = "perms[\"{0}\"]"; + + @Autowired + private SysMenuMapper menuMapper; + + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysRoleMenuMapper roleMenuMapper; + + /** + * 根据用户查询系统菜单列表 + * + * @param userId 用户ID + * @return 菜单列表 + */ + @Override + public List<SysMenu> selectMenuList(Long userId) + { + return selectMenuList(new SysMenu(), userId); + } + + /** + * 查询系统菜单列表 + * + * @param menu 菜单信息 + * @return 菜单列表 + */ + @Override + public List<SysMenu> selectMenuList(SysMenu menu, Long userId) + { + List<SysMenu> menuList = null; + // 管理员显示所有菜单信息 + if (SysUser.isAdmin(userId)) + { + menuList = menuMapper.selectMenuList(menu); + } + else + { + menu.getParams().put("userId", userId); + menuList = menuMapper.selectMenuListByUserId(menu); + } + return menuList; + } + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + @Override + public Set<String> selectMenuPermsByUserId(Long userId) + { + List<String> perms = menuMapper.selectMenuPermsByUserId(userId); + Set<String> permsSet = new HashSet<>(); + for (String perm : perms) + { + if (StringUtils.isNotEmpty(perm)) + { + permsSet.addAll(Arrays.asList(perm.trim().split(","))); + } + } + return permsSet; + } + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + @Override + public Set<String> selectMenuPermsByRoleId(Long roleId) + { + List<String> perms = menuMapper.selectMenuPermsByRoleId(roleId); + Set<String> permsSet = new HashSet<>(); + for (String perm : perms) + { + if (StringUtils.isNotEmpty(perm)) + { + permsSet.addAll(Arrays.asList(perm.trim().split(","))); + } + } + return permsSet; + } + + /** + * 根据用户ID查询菜单 + * + * @param userId 用户名称 + * @return 菜单列表 + */ + @Override + public List<SysMenu> selectMenuTreeByUserId(Long userId) + { + List<SysMenu> menus = null; + if (SecurityUtils.isAdmin(userId)) + { + menus = menuMapper.selectMenuTreeAll(); + } + else + { + menus = menuMapper.selectMenuTreeByUserId(userId); + } + return getChildPerms(menus, 0); + } + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @return 选中菜单列表 + */ + @Override + public List<Long> selectMenuListByRoleId(Long roleId) + { + SysRole role = roleMapper.selectRoleById(roleId); + return menuMapper.selectMenuListByRoleId(roleId, role.isMenuCheckStrictly()); + } + + /** + * 构建前端路由所需要的菜单 + * + * @param menus 菜单列表 + * @return 路由列表 + */ + @Override + public List<RouterVo> buildMenus(List<SysMenu> menus) + { + List<RouterVo> routers = new LinkedList<RouterVo>(); + for (SysMenu menu : menus) + { + RouterVo router = new RouterVo(); + router.setHidden("1".equals(menu.getVisible())); + router.setName(getRouteName(menu)); + router.setPath(getRouterPath(menu)); + router.setComponent(getComponent(menu)); + router.setQuery(menu.getQuery()); + router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath())); + List<SysMenu> cMenus = menu.getChildren(); + if (StringUtils.isNotEmpty(cMenus) && UserConstants.TYPE_DIR.equals(menu.getMenuType())) + { + router.setAlwaysShow(true); + router.setRedirect("noRedirect"); + router.setChildren(buildMenus(cMenus)); + } + else if (isMenuFrame(menu)) + { + router.setMeta(null); + List<RouterVo> childrenList = new ArrayList<RouterVo>(); + RouterVo children = new RouterVo(); + children.setPath(menu.getPath()); + children.setComponent(menu.getComponent()); + children.setName(StringUtils.capitalize(menu.getPath())); + children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath())); + children.setQuery(menu.getQuery()); + childrenList.add(children); + router.setChildren(childrenList); + } + else if (menu.getParentId().intValue() == 0 && isInnerLink(menu)) + { + router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon())); + router.setPath("/"); + List<RouterVo> childrenList = new ArrayList<RouterVo>(); + RouterVo children = new RouterVo(); + String routerPath = innerLinkReplaceEach(menu.getPath()); + children.setPath(routerPath); + children.setComponent(UserConstants.INNER_LINK); + children.setName(StringUtils.capitalize(routerPath)); + children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), menu.getPath())); + childrenList.add(children); + router.setChildren(childrenList); + } + routers.add(router); + } + return routers; + } + + /** + * 构建前端所需要树结构 + * + * @param menus 菜单列表 + * @return 树结构列表 + */ + @Override + public List<SysMenu> buildMenuTree(List<SysMenu> menus) + { + List<SysMenu> returnList = new ArrayList<SysMenu>(); + List<Long> tempList = menus.stream().map(SysMenu::getMenuId).collect(Collectors.toList()); + for (Iterator<SysMenu> iterator = menus.iterator(); iterator.hasNext();) + { + SysMenu menu = (SysMenu) iterator.next(); + // 如果是顶级节点, 遍历该父节点的所有子节点 + if (!tempList.contains(menu.getParentId())) + { + recursionFn(menus, menu); + returnList.add(menu); + } + } + if (returnList.isEmpty()) + { + returnList = menus; + } + return returnList; + } + + /** + * 构建前端所需要下拉树结构 + * + * @param menus 菜单列表 + * @return 下拉树结构列表 + */ + @Override + public List<TreeSelect> buildMenuTreeSelect(List<SysMenu> menus) + { + List<SysMenu> menuTrees = buildMenuTree(menus); + return menuTrees.stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + @Override + public SysMenu selectMenuById(Long menuId) + { + return menuMapper.selectMenuById(menuId); + } + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public boolean hasChildByMenuId(Long menuId) + { + int result = menuMapper.hasChildByMenuId(menuId); + return result > 0; + } + + /** + * 查询菜单使用数量 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public boolean checkMenuExistRole(Long menuId) + { + int result = roleMenuMapper.checkMenuExistRole(menuId); + return result > 0; + } + + /** + * 新增保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public int insertMenu(SysMenu menu) + { + return menuMapper.insertMenu(menu); + } + + /** + * 修改保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public int updateMenu(SysMenu menu) + { + return menuMapper.updateMenu(menu); + } + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public int deleteMenuById(Long menuId) + { + return menuMapper.deleteMenuById(menuId); + } + + /** + * 校验菜单名称是否唯一 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public boolean checkMenuNameUnique(SysMenu menu) + { + Long menuId = StringUtils.isNull(menu.getMenuId()) ? -1L : menu.getMenuId(); + SysMenu info = menuMapper.checkMenuNameUnique(menu.getMenuName(), menu.getParentId()); + if (StringUtils.isNotNull(info) && info.getMenuId().longValue() != menuId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + @Override + public List<SysMenu> selectListByRoleId(Long roleId) { + return menuMapper.selectListByRoleId(roleId); + } + + @Override + public List<SysMenu> getAllInIds(List<Long> menusId) { + return menuMapper.getAllInIds(menusId); + } + + @Override + public List<SysMenu> selectList() { + return menuMapper.selectList(); + } + + /** + * 获取路由名称 + * + * @param menu 菜单信息 + * @return 路由名称 + */ + public String getRouteName(SysMenu menu) + { + String routerName = StringUtils.capitalize(menu.getPath()); + // 非外链并且是一级目录(类型为目录) + if (isMenuFrame(menu)) + { + routerName = StringUtils.EMPTY; + } + return routerName; + } + + /** + * 获取路由地址 + * + * @param menu 菜单信息 + * @return 路由地址 + */ + public String getRouterPath(SysMenu menu) + { + String routerPath = menu.getPath(); + // 内链打开外网方式 + if (menu.getParentId().intValue() != 0 && isInnerLink(menu)) + { + routerPath = innerLinkReplaceEach(routerPath); + } + // 非外链并且是一级目录(类型为目录) + if (0 == menu.getParentId().intValue() && UserConstants.TYPE_DIR.equals(menu.getMenuType()) + && UserConstants.NO_FRAME.equals(menu.getIsFrame())) + { + routerPath = "/" + menu.getPath(); + } + // 非外链并且是一级目录(类型为菜单) + else if (isMenuFrame(menu)) + { + routerPath = "/"; + } + return routerPath; + } + + /** + * 获取组件信息 + * + * @param menu 菜单信息 + * @return 组件信息 + */ + public String getComponent(SysMenu menu) + { + String component = UserConstants.LAYOUT; + if (StringUtils.isNotEmpty(menu.getComponent()) && !isMenuFrame(menu)) + { + component = menu.getComponent(); + } + else if (StringUtils.isEmpty(menu.getComponent()) && menu.getParentId().intValue() != 0 && isInnerLink(menu)) + { + component = UserConstants.INNER_LINK; + } + else if (StringUtils.isEmpty(menu.getComponent()) && isParentView(menu)) + { + component = UserConstants.PARENT_VIEW; + } + return component; + } + + /** + * 是否为菜单内部跳转 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean isMenuFrame(SysMenu menu) + { + return menu.getParentId().intValue() == 0 && UserConstants.TYPE_MENU.equals(menu.getMenuType()) + && menu.getIsFrame().equals(UserConstants.NO_FRAME); + } + + /** + * 是否为内链组件 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean isInnerLink(SysMenu menu) + { + return menu.getIsFrame().equals(UserConstants.NO_FRAME) && StringUtils.ishttp(menu.getPath()); + } + + /** + * 是否为parent_view组件 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean isParentView(SysMenu menu) + { + return menu.getParentId().intValue() != 0 && UserConstants.TYPE_DIR.equals(menu.getMenuType()); + } + + /** + * 根据父节点的ID获取所有子节点 + * + * @param list 分类表 + * @param parentId 传入的父节点ID + * @return String + */ + 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); + } + } + return returnList; + } + + /** + * 递归列表 + * + * @param list 分类表 + * @param t 子节点 + */ + private void recursionFn(List<SysMenu> list, SysMenu t) + { + // 得到子节点列表 + List<SysMenu> childList = getChildList(list, t); + t.setChildren(childList); + for (SysMenu tChild : childList) + { + if (hasChild(list, tChild)) + { + recursionFn(list, tChild); + } + } + } + + /** + * 得到子节点列表 + */ + 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); + } + } + return tlist; + } + + /** + * 判断是否有子节点 + */ + private boolean hasChild(List<SysMenu> list, SysMenu t) + { + return getChildList(list, t).size() > 0; + } + + /** + * 内链域名特殊字符替换 + * + * @return 替换后的内链域名 + */ + public String innerLinkReplaceEach(String path) + { + return StringUtils.replaceEach(path, new String[] { Constants.HTTP, Constants.HTTPS, Constants.WWW, "." }, + new String[] { "", "", "", "/" }); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java new file mode 100644 index 0000000..765438b --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java @@ -0,0 +1,92 @@ +package com.ruoyi.system.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.system.domain.SysNotice; +import com.ruoyi.system.mapper.SysNoticeMapper; +import com.ruoyi.system.service.ISysNoticeService; + +/** + * 公告 服务层实现 + * + * @author ruoyi + */ +@Service +public class SysNoticeServiceImpl implements ISysNoticeService +{ + @Autowired + private SysNoticeMapper noticeMapper; + + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + @Override + public SysNotice selectNoticeById(Long noticeId) + { + return noticeMapper.selectNoticeById(noticeId); + } + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + @Override + public List<SysNotice> selectNoticeList(SysNotice notice) + { + return noticeMapper.selectNoticeList(notice); + } + + /** + * 新增公告 + * + * @param notice 公告信息 + * @return 结果 + */ + @Override + public int insertNotice(SysNotice notice) + { + return noticeMapper.insertNotice(notice); + } + + /** + * 修改公告 + * + * @param notice 公告信息 + * @return 结果 + */ + @Override + public int updateNotice(SysNotice notice) + { + return noticeMapper.updateNotice(notice); + } + + /** + * 删除公告对象 + * + * @param noticeId 公告ID + * @return 结果 + */ + @Override + public int deleteNoticeById(Long noticeId) + { + return noticeMapper.deleteNoticeById(noticeId); + } + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * @return 结果 + */ + @Override + public int deleteNoticeByIds(Long[] noticeIds) + { + return noticeMapper.deleteNoticeByIds(noticeIds); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java new file mode 100644 index 0000000..15f36b1 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java @@ -0,0 +1,252 @@ +package com.ruoyi.system.service.impl; + +import com.ruoyi.common.basic.PageInfo; +import com.ruoyi.system.domain.SysOperLog; +import com.ruoyi.system.mapper.SysOperLogMapper; +import com.ruoyi.system.mapper.SysUserMapper; +import com.ruoyi.system.query.SysOperLogQuery; +import com.ruoyi.system.service.ISysOperLogService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 操作日志 服务层处理 + * + * @author ruoyi + */ +@Service +public class SysOperLogServiceImpl implements ISysOperLogService +{ + @Autowired + private SysOperLogMapper operLogMapper; + @Autowired + private SysUserMapper userMapper; + + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + @Override + public void insertOperlog(SysOperLog operLog) + { + operLogMapper.insertOperlog(operLog); + } + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + @Override + public List<SysOperLog> selectOperLogList(SysOperLog operLog) + { + return operLogMapper.selectOperLogList(operLog); + } + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + @Override + public int deleteOperLogByIds(List<Long> operIds) + { + return operLogMapper.deleteOperLogByIds(operIds); + } + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + @Override + public SysOperLog selectOperLogById(Long operId) + { + return operLogMapper.selectOperLogById(operId); + } + + /** + * 清空操作日志 + */ + @Override + public void cleanOperLog() + { + operLogMapper.cleanOperLog(); + } + + +// @Override +// public void getLogDetail(List<SysOperLogVO> sysOperLogVOS) { +// for (SysOperLogVO sysOperLogVO : sysOperLogVOS) { +// if(sysOperLogVO.getTitle().contains("单位")){ +// JSONObject jsonObject = JSONObject.parseObject(sysOperLogVO.getOperParam()); +// if(Objects.nonNull(jsonObject)){ +// String companyName = jsonObject.getString("companyName"); +// switch (sysOperLogVO.getBusinessType()){ +// case 1: +// // 新增 +// sysOperLogVO.setOperDetail("新增了<"+companyName+">单位"); +// break; +// case 2: +// // 编辑 +// sysOperLogVO.setOperDetail("编辑了<"+companyName+">单位"); +// break; +// } +// } +// } +// if(sysOperLogVO.getTitle().contains("部门")){ +// JSONObject jsonObject = JSONObject.parseObject(sysOperLogVO.getOperParam()); +// if(Objects.nonNull(jsonObject)){ +// String deptName = jsonObject.getString("deptName"); +// switch (sysOperLogVO.getBusinessType()){ +// case 1: +// // 新增 +// sysOperLogVO.setOperDetail("新增了<"+deptName+">部门"); +// break; +// case 2: +// // 编辑 +// sysOperLogVO.setOperDetail("编辑了<"+deptName+">部门"); +// break; +// } +// } +// } +// if(sysOperLogVO.getTitle().contains("角色信息")){ +// JSONObject jsonObject = JSONObject.parseObject(sysOperLogVO.getOperParam()); +// if(Objects.nonNull(jsonObject)){ +// String roleName = jsonObject.getString("roleName"); +// switch (sysOperLogVO.getBusinessType()){ +// case 1: +// // 新增 +// sysOperLogVO.setOperDetail("新增了<"+roleName+">角色"); +// break; +// case 2: +// // 编辑 +// sysOperLogVO.setOperDetail("编辑了<"+roleName+">角色"); +// break; +// } +// } +// } +// if(sysOperLogVO.getTitle().contains("用户信息")){ +// JSONObject jsonObject = JSONObject.parseObject(sysOperLogVO.getOperParam()); +// if(Objects.nonNull(jsonObject)){ +// String nickName = jsonObject.getString("nickName"); +// switch (sysOperLogVO.getBusinessType()){ +// case 1: +// // 新增 +// sysOperLogVO.setOperDetail("新增了<"+nickName+">用户"); +// break; +// case 2: +// // 编辑 +// sysOperLogVO.setOperDetail("编辑了<"+nickName+">用户"); +// break; +// } +// } +// } +// if(sysOperLogVO.getTitle().contains("问题反馈")){ +// JSONObject jsonObject = JSONObject.parseObject(sysOperLogVO.getOperParam()); +// if(Objects.nonNull(jsonObject)){ +// String problemFeedback = jsonObject.getString("problemFeedback"); +// switch (sysOperLogVO.getBusinessType()){ +// case 1: +// // 新增 +// sysOperLogVO.setOperDetail("新增了<"+problemFeedback+">问题反馈"); +// break; +// case 2: +// // 编辑 +// sysOperLogVO.setOperDetail("编辑了<"+problemFeedback+">问题反馈"); +// break; +// } +// } +// } +// if(sysOperLogVO.getTitle().contains("现场作业")){ +// JSONObject jsonObject = JSONObject.parseObject(sysOperLogVO.getOperParam()); +// if(Objects.nonNull(jsonObject)){ +// String engineeringName = jsonObject.getString("engineeringName"); +// switch (sysOperLogVO.getBusinessType()){ +// case 1: +// // 新增 +// sysOperLogVO.setOperDetail("新增了<"+engineeringName+">现场作业"); +// break; +// case 2: +// // 编辑 +// sysOperLogVO.setOperDetail("编辑了<"+engineeringName+">现场作业"); +// break; +// case 10: +// // 现场作业修改时间 +// String startTime = jsonObject.getString("startTime"); +// String endTime = jsonObject.getString("endTime"); +// sysOperLogVO.setOperDetail("编辑了<"+engineeringName+">现场作业时间为:"+startTime+"~"+endTime); +// case 11: +// // 现场作业修改经理 +// Long projectManager = jsonObject.getLong("projectManager"); +// SysUser manager = userMapper.selectUserById(projectManager); +// sysOperLogVO.setOperDetail("编辑了<"+engineeringName+">现场作业经理为:"+manager.getNickName()); +// case 12: +// // 现场作业修改负责人 +// Long workHeader = jsonObject.getLong("workHeader"); +// SysUser header = userMapper.selectUserById(workHeader); +// sysOperLogVO.setOperDetail("编辑了<"+engineeringName+">现场作业负责人为:"+header.getNickName()); +// case 13: +// // 现场作业修改人员新增 +// JSONArray addUserIds = jsonObject.getJSONArray("addUserIds"); +// JSONArray borrowUserIds = jsonObject.getJSONArray("borrowUserIds"); +// List<Long> userIds = new ArrayList<>(); +// for (Object addUserId : addUserIds) { +// userIds.add(Long.valueOf((String.valueOf(addUserId)))); +// } +// for (Object borrowUserId : borrowUserIds) { +// userIds.add(Long.valueOf((String.valueOf(borrowUserId)))); +// } +// List<SysUser> userList = userMapper.selectUserByIds(userIds); +// List<String> nickNameList = userList.stream().map(SysUser::getNickName).collect(Collectors.toList()); +// String nickNames = nickNameList.stream().map(Object::toString).collect(Collectors.joining("、")); +// sysOperLogVO.setOperDetail("编辑了<"+engineeringName+">现场作业人员新增:"+nickNames); +// case 14: +// // 现场作业修改人员减少 +// Long reduceUserId = jsonObject.getLong("reduceUserId"); +// SysUser reduceUser = userMapper.selectUserById(reduceUserId); +// sysOperLogVO.setOperDetail("编辑了<"+engineeringName+">现场作业人员减少:"+reduceUser.getNickName()); +// } +// } +// } +// if(sysOperLogVO.getTitle().contains("培训类别")){ +// JSONObject jsonObject = JSONObject.parseObject(sysOperLogVO.getOperParam()); +// if(Objects.nonNull(jsonObject)){ +// String content = jsonObject.getString("content"); +// switch (sysOperLogVO.getBusinessType()){ +// case 1: +// // 新增 +// sysOperLogVO.setOperDetail("新增了<"+content+">培训类别"); +// break; +// case 2: +// // 编辑 +// sysOperLogVO.setOperDetail("编辑了<"+content+">培训类别"); +// break; +// } +// } +// } +// if(sysOperLogVO.getTitle().contains("培训信息")){ +// JSONObject jsonObject = JSONObject.parseObject(sysOperLogVO.getOperParam()); +// if(Objects.nonNull(jsonObject)){ +// String trainName = jsonObject.getString("trainName"); +// switch (sysOperLogVO.getBusinessType()){ +// case 1: +// // 新增 +// sysOperLogVO.setOperDetail("新增了<"+trainName+">培训信息"); +// break; +// case 2: +// // 编辑 +// sysOperLogVO.setOperDetail("编辑了<"+trainName+">培训信息"); +// break; +// } +// } +// } +// } +// } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java new file mode 100644 index 0000000..5e5fe06 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java @@ -0,0 +1,178 @@ +package com.ruoyi.system.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.SysPost; +import com.ruoyi.system.mapper.SysPostMapper; +import com.ruoyi.system.mapper.SysUserPostMapper; +import com.ruoyi.system.service.ISysPostService; + +/** + * 岗位信息 服务层处理 + * + * @author ruoyi + */ +@Service +public class SysPostServiceImpl implements ISysPostService +{ + @Autowired + private SysPostMapper postMapper; + + @Autowired + private SysUserPostMapper userPostMapper; + + /** + * 查询岗位信息集合 + * + * @param post 岗位信息 + * @return 岗位信息集合 + */ + @Override + public List<SysPost> selectPostList(SysPost post) + { + return postMapper.selectPostList(post); + } + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + @Override + public List<SysPost> selectPostAll() + { + return postMapper.selectPostAll(); + } + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + @Override + public SysPost selectPostById(Long postId) + { + return postMapper.selectPostById(postId); + } + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + @Override + public List<Long> selectPostListByUserId(Long userId) + { + return postMapper.selectPostListByUserId(userId); + } + + /** + * 校验岗位名称是否唯一 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public boolean checkPostNameUnique(SysPost post) + { + Long postId = StringUtils.isNull(post.getPostId()) ? -1L : post.getPostId(); + SysPost info = postMapper.checkPostNameUnique(post.getPostName()); + if (StringUtils.isNotNull(info) && info.getPostId().longValue() != postId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验岗位编码是否唯一 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public boolean checkPostCodeUnique(SysPost post) + { + Long postId = StringUtils.isNull(post.getPostId()) ? -1L : post.getPostId(); + SysPost info = postMapper.checkPostCodeUnique(post.getPostCode()); + if (StringUtils.isNotNull(info) && info.getPostId().longValue() != postId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * @return 结果 + */ + @Override + public int countUserPostById(Long postId) + { + return userPostMapper.countUserPostById(postId); + } + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + @Override + public int deletePostById(Long postId) + { + return postMapper.deletePostById(postId); + } + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * @return 结果 + */ + @Override + public int deletePostByIds(Long[] postIds) + { + for (Long postId : postIds) + { + SysPost post = selectPostById(postId); + if (countUserPostById(postId) > 0) + { + throw new ServiceException(String.format("%1$s已分配,不能删除", post.getPostName())); + } + } + return postMapper.deletePostByIds(postIds); + } + + /** + * 新增保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public int insertPost(SysPost post) + { + return postMapper.insertPost(post); + } + + /** + * 修改保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public int updatePost(SysPost post) + { + return postMapper.updatePost(post); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java new file mode 100644 index 0000000..ed4421b --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java @@ -0,0 +1,569 @@ +package com.ruoyi.system.service.impl; + +import java.util.*; +import java.util.stream.Collectors; + +import com.ruoyi.common.basic.PageInfo; +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.system.mapper.*; +import com.ruoyi.system.query.SysRoleQuery; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +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.exception.ServiceException; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.system.domain.SysRoleDept; +import com.ruoyi.system.domain.SysRoleMenu; +import com.ruoyi.system.domain.SysUserRole; +import com.ruoyi.system.service.ISysRoleService; +import org.springframework.util.CollectionUtils; + +/** + * 角色 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysRoleServiceImpl implements ISysRoleService +{ + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysRoleMenuMapper roleMenuMapper; + + @Autowired + private SysUserRoleMapper userRoleMapper; + + @Autowired + private SysRoleDeptMapper roleDeptMapper; + @Autowired + private SysMenuMapper menuMapper; + + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + @Override + public List<SysRole> selectRoleList(SysRole role) + { + return roleMapper.selectRoleList(role); + } + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * @return 角色列表 + */ + @Override + public List<SysRole> selectRolesByUserId(Long userId) + { + List<SysRole> userRoles = roleMapper.selectRolePermissionByUserId(userId); + List<SysRole> roles = selectRoleAll(); + for (SysRole role : roles) + { + for (SysRole userRole : userRoles) + { + if (role.getRoleId().longValue() == userRole.getRoleId().longValue()) + { + role.setFlag(true); + break; + } + } + } + return roles; + } + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + @Override + public Set<String> selectRolePermissionByUserId(Long userId) + { + List<SysRole> perms = roleMapper.selectRolePermissionByUserId(userId); + Set<String> permsSet = new HashSet<>(); + for (SysRole perm : perms) + { + if (StringUtils.isNotNull(perm)) + { + permsSet.addAll(Arrays.asList(perm.getRoleKey().trim().split(","))); + } + } + return permsSet; + } + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + @Override + public List<SysRole> selectRoleAll() + { + return this.selectRoleList(new SysRole()); + } + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + @Override + public List<Long> selectRoleListByUserId(Long userId) + { + return roleMapper.selectRoleListByUserId(userId); + } + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + @Override + public SysRole selectRoleById(Long roleId) + { + return roleMapper.selectRoleById(roleId); + } + + /** + * 校验角色名称是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public boolean checkRoleNameUnique(SysRole role) + { + Long roleId = StringUtils.isNull(role.getRoleId()) ? -1L : role.getRoleId(); + SysRole info = roleMapper.checkRoleNameUnique(role.getRoleName()); + if (StringUtils.isNotNull(info) && info.getRoleId().longValue() != roleId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验角色权限是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public boolean checkRoleKeyUnique(SysRole role) + { + Long roleId = StringUtils.isNull(role.getRoleId()) ? -1L : role.getRoleId(); + SysRole info = roleMapper.checkRoleKeyUnique(role.getRoleKey()); + if (StringUtils.isNotNull(info) && info.getRoleId().longValue() != roleId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验角色是否允许操作 + * + * @param role 角色信息 + */ + @Override + public void checkRoleAllowed(SysRole role) + { + if (StringUtils.isNotNull(role.getRoleId()) && role.isAdmin()) + { + throw new ServiceException("不允许操作超级管理员角色"); + } + } + + /** + * 校验角色是否有数据权限 + * + * @param roleId 角色id + */ + @Override + public void checkRoleDataScope(Long roleId) + { + if (!SysUser.isAdmin(SecurityUtils.getUserId())) + { + SysRole role = new SysRole(); + role.setRoleId(roleId); + List<SysRole> roles = SpringUtils.getAopProxy(this).selectRoleList(role); + if (StringUtils.isEmpty(roles)) + { + throw new ServiceException("没有权限访问角色数据!"); + } + } + } + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * @return 结果 + */ + @Override + public int countUserRoleByRoleId(Long roleId) + { + return userRoleMapper.countUserRoleByRoleId(roleId); + } + + /** + * 新增保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + @Transactional + public int insertRole(SysRole role) + { + // 新增角色信息 + roleMapper.insertRole(role); + return insertRoleMenu(role); + } + + /** + * 修改保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + @Transactional + public int updateRole(SysRole role) + { + // 修改角色信息 + roleMapper.updateRole(role); + // 删除角色与菜单关联 + roleMenuMapper.deleteRoleMenuByRoleId(role.getRoleId()); + return insertRoleMenu(role); + } + + /** + * 修改保存角色信息 + * + * @param dto 角色信息 + * @return 结果 + */ +// @Override +// @Transactional +// public int editRole(SysRoleDTO dto) +// { +// // 修改角色信息 +// SysRole sysRole = new SysRole(); +// sysRole.setRoleName(dto.getRoleName()); +// sysRole.setPostType(dto.getPostType()); +// roleMapper.updateRole(sysRole); +// // 删除角色与菜单关联 +// roleMenuMapper.deleteRoleMenuByRoleId(dto.getRoleId()); +// +// // 添加角色权限中间表 +// List<Long> menuIds = dto.getMenuIds(); +// List<SysRoleMenu> sysRoleMenus = new ArrayList<>(); +// for (Long menuId : menuIds) { +// SysRoleMenu sysRoleMenu = new SysRoleMenu(); +// sysRoleMenu.setRoleId(dto.getRoleId()); +// sysRoleMenu.setMenuId(menuId); +// sysRoleMenus.add(sysRoleMenu); +// } +// +// return roleMenuMapper.batchRoleMenu(sysRoleMenus); +// } + + /** + * 修改角色状态 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public int updateRoleStatus(SysRole role) + { + return roleMapper.updateRole(role); + } + + /** + * 修改数据权限信息 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + @Transactional + public int authDataScope(SysRole role) + { + // 修改角色信息 + roleMapper.updateRole(role); + // 删除角色与部门关联 + roleDeptMapper.deleteRoleDeptByRoleId(role.getRoleId()); + // 新增角色和部门信息(数据权限) + return insertRoleDept(role); + } + + /** + * 新增角色菜单信息 + * + * @param role 角色对象 + */ + public int insertRoleMenu(SysRole role) + { + int rows = 1; + // 新增用户与角色管理 + List<SysRoleMenu> list = new ArrayList<SysRoleMenu>(); + for (Long menuId : role.getMenuIds()) + { + SysRoleMenu rm = new SysRoleMenu(); + rm.setRoleId(role.getRoleId()); + rm.setMenuId(menuId); + list.add(rm); + } + if (list.size() > 0) + { + rows = roleMenuMapper.batchRoleMenu(list); + } + return rows; + } + + /** + * 新增角色部门信息(数据权限) + * + * @param role 角色对象 + */ + public int insertRoleDept(SysRole role) + { + int rows = 1; + // 新增角色与部门(数据权限)管理 + List<SysRoleDept> list = new ArrayList<SysRoleDept>(); + for (Long deptId : role.getDeptIds()) + { + SysRoleDept rd = new SysRoleDept(); + rd.setRoleId(role.getRoleId()); + rd.setDeptId(deptId); + list.add(rd); + } + if (list.size() > 0) + { + rows = roleDeptMapper.batchRoleDept(list); + } + return rows; + } + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + @Override + @Transactional + public int deleteRoleById(Long roleId) + { + // 删除角色与菜单关联 + roleMenuMapper.deleteRoleMenuByRoleId(roleId); + // 删除角色与部门关联 + roleDeptMapper.deleteRoleDeptByRoleId(roleId); + return roleMapper.deleteRoleById(roleId); + } + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * @return 结果 + */ + @Override + @Transactional + public int deleteRoleByIds(List<Long> roleIds) + { + for (Long roleId : roleIds) + { + SysRole role = selectRoleById(roleId); + if (countUserRoleByRoleId(roleId) > 0) + { + throw new ServiceException(String.format("%1$s已分配,不能删除", role.getRoleName())); + } + } + // 删除角色与菜单关联 + roleMenuMapper.deleteRoleMenu(roleIds); + return roleMapper.deleteRoleByIds(roleIds); + } + + /** + * 取消授权用户角色 + * + * @param userRole 用户和角色关联信息 + * @return 结果 + */ + @Override + public int deleteAuthUser(SysUserRole userRole) + { + return userRoleMapper.deleteUserRoleInfo(userRole); + } + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要取消授权的用户数据ID + * @return 结果 + */ + @Override + public int deleteAuthUsers(Long roleId, Long[] userIds) + { + return userRoleMapper.deleteUserRoleInfos(roleId, userIds); + } + + /** + * 批量选择授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要授权的用户数据ID + * @return 结果 + */ + @Override + public int insertAuthUsers(Long roleId, Long[] userIds) + { + // 新增用户与角色管理 + List<SysUserRole> list = new ArrayList<SysUserRole>(); + for (Long userId : userIds) + { + SysUserRole ur = new SysUserRole(); + ur.setUserId(userId); + ur.setRoleId(roleId); + list.add(ur); + } + return userRoleMapper.batchUserRole(list); + } + + @Override + public int selectCountByRoleName(String roleName) { + return roleMapper.selectCountByRoleName(roleName); + } + +// @Override +// public void saveRole(SysRoleDTO dto) { +// +// // 添加角色 +// SysRole sysRole = new SysRole(); +// sysRole.setRoleName(dto.getRoleName()); +// sysRole.setPostType(dto.getPostType()); +// roleMapper.insertRole(sysRole); +// +// // 添加角色权限中间表 +// List<Long> menuIds = dto.getMenuIds(); +// List<SysRoleMenu> sysRoleMenus = new ArrayList<>(); +// for (Long menuId : menuIds) { +// SysRoleMenu sysRoleMenu = new SysRoleMenu(); +// sysRoleMenu.setRoleId(sysRole.getRoleId()); +// sysRoleMenu.setMenuId(menuId); +// sysRoleMenus.add(sysRoleMenu); +// } +// roleMenuMapper.batchRoleMenu(sysRoleMenus); +// } + + @Override + public Boolean isExit(Long id, String roleName) { + int count = this.selectCountByRoleName(roleName); + if (StringUtils.isNotNull(id)) { + // 修改 + SysRole sysRole = roleMapper.selectRoleById(id); + return Objects.nonNull(sysRole) && !sysRole.getRoleName().equals(roleName) && count > 0; + } else { + // 新增 + return count > 0; + } + } + +// @Override +// public List<SysRole> selectList(SysRoleQuery query) { +// return roleMapper.selectList(query); +// } + + @Override + public int selectCount(Integer status) { + return roleMapper.selectCount(status); + } + + @Override + public void updateStatus(SysRole role) { + roleMapper.updateStatus(role); + } + + @Override + public List<SysRole> selectListByDelFlag(Integer delFlag) { + return roleMapper.selectListByDelFlag(delFlag); + } + + @Override + public SysRole selectRoleByUserId(Long userId) { + return roleMapper.selectRoleByUserId(userId); + } + + @Override + public List<SysMenu> getMenuLevelList(List<Long> menusId) { + //获取当前的权限菜单 + List<SysMenu> all = menuMapper.getAllInIds(menusId); + // 第三级 + List<SysMenu> s3 = all.stream().filter(e -> e.getMenuType().equals("F")).collect(Collectors.toList()); + // 第二级 + List<SysMenu> s2 = all.stream().filter(e -> e.getMenuType().equals("C")).collect(Collectors.toList()); + // 第一级 + List<SysMenu> s1 = all.stream().filter(e -> e.getMenuType().equals("M")).collect(Collectors.toList()); + + for (SysMenu menu : s2) { + List<SysMenu> collect = s3.stream().filter(e -> e.getParentId().equals(menu.getMenuId())).collect(Collectors.toList()); + menu.setChildren(collect); + } + for (SysMenu menu : s1) { + List<SysMenu> collect = s2.stream().filter(e -> e.getParentId().equals(menu.getMenuId())).collect(Collectors.toList()); + menu.setChildren(collect); + } + return s1; + } + + @Override + public List<SysMenu> roleInfoFromUserId(Long userId) { + SysRole sysRole = roleMapper.selectRoleByUserId(userId); + // 获取当前角色的菜单列表 + List<SysMenu> menus = menuMapper.selectListByRoleId(sysRole.getRoleId()); + if(menus.size()==0){ + return new ArrayList<>(); + } + List<Long> menusId = menus.stream().map(SysMenu::getMenuId).collect(Collectors.toList()); + // 获取当前的权限菜单(有层级) + return this.getMenuLevelList(menusId); + } + + @Override + public String selectByUserId(Long user_id) { + return roleMapper.selectByUserId(user_id); + } + + + @Override + public PageInfo<SysRole> selectPageList(SysRoleQuery query) { + PageInfo<SysRole> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize()); + List<SysRole> list = roleMapper.selectPageList(query,pageInfo); + pageInfo.setRecords(list); + return pageInfo; + } + + + @Override + public List<SysRole> selectRoleByUserIds(List<String> roleIds) { + return roleMapper.selectRoleByUserIds(roleIds); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java new file mode 100644 index 0000000..f80a877 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java @@ -0,0 +1,96 @@ +package com.ruoyi.system.service.impl; + +import org.springframework.stereotype.Service; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.SysUserOnline; +import com.ruoyi.system.service.ISysUserOnlineService; + +/** + * 在线用户 服务层处理 + * + * @author ruoyi + */ +@Service +public class SysUserOnlineServiceImpl implements ISysUserOnlineService +{ + /** + * 通过登录地址查询信息 + * + * @param ipaddr 登录地址 + * @param user 用户信息 + * @return 在线用户信息 + */ + @Override + public SysUserOnline selectOnlineByIpaddr(String ipaddr, LoginUser user) + { + if (StringUtils.equals(ipaddr, user.getIpaddr())) + { + return loginUserToUserOnline(user); + } + return null; + } + + /** + * 通过用户名称查询信息 + * + * @param userName 用户名称 + * @param user 用户信息 + * @return 在线用户信息 + */ + @Override + public SysUserOnline selectOnlineByUserName(String userName, LoginUser user) + { + if (StringUtils.equals(userName, user.getUsername())) + { + return loginUserToUserOnline(user); + } + return null; + } + + /** + * 通过登录地址/用户名称查询信息 + * + * @param ipaddr 登录地址 + * @param userName 用户名称 + * @param user 用户信息 + * @return 在线用户信息 + */ + @Override + public SysUserOnline selectOnlineByInfo(String ipaddr, String userName, LoginUser user) + { + if (StringUtils.equals(ipaddr, user.getIpaddr()) && StringUtils.equals(userName, user.getUsername())) + { + return loginUserToUserOnline(user); + } + return null; + } + + /** + * 设置在线用户信息 + * + * @param user 用户信息 + * @return 在线用户 + */ + @Override + public SysUserOnline loginUserToUserOnline(LoginUser user) + { + if (StringUtils.isNull(user) || StringUtils.isNull(user.getUser())) + { + return null; + } + SysUserOnline sysUserOnline = new SysUserOnline(); + sysUserOnline.setTokenId(user.getToken()); + sysUserOnline.setUserName(user.getUsername()); + sysUserOnline.setIpaddr(user.getIpaddr()); + sysUserOnline.setLoginLocation(user.getLoginLocation()); + sysUserOnline.setBrowser(user.getBrowser()); + sysUserOnline.setOs(user.getOs()); + sysUserOnline.setLoginTime(user.getLoginTime()); + if (StringUtils.isNotNull(user.getUser().getDept())) + { + sysUserOnline.setDeptName(user.getUser().getDept().getDeptName()); + } + return sysUserOnline; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java new file mode 100644 index 0000000..726d184 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java @@ -0,0 +1,661 @@ +package com.ruoyi.system.service.impl; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ruoyi.common.annotation.DataScope; +import com.ruoyi.common.basic.PageInfo; +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.exception.ServiceException; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.bean.BeanValidators; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.system.domain.SysPost; +import com.ruoyi.system.domain.SysUserPost; +import com.ruoyi.system.domain.SysUserRole; +import com.ruoyi.system.mapper.*; +import com.ruoyi.system.query.SysUserQuery; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.system.service.ISysUserService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; + +import javax.validation.Validator; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * 用户 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysUserServiceImpl implements ISysUserService +{ + private static final Logger log = LoggerFactory.getLogger(SysUserServiceImpl.class); + + @Autowired + private SysUserMapper userMapper; + + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysPostMapper postMapper; + + @Autowired + private SysUserRoleMapper userRoleMapper; + + @Autowired + private SysUserPostMapper userPostMapper; + + @Autowired + private ISysConfigService configService; + + @Autowired + protected Validator validator; + + /** + * 根据条件分页查询用户列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + @DataScope(deptAlias = "d", userAlias = "u") + public List<SysUser> selectUserList(SysUser user) + { + return userMapper.selectUserList(user); + } + + /** + * 根据条件分页查询已分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + @DataScope(deptAlias = "d", userAlias = "u") + public List<SysUser> selectAllocatedList(SysUser user) + { + return userMapper.selectAllocatedList(user); + } + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + @DataScope(deptAlias = "d", userAlias = "u") + public List<SysUser> selectUnallocatedList(SysUser user) + { + return userMapper.selectUnallocatedList(user); + } + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + @Override + public SysUser selectUserByUserName(String userName) + { + return userMapper.selectUserByUserName(userName); + } + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + @Override + public SysUser selectUserById(Long userId) + { + return userMapper.selectUserById(userId); + } + + /** + * 查询用户所属角色组 + * + * @param userName 用户名 + * @return 结果 + */ + @Override + public String selectUserRoleGroup(String userName) + { + List<SysRole> list = roleMapper.selectRolesByUserName(userName); + if (CollectionUtils.isEmpty(list)) + { + return StringUtils.EMPTY; + } + return list.stream().map(SysRole::getRoleName).collect(Collectors.joining(",")); + } + + /** + * 查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + @Override + public String selectUserPostGroup(String userName) + { + List<SysPost> list = postMapper.selectPostsByUserName(userName); + if (CollectionUtils.isEmpty(list)) + { + return StringUtils.EMPTY; + } + return list.stream().map(SysPost::getPostName).collect(Collectors.joining(",")); + } + + /** + * 校验用户名称是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public boolean checkUserNameUnique(SysUser user) + { + Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); + SysUser info = userMapper.checkUserNameUnique(user.getUserName()); + if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验手机号码是否唯一 + * + * @param user 用户信息 + * @return + */ + @Override + public boolean checkPhoneUnique(SysUser user) + { + Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); + SysUser info = userMapper.checkPhoneUnique(user.getPhonenumber()); + if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + * @return + */ + @Override + public boolean checkEmailUnique(SysUser user) + { + Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); + SysUser info = userMapper.checkEmailUnique(user.getEmail()); + if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验用户是否允许操作 + * + * @param user 用户信息 + */ + @Override + public void checkUserAllowed(SysUser user) + { + if (StringUtils.isNotNull(user.getUserId()) && user.isAdmin()) + { + throw new ServiceException("不允许操作超级管理员用户"); + } + } + + /** + * 校验用户是否有数据权限 + * + * @param userId 用户id + */ + @Override + public void checkUserDataScope(Long userId) + { + if (!SysUser.isAdmin(SecurityUtils.getUserId())) + { + SysUser user = new SysUser(); + user.setUserId(userId); + List<SysUser> users = SpringUtils.getAopProxy(this).selectUserList(user); + if (StringUtils.isEmpty(users)) + { + throw new ServiceException("没有权限访问用户数据!"); + } + } + } + + /** + * 新增保存用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + @Transactional + public int insertUser(SysUser user) + { + // 新增用户信息 + int rows = userMapper.insertUser(user); + // 新增用户岗位关联 + insertUserDept(user); + // 新增用户与角色管理 + insertUserRoleId(user); + return rows; + } + + /** + * 注册用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public boolean registerUser(SysUser user) + { + return userMapper.insertUser(user) > 0; + } + + /** + * 修改保存用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + @Transactional + public int updateUser(SysUser user) + { + Long userId = user.getUserId(); + // 删除用户与角色关联 + userRoleMapper.deleteUserRoleByUserId(userId); + // 新增用户与角色管理 + insertUserRoleId(user); + // 新增用户与部门管理 + insertUserDept(user); + return userMapper.updateUser(user); + } + + /** + * 用户授权角色 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + @Override + @Transactional + public void insertUserAuth(Long userId, Long[] roleIds) + { + userRoleMapper.deleteUserRoleByUserId(userId); + insertUserRole(userId, roleIds); + } + + /** + * 修改用户状态 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int updateUserStatus(SysUser user) + { + return userMapper.updateUser(user); + } + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int updateUserProfile(SysUser user) + { + return userMapper.updateUser(user); + } + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * @return 结果 + */ + @Override + public boolean updateUserAvatar(String userName, String avatar) + { + return userMapper.updateUserAvatar(userName, avatar) > 0; + } + + /** + * 重置用户密码 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int resetPwd(SysUser user) + { + return userMapper.updateUser(user); + } + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * @return 结果 + */ + @Override + public int resetUserPwd(String userName, String password) + { + return userMapper.resetUserPwd(userName, password); + } + + /** + * 新增用户角色信息 + * + * @param user 用户对象 + */ + public void insertUserRole(SysUser user) + { + this.insertUserRole(user.getUserId(), user.getRoleIds()); + } + + /** + * 新增用户角色信息 + * + * @param user 用户对象 + */ + public void insertUserRoleId(SysUser user) + { + this.insertUserRoleId(user.getUserId(), user.getRoleId()); + } + + /** + * 新增用户角色信息 + * + * @param user 用户对象 + */ + public void insertUserDept(SysUser user) + { + this.insertUserDept(user.getUserId(), user.getDeptIds()); + } + + + /** + * 新增用户岗位信息 + * + * @param user 用户对象 + */ + public void insertUserPost(SysUser user) + { + Long[] posts = user.getPostIds(); + if (StringUtils.isNotEmpty(posts)) + { + // 新增用户与岗位管理 + List<SysUserPost> list = new ArrayList<SysUserPost>(posts.length); + for (Long postId : posts) + { + SysUserPost up = new SysUserPost(); + up.setUserId(user.getUserId()); + up.setPostId(postId); + list.add(up); + } + userPostMapper.batchUserPost(list); + } + } + + /** + * 新增用户角色信息 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + public void insertUserRole(Long userId, Long[] roleIds) + { + if (StringUtils.isNotEmpty(roleIds)) + { + // 新增用户与角色管理 + List<SysUserRole> list = new ArrayList<SysUserRole>(roleIds.length); + for (Long roleId : roleIds) + { + SysUserRole ur = new SysUserRole(); + ur.setUserId(userId); + ur.setRoleId(roleId); + list.add(ur); + } + userRoleMapper.batchUserRole(list); + } + } + + /** + * 新增用户角色信息 + * + * @param userId 用户ID + * @param roleId 角色组 + */ + public void insertUserRoleId(Long userId, Long roleId) + { + if (Objects.nonNull(userId) && Objects.nonNull(roleId)){ + // 新增用户与角色管理 + SysUserRole ur = new SysUserRole(); + ur.setUserId(userId); + ur.setRoleId(roleId); + userRoleMapper.insertUserRole(ur); + } + } + /** + * 新增用户部门信息 + * + * @param userId 用户ID + * @param deptIds 部门id集合 + */ + public void insertUserDept(Long userId, List<String> deptIds) + { + } + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + @Override + @Transactional + public int deleteUserById(Long userId) + { + // 删除用户与角色关联 + userRoleMapper.deleteUserRoleByUserId(userId); + // 删除用户与岗位表 + userPostMapper.deleteUserPostByUserId(userId); + return userMapper.deleteUserById(userId); + } + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * @return 结果 + */ + @Override + @Transactional + public int deleteUserByIds(List<Long> userIds) + { + for (Long userId : userIds) + { + checkUserAllowed(new SysUser(userId)); +// checkUserDataScope(userId); + } + // 删除用户与角色关联 + userRoleMapper.deleteUserRole(userIds); + // 删除用户与岗位关联 +// userPostMapper.deleteUserPost(userIds); + return userMapper.deleteUserByIds(userIds); + } + + /** + * 导入用户数据 + * + * @param userList 用户数据列表 + * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据 + * @param operName 操作用户 + * @return 结果 + */ + @Override + public String importUser(List<SysUser> userList, Boolean isUpdateSupport, String operName) + { + if (StringUtils.isNull(userList) || userList.size() == 0) + { + throw new ServiceException("导入用户数据不能为空!"); + } + int successNum = 0; + int failureNum = 0; + StringBuilder successMsg = new StringBuilder(); + StringBuilder failureMsg = new StringBuilder(); + String password = configService.selectConfigByKey("sys.user.initPassword"); + for (SysUser user : userList) + { + try + { + // 验证是否存在这个用户 + SysUser u = userMapper.selectUserByUserName(user.getUserName()); + if (StringUtils.isNull(u)) + { + BeanValidators.validateWithException(validator, user); + user.setPassword(SecurityUtils.encryptPassword(password)); + user.setCreateBy(operName); + userMapper.insertUser(user); + successNum++; + successMsg.append("<br/>" + successNum + "、账号 " + user.getUserName() + " 导入成功"); + } + else if (isUpdateSupport) + { + BeanValidators.validateWithException(validator, user); + checkUserAllowed(u); + checkUserDataScope(u.getUserId()); + user.setUserId(u.getUserId()); + user.setUpdateBy(operName); + userMapper.updateUser(user); + successNum++; + successMsg.append("<br/>" + successNum + "、账号 " + user.getUserName() + " 更新成功"); + } + else + { + failureNum++; + failureMsg.append("<br/>" + failureNum + "、账号 " + user.getUserName() + " 已存在"); + } + } + catch (Exception e) + { + failureNum++; + String msg = "<br/>" + failureNum + "、账号 " + user.getUserName() + " 导入失败:"; + failureMsg.append(msg + e.getMessage()); + log.error(msg, e); + } + } + if (failureNum > 0) + { + failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); + throw new ServiceException(failureMsg.toString()); + } + else + { + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); + } + return successMsg.toString(); + } + + @Override + public List<SysUser> selectList() { + return userMapper.selectList(); + } + + @Override + public Integer selectCount(Integer status) { + return userMapper.selectCount(status); + } + +// @Override +// public List<SysUserVO> selectUserPageList(SysUserQuery query) { +// return userMapper.selectUserPageList(query); +// } + +// @Override +// public List<SysUserVO> selectBlackPageList(SysUserQuery query) { +// return userMapper.selectBlackPageList(query); +// } + + @Override + public List<SysUser> selectListByNamePhone(String name) { + return userMapper.selectListByNamePhone(name); + } + + @Override + public List<SysUser> selectUserByUserNameList(List<String> names) { + return userMapper.selectUserByUserNameList(names); + } + +// @Override +// public UserInfoVo getUserInfoBy(String singleNum) { +// return userMapper.getUserInfoBy(singleNum); +// } + + @Override + public Long getUserRole(Long userId) { + return userMapper.getUserRole(userId); + } + + @Override + public int updateUserIfBlack(List<Long> ids) { + return userMapper.updateUserIfBlack(ids); + } + + @Override + public List<SysUser> selectAllList() { + return userMapper.selectAllList(); + } + + + @Override + public void updatePassword(Long id, String s) { + userMapper.updatePassword(id,s); + } + + @Override + public long selectIdByPhone(String phonenumber) { + return userMapper.selectIdByPhone(phonenumber); + } + +// @Override +// public UserInfoVo userInfo(Long userId) { +// return userMapper.userInfo(userId); +// } + + @Override + public SysUser selectByPhone(String phonenumber) { + return userMapper.selectByPhone(phonenumber); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbAccountDetailServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbAccountDetailServiceImpl.java new file mode 100644 index 0000000..f504245 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbAccountDetailServiceImpl.java @@ -0,0 +1,21 @@ +package com.ruoyi.system.service.impl; + + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.system.mapper.TbAccountDetailMapper; +import com.ruoyi.system.model.TbAccountDetail; +import com.ruoyi.system.service.TbAccountDetailService; +import org.springframework.stereotype.Service; + +/** + * <p> + * 账户明细表 服务实现类 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +@Service +public class TbAccountDetailServiceImpl extends ServiceImpl<TbAccountDetailMapper, TbAccountDetail> implements TbAccountDetailService { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbAddressServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbAddressServiceImpl.java new file mode 100644 index 0000000..b4f444e --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbAddressServiceImpl.java @@ -0,0 +1,21 @@ +package com.ruoyi.system.service.impl; + + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.system.mapper.TbAddressMapper; +import com.ruoyi.system.model.TbAddress; +import com.ruoyi.system.service.TbAddressService; +import org.springframework.stereotype.Service; + +/** + * <p> + * 用户地址表 服务实现类 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +@Service +public class TbAddressServiceImpl extends ServiceImpl<TbAddressMapper, TbAddress> implements TbAddressService { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbBankServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbBankServiceImpl.java new file mode 100644 index 0000000..35c911f --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbBankServiceImpl.java @@ -0,0 +1,21 @@ +package com.ruoyi.system.service.impl; + + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.system.mapper.TbBankMapper; +import com.ruoyi.system.model.TbBank; +import com.ruoyi.system.service.TbBankService; +import org.springframework.stereotype.Service; + +/** + * <p> + * 用户卡表 服务实现类 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +@Service +public class TbBankServiceImpl extends ServiceImpl<TbBankMapper, TbBank> implements TbBankService { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbCompanyServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbCompanyServiceImpl.java new file mode 100644 index 0000000..3a1f1e0 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbCompanyServiceImpl.java @@ -0,0 +1,21 @@ +package com.ruoyi.system.service.impl; + + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.system.mapper.TbCompanyMapper; +import com.ruoyi.system.model.TbCompany; +import com.ruoyi.system.service.TbCompanyService; +import org.springframework.stereotype.Service; + +/** + * <p> + * 公司消息表 服务实现类 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +@Service +public class TbCompanyServiceImpl extends ServiceImpl<TbCompanyMapper, TbCompany> implements TbCompanyService { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbMessageServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbMessageServiceImpl.java new file mode 100644 index 0000000..c09db16 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbMessageServiceImpl.java @@ -0,0 +1,21 @@ +package com.ruoyi.system.service.impl; + + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.system.mapper.TbMessageMapper; +import com.ruoyi.system.model.TbMessage; +import com.ruoyi.system.service.TbMessageService; +import org.springframework.stereotype.Service; + +/** + * <p> + * 消息表 服务实现类 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +@Service +public class TbMessageServiceImpl extends ServiceImpl<TbMessageMapper, TbMessage> implements TbMessageService { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbOpeningBankServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbOpeningBankServiceImpl.java new file mode 100644 index 0000000..0852d2c --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbOpeningBankServiceImpl.java @@ -0,0 +1,21 @@ +package com.ruoyi.system.service.impl; + + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.system.mapper.TbOpeningBankMapper; +import com.ruoyi.system.model.TbOpeningBank; +import com.ruoyi.system.service.TbOpeningBankService; +import org.springframework.stereotype.Service; + +/** + * <p> + * 开户行表 服务实现类 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +@Service +public class TbOpeningBankServiceImpl extends ServiceImpl<TbOpeningBankMapper, TbOpeningBank> implements TbOpeningBankService { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbPermitServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbPermitServiceImpl.java new file mode 100644 index 0000000..ee81ffd --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbPermitServiceImpl.java @@ -0,0 +1,21 @@ +package com.ruoyi.system.service.impl; + + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.system.mapper.TbPermitMapper; +import com.ruoyi.system.model.TbPermit; +import com.ruoyi.system.service.TbPermitService; +import org.springframework.stereotype.Service; + +/** + * <p> + * 许可证表 服务实现类 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +@Service +public class TbPermitServiceImpl extends ServiceImpl<TbPermitMapper, TbPermit> implements TbPermitService { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbRegionServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbRegionServiceImpl.java new file mode 100644 index 0000000..0adb87c --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbRegionServiceImpl.java @@ -0,0 +1,82 @@ +package com.ruoyi.system.service.impl; + + +import cn.hutool.core.bean.BeanUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.common.redis.service.RedisService; +import com.ruoyi.system.mapper.TbRegionMapper; +import com.ruoyi.system.model.TbRegion; +import com.ruoyi.system.service.TbRegionService; +import com.ruoyi.system.vo.RegionVo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * <p> + * 省市区三级联动 服务实现类 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +@Service +public class TbRegionServiceImpl extends ServiceImpl<TbRegionMapper, TbRegion> implements TbRegionService { + + @Autowired + private RedisService redisService; + + @Override + public List<RegionVo> listCityVo() { + // 缓存 + if (redisService.getCacheList("region_list")!=null) { + List<RegionVo> regionVoList = BeanUtil.copyToList(redisService.getCacheList("region_list"), RegionVo.class); + return regionVoList; + } else { + // 省级 + List<TbRegion> provinceList = this.baseMapper.selectList( + new LambdaQueryWrapper<TbRegion>().eq(TbRegion::getParentId, 0)); + List<TbRegion> childrenList = this.baseMapper.selectList( + new LambdaQueryWrapper<TbRegion>().ne(TbRegion::getParentId, 0)); + List<RegionVo> provinceVoList = BeanUtil.copyToList(provinceList, RegionVo.class); + provinceVoList = provinceVoList.stream().peek(province -> { + province.setLevel(1); + List<RegionVo> cityVOList = getChildrenRegionVoList(province, + childrenList); + if (cityVOList!=null && !cityVOList.isEmpty()) { + cityVOList = cityVOList.stream().peek(city -> { + city.setLevel(2); + List<RegionVo> countyVoList = getChildrenRegionVoList(city, + childrenList); + if (countyVoList!=null && !countyVoList.isEmpty()) { + countyVoList = countyVoList.stream().peek(county -> county.setLevel(3)) + .collect(Collectors.toList()); + city.setChildren(countyVoList); + } + }).collect(Collectors.toList()); + province.setChildren(cityVOList); + } + }).collect(Collectors.toList()); + // 缓存 + redisService.setCacheList("region_list", provinceVoList); + return provinceVoList; + } + } + + private static List<RegionVo> getChildrenRegionVoList(RegionVo province, + List<TbRegion> childrenList) { + List<RegionVo> cityVOList = childrenList.stream() + .filter(city -> city.getParentId().equals(province.getId())) + .map(item -> BeanUtil.copyProperties(item, RegionVo.class)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + // 将符合条件的元素从childrenList中移除 + childrenList.removeIf(item -> item.getParentId().equals(province.getId())); + return cityVOList; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbUserServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbUserServiceImpl.java new file mode 100644 index 0000000..621ee53 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbUserServiceImpl.java @@ -0,0 +1,21 @@ +package com.ruoyi.system.service.impl; + + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.system.mapper.TbUserMapper; +import com.ruoyi.system.model.TbUser; +import com.ruoyi.system.service.TbUserService; +import org.springframework.stereotype.Service; + +/** + * <p> + * 服务实现类 + * </p> + * + * @author administrator + * @since 2025-05-23 + */ +@Service +public class TbUserServiceImpl extends ServiceImpl<TbUserMapper, TbUser> implements TbUserService { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbWithdrawalServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbWithdrawalServiceImpl.java new file mode 100644 index 0000000..5dcd094 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbWithdrawalServiceImpl.java @@ -0,0 +1,21 @@ +package com.ruoyi.system.service.impl; + + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.system.mapper.TbWithdrawalMapper; +import com.ruoyi.system.model.TbWithdrawal; +import com.ruoyi.system.service.TbWithdrawalService; +import org.springframework.stereotype.Service; + +/** + * <p> + * 提现申请表 服务实现类 + * </p> + * + * @author administrator + * @since 2025-05-26 + */ +@Service +public class TbWithdrawalServiceImpl extends ServiceImpl<TbWithdrawalMapper, TbWithdrawal> implements TbWithdrawalService { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/task/base/AbstractJob.java b/ruoyi-system/src/main/java/com/ruoyi/system/task/base/AbstractJob.java new file mode 100644 index 0000000..cc5a359 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/task/base/AbstractJob.java @@ -0,0 +1,34 @@ +package com.ruoyi.system.task.base; + +import com.aizuda.bpm.mybatisplus.mapper.FlwTaskActorMapper; +import com.aizuda.bpm.mybatisplus.mapper.FlwTaskMapper; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.utils.SmsUtil; +import com.ruoyi.system.service.ISysUserService; +import com.ruoyi.system.task.utils.SpringContextsUtil; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public abstract class AbstractJob implements Job{ + protected Logger logger = LoggerFactory.getLogger(getClass()); + + @Override + public abstract void execute(JobExecutionContext context) throws JobExecutionException; + + protected FlwTaskMapper flwTaskMapper; + protected FlwTaskActorMapper flwTaskActorMapper; + protected SmsUtil smsUtil; + protected ISysUserService sysUserService; + + public AbstractJob(){ + this.flwTaskMapper = SpringContextsUtil.getBean(FlwTaskMapper.class); + this.flwTaskActorMapper = SpringContextsUtil.getBean(FlwTaskActorMapper.class); + this.smsUtil = SpringContextsUtil.getBean(SmsUtil.class); + this.sysUserService = SpringContextsUtil.getBean(ISysUserService.class); + } + + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/task/base/QuartzManager.java b/ruoyi-system/src/main/java/com/ruoyi/system/task/base/QuartzManager.java new file mode 100644 index 0000000..90a0017 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/task/base/QuartzManager.java @@ -0,0 +1,128 @@ +package com.ruoyi.system.task.base; + +import org.quartz.*; +import org.quartz.impl.StdSchedulerFactory; + +import java.util.Date; +import java.util.Map; + +public class QuartzManager { + private static SchedulerFactory factory = new StdSchedulerFactory(); + private static final String TRIGGER_NAME_PREFIX = "TRIGGER_PREFIX_"; + private static final String JOB_NAME_PREFIX = "JOB_PREFIX_"; + + /** + * 添加定时任务:具体某个时间点执行一次的任务,如:在某个2015-06-01 12:00发送一条消息 + * + * @param jobName + * 具体的任务名+ID标识唯一 + * @param jobType + * @param date + * @param jp + */ + public synchronized static void addJob(Class<? extends Job> jobClass, String jobName, TimeJobType jobType, Date date, + Map<String, ? extends Object> jp) { + //logger.debug("ADD JOB {},jobName={},jobTyep={},jobDate={},",jobClass.getName(),jobName,jobType,date); + try { + Scheduler sched = factory.getScheduler(); + JobDetail job = JobBuilder.newJob(jobClass).withIdentity(JOB_NAME_PREFIX + jobName, jobType.getType()) + .setJobData(new JobDataMap(jp)).build(); + + SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger() + .withIdentity(TRIGGER_NAME_PREFIX + jobName, jobType.getType()).startAt(date).build(); + removeJob(jobName, jobType); + + sched.scheduleJob(job, trigger); + + if (!sched.isShutdown()) { + sched.start(); + } + } catch (Exception e) { + //logger.error("ADD JOB exception {},jobName={},jobTyep={},jobDate={},",jobClass.getName(),jobName,jobType,date); + } + } + + /** + * 修改一个任务的触发时间(使用默认的任务组名,触发器名,触发器组名) + * @param jobName + * @param time + */ + public synchronized static void modifyJobTime(String jobName,TimeJobType jobType, Date time) { + //logger.error("Update JOB exception,jobName={},jobTyep={},jobDate={}," ,jobName,jobType,time); + try { + JobKey jobKey = new JobKey(JOB_NAME_PREFIX + jobName, jobType.getType()); + TriggerKey key = new TriggerKey(TRIGGER_NAME_PREFIX + jobName, jobType.getType()); + + + Scheduler sched = factory.getScheduler(); + SimpleTrigger trigger = (SimpleTrigger) sched.getTrigger(key); + if(trigger == null) { + return; + } + Date oldTime = trigger.getStartTime(); + + if (oldTime.getTime() != time.getTime()) { + JobDetail jobDetail = sched.getJobDetail(jobKey); + Class<? extends Job> objJobClass = jobDetail.getJobClass(); + removeJob(jobName,jobType); + Map<String, Object> jp = jobDetail.getJobDataMap(); + addJob(objJobClass, jobName, jobType, time, jp); + } + } catch (Exception e) { + // logger.error("Update JOB exception,jobName={},jobTyep={},jobDate={}," ,jobName,jobType,time); + } + } + + /** + * 移除一个任务 + * + * @param jobName + */ + public synchronized static void removeJob(String jobName, TimeJobType jobType) { + try { + JobKey jobKey = new JobKey(JOB_NAME_PREFIX + jobName, jobType.getType()); + TriggerKey key = new TriggerKey(TRIGGER_NAME_PREFIX + jobName, jobType.getType()); + Scheduler sched = factory.getScheduler(); + + JobDetail detail = sched.getJobDetail(jobKey); + if (detail != null) { + sched.pauseJob(jobKey); + sched.pauseTrigger(key);// 停止触发器 + sched.unscheduleJob(key);// 移除触发器 + sched.deleteJob(jobKey);// 删除任务 + } + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + /** + * 启动所有定时任务 + */ + public synchronized static void startJobs() { + try { + Scheduler sched = factory.getScheduler(); + sched.start(); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + /** + * 关闭所有定时任务 + */ + public synchronized static void shutdownJobs() { + try { + Scheduler sched = factory.getScheduler(); + if (!sched.isShutdown()) { + sched.shutdown(); + } + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/task/base/TimeJobType.java b/ruoyi-system/src/main/java/com/ruoyi/system/task/base/TimeJobType.java new file mode 100644 index 0000000..8bacc4a --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/task/base/TimeJobType.java @@ -0,0 +1,28 @@ +package com.ruoyi.system.task.base; + +/** + * @Description 按时间点发布的任务类型 + * @date 2025年2月17日 下午7:22:28 + */ +public enum TimeJobType { + AUTO_AUDIT("auto_audit","自动审核"); + private String type; + private String desc; + private TimeJobType(String type, String desc) { + this.type = type; + this.desc = desc; + } + public String getType() { + return type; + } + public void setType(String type) { + this.type = type; + } + public String getDesc() { + return desc; + } + public void setDesc(String desc) { + this.desc = desc; + } + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/task/exceptions/TimeException.java b/ruoyi-system/src/main/java/com/ruoyi/system/task/exceptions/TimeException.java new file mode 100644 index 0000000..efd9cf8 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/task/exceptions/TimeException.java @@ -0,0 +1,37 @@ +package com.ruoyi.system.task.exceptions; + +/** + * @文件说明:定时器任务执行异常 + * @版权所有:成都喜来达 + * @项目名称: fengsheng + * @创建者: Leeyns + * @创建日期: 2016年5月18日 + * @最近修改者:Leeyns + * @最近修改日期:2016年5月18日 + */ +public class TimeException extends Exception { + + /** + * TODO + */ + private static final long serialVersionUID = 5703430073981692250L; + + private String message; + + public TimeException() { + super(); + } + + public TimeException(String message) { + super(message); + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/task/jobs/StateProcessJob.java b/ruoyi-system/src/main/java/com/ruoyi/system/task/jobs/StateProcessJob.java new file mode 100644 index 0000000..147e9d0 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/task/jobs/StateProcessJob.java @@ -0,0 +1,48 @@ +package com.ruoyi.system.task.jobs; + +import com.aizuda.bpm.engine.entity.FlwTaskActor; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.system.bo.ProcessAgreeBO; +import com.ruoyi.system.task.base.AbstractJob; +import org.quartz.JobDataMap; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; + +import java.util.Objects; + +/** + * 发票定时任务 + * @author Administrator + * + */ +public class StateProcessJob extends AbstractJob { + + public static final String name = "stateProcess_"; + + @Override + public void execute(JobExecutionContext context) + throws JobExecutionException { + JobDataMap maps = context.getMergedJobDataMap(); + Long taskId = maps.getLong("id"); + try { + System.err.println("执行定时任务"); + ProcessAgreeBO processAgreeBO = new ProcessAgreeBO(); + processAgreeBO.setTaskId(String.valueOf(taskId)); + FlwTaskActor flwTaskActor = flwTaskActorMapper.selectOne(Wrappers.lambdaQuery(FlwTaskActor.class).eq(FlwTaskActor::getTaskId, taskId).last("LIMIT 1")); + if(Objects.isNull(flwTaskActor)){ + System.err.println("该任务不存在"); + return; + } + processAgreeBO.setRemark("自动审批"); + processAgreeBO.setUserId(Long.valueOf(flwTaskActor.getActorId())); + // 短信发送 + SysUser sysUser = sysUserService.selectUserById(Long.valueOf(flwTaskActor.getActorId())); + smsUtil.sendSms(sysUser.getPhonenumber(), "2369951", new String[]{}); + }catch(Exception e){ + e.printStackTrace(); + } + } + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/task/utils/SpringContextsUtil.java b/ruoyi-system/src/main/java/com/ruoyi/system/task/utils/SpringContextsUtil.java new file mode 100644 index 0000000..558257f --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/task/utils/SpringContextsUtil.java @@ -0,0 +1,37 @@ +package com.ruoyi.system.task.utils; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +@Component +public class SpringContextsUtil implements ApplicationContextAware{ + + private static ApplicationContext applicationContext; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + if (SpringContextsUtil.applicationContext == null) { + SpringContextsUtil.applicationContext = applicationContext; + } + } + // 获取applicationContext + public static ApplicationContext getApplicationContext() { + return applicationContext; + } + // 通过name获取 Bean. + public static Object getBean(String name) { + return getApplicationContext().getBean(name); + } + // 通过class获取Bean. + public static <T> T getBean(Class<T> clazz) { + return getApplicationContext().getBean(clazz); + } + // 通过name,以及Clazz返回指定的Bean + public static <T> T getBean(String name, Class<T> clazz) { + System.out.println(getApplicationContext().getBean(name, clazz)); + return getApplicationContext().getBean(name, clazz); + } + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/task/utils/TaskUtil.java b/ruoyi-system/src/main/java/com/ruoyi/system/task/utils/TaskUtil.java new file mode 100644 index 0000000..c2b4bb0 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/task/utils/TaskUtil.java @@ -0,0 +1,13 @@ +package com.ruoyi.system.task.utils; + + +import org.springframework.stereotype.Component; + +/** + * @author zhibing.pu + * @date 2023/7/11 8:39 + */ +@Component +public class TaskUtil { + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resp/AccessTokenRespBody.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resp/AccessTokenRespBody.java new file mode 100644 index 0000000..db2d1b4 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resp/AccessTokenRespBody.java @@ -0,0 +1,28 @@ +package com.ruoyi.system.utils.wx.body.resp; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * AccessToken 全局唯一 + * + * @author xiaochen + */ +@Data +public class AccessTokenRespBody extends RespBody implements Serializable { + + /** + * 获取到的凭证 + */ + @JsonProperty("access_token") + private String accessToken; + /** + * 凭证有效时间,单位:秒 + */ + @JsonProperty("expires_in") + private int expiresIn; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resp/Code2SessionRespBody.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resp/Code2SessionRespBody.java new file mode 100644 index 0000000..d741edb --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resp/Code2SessionRespBody.java @@ -0,0 +1,29 @@ +package com.ruoyi.system.utils.wx.body.resp; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * @author xiaochen + * @ClassName Code2SessionRespBody + * @Description + * @date 2021-07-28 12:35 + */ +@Data +public class Code2SessionRespBody extends RespBody { + /** + * 用户唯一标识 + */ + @JsonProperty("openid") + private String openid; + /** + * 会话密钥 + */ + @JsonProperty("session_key") + private String sessionKey; + /** + * 用户在开放平台的唯一标识符,若当前小程序已绑定到微信开放平台帐号下会返回,详见 UnionID 机制说明。 + */ + @JsonProperty("unionid") + private String unionid; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resp/RespBody.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resp/RespBody.java new file mode 100644 index 0000000..ee60ab3 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resp/RespBody.java @@ -0,0 +1,19 @@ +package com.ruoyi.system.utils.wx.body.resp; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * @author xiaochen + * @ClassName RespBody + * @Description + * @date 2021-07-28 11:44 + */ +@Data +public class RespBody { + @JsonProperty("errcode") + private Integer errorCode; + + @JsonProperty("errmsg") + private String errorMsg; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resq/Code2SessionResqBody.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resq/Code2SessionResqBody.java new file mode 100644 index 0000000..424e376 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/body/resq/Code2SessionResqBody.java @@ -0,0 +1,21 @@ +package com.ruoyi.system.utils.wx.body.resq; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * @author xiaochen + * @ClassName Code2SessionResqBody + * @Description + * @date 2021-07-28 11:47 + */ +@Data +public class Code2SessionResqBody { + @JsonProperty("js_code") + private String jsCode; + + public Code2SessionResqBody build(String jsCode) { + this.jsCode = jsCode; + return this; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/model/WeixinProperties.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/model/WeixinProperties.java new file mode 100644 index 0000000..fdeb5ab --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/model/WeixinProperties.java @@ -0,0 +1,79 @@ +package com.ruoyi.system.utils.wx.model; + +import lombok.ToString; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * @author xiaochen + * @ClassName WeixinProperties + * @Description + * @date 2024-08-14 13:55 + */ +@ToString +@Component +@ConfigurationProperties(prefix = "wx.conf") +public class WeixinProperties { + /** + * 默认开启 + */ + private boolean enabled = true; + /** + * 获取 App ID + * + * @return App ID + */ + private String appId; + /** + * 获取 Mch ID + * + * @return Mch ID + */ + private String mchId; + + /** + * 获取 secret ID + * + * @return secret ID + */ + private String secretId; + + public String getSecretId() { + return secretId; + } + + public void setSecretId(String secretId) { + this.secretId = secretId; + } + + /** + * HTTP(S) 连接超时时间,单位毫秒 + * + */ + public int getHttpConnectTimeoutMs() { + return 6 * 1000; + } + + /** + * HTTP(S) 读数据超时时间,单位毫秒 + */ + public int getHttpReadTimeoutMs() { + return 8 * 1000; + } + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getMchId() { + return mchId; + } + + public void setMchId(String mchId) { + this.mchId = mchId; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/AppletPhoneEncrypteData.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/AppletPhoneEncrypteData.java new file mode 100644 index 0000000..c7f5257 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/AppletPhoneEncrypteData.java @@ -0,0 +1,19 @@ +package com.ruoyi.system.utils.wx.pojo; + +import lombok.Data; + +/** + * @author xiaochen + * @ClassName AppletUserDecodeData + * @Description + * @date 2021-08-13 17:46 + * 小程序加密数据体 + * + */ +@Data +public class AppletPhoneEncrypteData { + private String encryptedData; + private String openid; + private String unionid; + private String iv; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/AppletUserDecodeData.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/AppletUserDecodeData.java new file mode 100644 index 0000000..a3573ad --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/AppletUserDecodeData.java @@ -0,0 +1,52 @@ +package com.ruoyi.system.utils.wx.pojo; + +import lombok.Data; + +/** + * @author xiaochen + * @ClassName AppletUserDecodeData + * @Description + * 用户主体信息部分 + * { + * "openId": "OPENID", + * "nickName": "NICKNAME", + * "gender": GENDER, + * "city": "CITY", + * "province": "PROVINCE", + * "country": "COUNTRY", + * "avatarUrl": "AVATARURL", + * "unionId": "UNIONID", + * "watermark": + * { + * "appid":"APPID", + * "timestamp":TIMESTAMP + * } + * } + * 电话部分 + * { + * "phoneNumber": "13580006666", + * "purePhoneNumber": "13580006666", + * "countryCode": "86", + * "watermark": + * { + * "appid":"APPID", + * "timestamp": TIMESTAMP + * } + * } + * + */ +@Data +public class AppletUserDecodeData { + private String openId; + private String unionId; + private String nickName; + private int gender; + private String city; + private String province; + private String country; + private String avatarUrl; + private Watermark watermark; + private String phoneNumber; + private String purePhoneNumber; + private String countryCode; +} \ No newline at end of file diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/AppletUserEncrypteData.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/AppletUserEncrypteData.java new file mode 100644 index 0000000..16d0057 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/AppletUserEncrypteData.java @@ -0,0 +1,20 @@ +package com.ruoyi.system.utils.wx.pojo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @author xiaochen + * @ClassName AppletUserDecodeData + * @Description + * 小程序加密数据体 + * + */ +@Data +public class AppletUserEncrypteData extends AppletPhoneEncrypteData { + private String rawData; + private String signature; + private String code; + @ApiModelProperty(value = "邀请用户id") + private Long inviteUserId; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/Watermark.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/Watermark.java new file mode 100644 index 0000000..16f4f7a --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/pojo/Watermark.java @@ -0,0 +1,9 @@ +package com.ruoyi.system.utils.wx.pojo; + +import lombok.Data; + +@Data +public class Watermark { + private String appid; + private String timestamp; +} \ No newline at end of file diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/SHA1.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/SHA1.java new file mode 100644 index 0000000..2b74822 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/SHA1.java @@ -0,0 +1,36 @@ +package com.ruoyi.system.utils.wx.tools; + +import java.security.MessageDigest; + +public class SHA1 { + + + /** + * 用SHA1算法生成安全签名 + * + * @param str + * @return + * @throws WxException + */ + public static String getSHA1(String str) throws WxException { + try { + // SHA1签名生成 + MessageDigest md = MessageDigest.getInstance("SHA-1"); + md.update(str.getBytes()); + byte[] digest = md.digest(); + StringBuffer hexstr = new StringBuffer(); + String shaHex; + for (int i = 0; i < digest.length; i++) { + shaHex = Integer.toHexString(digest[i] & 0xFF); + if (shaHex.length() < 2) { + hexstr.append(0); + } + hexstr.append(shaHex); + } + return hexstr.toString(); + } catch (Exception e) { + throw new WxException(WxException.ComputeSignatureError); + } + } + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WebUtils.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WebUtils.java new file mode 100644 index 0000000..c2e15e1 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WebUtils.java @@ -0,0 +1,48 @@ +package com.ruoyi.system.utils.wx.tools; + +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +/** + * @Author xiaochen + * @Date 2019/08/26 10:28 AM + * @Description + */ +public final class WebUtils { + + private WebUtils() { + } + + /** + * 当前请求 + */ + public static HttpServletRequest request() { + return contextHolder() == null ? null : contextHolder().getRequest(); + } + + /** + * 当前响应 + */ + public static HttpServletResponse response() { + return contextHolder() == null ? null : contextHolder().getResponse(); + } + + /** + * 当前session + */ + public static HttpSession session() { + return request() == null ? null : request().getSession(); + } + + /** + * 当前ServletRequest + */ + public static ServletRequestAttributes contextHolder() { + return (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + } + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxAppletTools.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxAppletTools.java new file mode 100644 index 0000000..2298a44 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxAppletTools.java @@ -0,0 +1,123 @@ +package com.ruoyi.system.utils.wx.tools; + + +import com.ruoyi.common.redis.service.RedisService; +import com.ruoyi.system.utils.wx.body.resp.AccessTokenRespBody; +import com.ruoyi.system.utils.wx.body.resp.Code2SessionRespBody; +import com.ruoyi.system.utils.wx.body.resq.Code2SessionResqBody; +import com.ruoyi.system.utils.wx.model.WeixinProperties; +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.StringUtils; +import org.springframework.web.client.RestTemplate; + +import java.text.MessageFormat; +import java.util.concurrent.TimeUnit; + +/** + * @author xiaochen + * @ClassName WxAppletTools + * @Description + * @date 2024-8-04 13:55 + */ +@Slf4j +public class WxAppletTools { + private final static String ACCESSTOKEN_CACHE_KEY = "accessToken"; + /** + * 请求参数 + * 属性 类型 默认值 必填 说明 + * appid string 是 小程序 appId + * secret string 是 小程序 appSecret + * js_code string 是 登录时获取的 code + * grant_type string 是 授权类型,此处只需填写 authorization_cod + * <p> + * 返回值: + * <p> + * 属性 类型 说明 + * openid string 用户唯一标识 + * session_key string 会话密钥 + * unionid string 用户在开放平台的唯一标识符,若当前小程序已绑定到微信开放平台帐号下会返回,详见 UnionID 机制说明。 + * errcode number 错误码 + * errmsg string 错误信息 + */ + private static final String JSCODE_2_SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code"; + /** + * 请求参数 + * 属性 类型 默认值 必填 说明 + * grant_type string 是 填写 client_credential + * appid string 是 小程序唯一凭证,即 AppID,可在「微信公众平台 - 设置 - 开发设置」页中获得。(需要已经成为开发者,且帐号没有异常状态) + * secret string 是 小程序唯一凭证密钥,即 AppSecret,获取方式同 appid + * 返回值 + * Object + * 返回的 JSON 数据包 + * <p> + * 属性 类型 说明 + * access_token string 获取到的凭证 + * expires_in number 凭证有效时间,单位:秒。目前是7200秒之内的值。 + * errcode number 错误码 + * errmsg string 错误信息 + */ + public static String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}"; + private WeixinProperties wxConfig; + private RestTemplate wxRestTemplate; + private RedisService redisService; + + public WxAppletTools(RestTemplate wxRestTemplate, WeixinProperties wxConfig, RedisService redisService) { + this.wxRestTemplate = wxRestTemplate; + this.wxConfig = wxConfig; + this.redisService = redisService; + } + + /** + * 自定义部分数据 + * + * @param wxConfig + * @return + */ + public WxAppletTools build(WeixinProperties wxConfig) { + this.wxConfig = wxConfig; + return this; + } + + /** + * @param resqBody + * @return + */ + public Code2SessionRespBody getOpenIdByJscode2session(Code2SessionResqBody resqBody) { + long start = System.currentTimeMillis(); + String requestUrl = MessageFormat.format(JSCODE_2_SESSION_URL, wxConfig.getAppId(), wxConfig.getSecretId(), resqBody.getJsCode()); + long end = System.currentTimeMillis(); + log.info("code换取sessionKey时间:{}", (end - start)); + String respBody = wxRestTemplate.getForEntity(requestUrl, String.class).getBody(); + end = System.currentTimeMillis(); + log.info("code换取sessionKey时间:{}", (end - start)); + log.info("Jscode2session:{}", respBody); + Code2SessionRespBody code2SessionRespBody = WxJsonUtils.parseObject(respBody, Code2SessionRespBody.class); + // 判断有误异常 + if (StringUtils.hasLength(code2SessionRespBody.getErrorMsg())) { + // 抛出错误 + throw new WxException(code2SessionRespBody.getErrorCode() + ":" + code2SessionRespBody.getErrorMsg()); + } + return code2SessionRespBody; + } + + /** + * @return + */ + public String getAccessToken(String version) { + String accessToken = redisService.getCacheObject(ACCESSTOKEN_CACHE_KEY + version); + if (StringUtils.hasLength(accessToken)) { + return accessToken; + } + String requestUrl = MessageFormat.format(ACCESS_TOKEN_URL, wxConfig.getAppId(), wxConfig.getSecretId()); + String respBody = wxRestTemplate.getForEntity(requestUrl, String.class).getBody(); + AccessTokenRespBody accessTokenRespBody = WxJsonUtils.parseObject(respBody, AccessTokenRespBody.class); + // 判断有误异常 + if (StringUtils.hasLength(accessTokenRespBody.getErrorMsg())) { + // 抛出错误 + throw new WxException(accessTokenRespBody.getErrorCode() + ":" + accessTokenRespBody.getErrorMsg()); + } + redisService.setCacheObject(ACCESSTOKEN_CACHE_KEY + version, accessTokenRespBody.getAccessToken(), 7200L, TimeUnit.SECONDS); + return accessTokenRespBody.getAccessToken(); + } + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxCache.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxCache.java new file mode 100644 index 0000000..fa82920 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxCache.java @@ -0,0 +1,117 @@ +package com.ruoyi.system.utils.wx.tools; + +import java.util.concurrent.TimeUnit; + +/** + * 缓存 + * + * @author xiaochen + */ +class WxCache { + /** + * 缓存的初始化容量 + */ + private int initialCapacity = 50; + /** + * 缓存最大容量 + */ + private long maximumSize = 200L; + /** + * 缓存时长 + */ + private long duration = 7000L; + /** + * 时长单位,自动转换 + * 支持: + * 时 + * 分 + * 秒 + * 天 + */ + private TimeUnit timeunit = TimeUnit.SECONDS; + + public int getInitialCapacity() { + return initialCapacity; + } + + public void setInitialCapacity(int initialCapacity) { + this.initialCapacity = initialCapacity; + } + + public long getMaximumSize() { + return maximumSize; + } + + public void setMaximumSize(long maximumSize) { + this.maximumSize = maximumSize; + } + + + public long getDuration() { + return duration; + } + + public void setDuration(long duration) { + this.duration = duration; + } + + public TimeUnit getTimeunit() { + return timeunit; + } + + public void setTimeunit(TimeUnit timeunit) { + this.timeunit = timeunit; + } + + public static class Builder { + private int initialCapacity; + private long maximumSize; + private long duration; + private TimeUnit timeunit; + + public Builder setInitialCapacity(int initialCapacity) { + this.initialCapacity = initialCapacity; + return this; + } + + public Builder setMaximumSize(long maximumSize) { + this.maximumSize = maximumSize; + return this; + } + + public Builder setDuration(long duration) { + this.duration = duration; + return this; + } + + public Builder setTimeUnit(TimeUnit timeunit) { + this.timeunit = timeunit; + return this; + } + + public WxCache build() { + return new WxCache(this); + } + } + + public static Builder options() { + return new Builder(); + } + + private WxCache(Builder builder) { + this.initialCapacity = 0 == builder.initialCapacity ? this.initialCapacity : builder.initialCapacity; + this.maximumSize = 0L == builder.maximumSize ? this.maximumSize : builder.maximumSize; + this.duration = 0L == builder.duration ? this.duration : builder.duration; + this.timeunit = null == builder.timeunit ? this.timeunit : builder.timeunit; + } + + @Override + public String toString() { + return "WxCache{" + + "initialCapacity=" + initialCapacity + + ", maximumSize=" + maximumSize + + ", duration=" + duration + + ", timeunit=" + timeunit + + '}'; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxCacheTemplate.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxCacheTemplate.java new file mode 100644 index 0000000..8640177 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxCacheTemplate.java @@ -0,0 +1,34 @@ +package com.ruoyi.system.utils.wx.tools; + +/** + * @author xiaochen + * @ClassName WxCacheTemplate + * @Description + * @date 2021-01-11 11:27 + */ +public interface WxCacheTemplate<T> { + /** + * 保存key + * + * @param key + * @param value + * @return + */ + boolean setKey(String key, T value); + + /** + * 获取缓存 + * + * @param key + * @return + */ + T getKey(String key); + + /** + * 删除 + * + * @param key + * @return + */ + boolean delKey(String key); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxException.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxException.java new file mode 100644 index 0000000..09d46ae --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxException.java @@ -0,0 +1,55 @@ +package com.ruoyi.system.utils.wx.tools; + +/** + * @author lihen + */ +public class WxException extends RuntimeException { + + private final static int OK = 0; + private final static int ValidateSignatureError = -40001; + private final static int ParseXmlError = -40002; + public final static int ComputeSignatureError = -40003; + private final static int IllegalAesKey = -40004; + private final static int ValidateAppidError = -40005; + private final static int EncryptAESError = -40006; + private final static int DecryptAESError = -40007; + private final static int IllegalBuffer = -40008; + + private int code; + + private static String getMessage(int code) { + switch (code) { + case ValidateSignatureError: + return "签名验证错误"; + case ParseXmlError: + return "xml解析失败"; + case ComputeSignatureError: + return "sha加密生成签名失败"; + case IllegalAesKey: + return "SymmetricKey非法"; + case ValidateAppidError: + return "appid校验失败"; + case EncryptAESError: + return "aes加密失败"; + case DecryptAESError: + return "aes解密失败"; + case IllegalBuffer: + return "解密后得到的buffer非法"; + default: + return null; + } + } + + public int getCode() { + return code; + } + + WxException(int code) { + super(getMessage(code)); + this.code = code; + } + + public WxException(String message) { + super(message); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxJsonUtils.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxJsonUtils.java new file mode 100644 index 0000000..cc6113e --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxJsonUtils.java @@ -0,0 +1,109 @@ +package com.ruoyi.system.utils.wx.tools; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +/** + * Json转换工具类 + * 参考:https://blog.csdn.net/weixin_38413579/article/details/82562634 + * @author madman + */ +@Slf4j +public final class WxJsonUtils { + public static final String dateFormat = "yyyy-MM-dd"; + public static final String dateTimeFormat = "yyyy-MM-dd HH:mm:ss"; + private static final ObjectMapper OM = new ObjectMapper(); + private static final JavaTimeModule timeModule = new JavaTimeModule(); + + /** + * 转换LocalDateTime + */ + static class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> { + @Override + public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { + jsonGenerator.writeString(localDateTime.format(DateTimeFormatter.ofPattern(dateTimeFormat))); + } + } + + /** + * 转换LocalDate + */ + static class LocalDateSerializer extends JsonSerializer<LocalDate> { + @Override + public void serialize(LocalDate localDate, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { + jsonGenerator.writeString(localDate.format(DateTimeFormatter.ofPattern(dateFormat))); + } + } + + /** + * 设置 ObjectMapper + * + * @return + */ + private static ObjectMapper getObjectMapper() { + // 序列化 + timeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer()); + timeModule.addSerializer(LocalDate.class, new LocalDateSerializer()); + // 反序列化 + timeModule.addDeserializer(LocalDateTime.class, + new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(dateTimeFormat))); + timeModule.addDeserializer(LocalDate.class, + new LocalDateDeserializer(DateTimeFormatter.ofPattern(dateFormat))); + // 允许对象忽略json中不存在的属性 + OM.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + OM.registerModule(timeModule); + return OM; + } + + /** + * 将对象序列化 + */ + public static <T> String toJsonString(T obj) { + try { + ObjectMapper om = getObjectMapper(); + return om.writeValueAsString(obj); + } catch (JsonProcessingException e) { + log.error("转json字符串失败:{}", obj); + return null; + } + } + + /** + * 反序列化对象字符串 + */ + public static <T> T parseObject(String json, Class<T> clazz) { + try { + ObjectMapper om = getObjectMapper(); + return om.readValue(json, clazz); + } catch (JsonProcessingException e) { + throw new RuntimeException("反序列化对象字符串失败"); + } + } + + /** + * 反序列化字符串成为对象 + */ + public static <T> T parseObject(String json, TypeReference<T> valueTypeRef) { + try { + ObjectMapper om = getObjectMapper(); + return om.readValue(json, valueTypeRef); + } catch (JsonProcessingException e) { + throw new RuntimeException("反序列化字符串成为对象失败"); + } + } + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxUtils.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxUtils.java new file mode 100644 index 0000000..6287358 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/wx/tools/WxUtils.java @@ -0,0 +1,175 @@ +package com.ruoyi.system.utils.wx.tools; + +import com.ruoyi.system.utils.wx.pojo.AppletUserDecodeData; +import com.ruoyi.common.utils.sign.Base64; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.CharEncoding; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import javax.servlet.http.HttpServletRequest; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.security.AlgorithmParameters; +import java.security.Security; +import java.util.Arrays; + +/** + * @Description 获取用户信息工具类 + * @Author xiaochen + * @Date 2021/8/12 15:45 + */ +@Slf4j +public class WxUtils { + + /** + * 微信小程序API 用户数据的解密 + * + * @param encryptedData + * @param sessionKey + * @param iv + * @return + */ + public static AppletUserDecodeData encryptedData(String encryptedData, String sessionKey, String iv) { + // 被加密的数据 + byte[] dataByte = Base64.decode(encryptedData); + // 加密秘钥 + byte[] keyByte = Base64.decode(sessionKey); + // 偏移量 + byte[] ivByte = Base64.decode(iv); + try { + // 如果密钥不足16位,那么就补足. 这个if 中的内容很重要 + int base = 16; + if (keyByte.length % base != 0) { + int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0); + byte[] temp = new byte[groups * base]; + Arrays.fill(temp, (byte) 0); + System.arraycopy(keyByte, 0, temp, 0, keyByte.length); + keyByte = temp; + } + // 初始化 + Security.addProvider(new BouncyCastleProvider()); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC"); + SecretKeySpec spec = new SecretKeySpec(keyByte, "AES"); + AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES"); + parameters.init(new IvParameterSpec(ivByte)); + cipher.init(Cipher.DECRYPT_MODE, spec, parameters); + byte[] resultByte = cipher.doFinal(dataByte); + if (null != resultByte && resultByte.length > 0) { + String result = new String(resultByte, CharEncoding.UTF_8); + log.info("解密原串:{}", result); + return WxJsonUtils.parseObject(result, AppletUserDecodeData.class); + } + throw new RuntimeException("解密的数据为空"); + } catch (Exception e) { + log.error("解密失败. error = {}", e.getMessage(), e); + throw new RuntimeException(e.getMessage()); + } + } + + /** + * 微信小程序API 用户数据的签名验证 + * signature = sha1( rawData + session_key ) + * + * @param rawData 不包括敏感信息的原始数据字符串,用于计算签名。 + * @param sessionKey + */ + public static void verifySignature(String rawData, String sessionKey, String signature) { + String serverSignature = SHA1.getSHA1(rawData + sessionKey); + log.info(rawData + ">>>>>>:" + sessionKey + " === " + serverSignature + " ======" + signature); + if (!signature.equals(serverSignature)) { + throw new RuntimeException("数据验签不通过"); + } + } + + /** + * 根据流接收请求数据 + * + * @param request + * @return + */ + public static String streamBodyByReceive(HttpServletRequest request) throws IOException { + log.info("微信异步回调地址:{}", request.getRequestURL()); + StringBuffer buffer = new StringBuffer(); + InputStream inputStream = request.getInputStream(); + InputStreamReader reader = new InputStreamReader(inputStream); + BufferedReader bufferedReader = new BufferedReader(reader); + String body = null; + while ((body = bufferedReader.readLine()) != null) { + buffer.append(body); + } + String data = buffer.toString(); + reader.close(); + inputStream.close(); + log.info("微信异步回调数据:{}", data); + return data; + } + + /** + * 日志 + * + * @return + */ + public static Logger getLogger() { + Logger logger = LoggerFactory.getLogger("wxpay java sdk"); + return logger; + } + + /** + * debug + * + * @param msg + * @param args + */ + public static void debug(String msg, Object... args) { + Logger log = getLogger(); + if (log.isDebugEnabled()) { + log.debug(msg, args); + } + } + + /** + * info + * + * @param msg + * @param args + */ + public static void info(String msg, Object... args) { + Logger log = getLogger(); + if (log.isInfoEnabled()) { + log.info(msg, args); + } + } + + /** + * warn + * + * @param msg + * @param args + */ + public static void warn(String msg, Object... args) { + Logger log = getLogger(); + if (log.isWarnEnabled()) { + log.warn(msg, args); + } + } + + /** + * error + * + * @param msg + * @param args + */ + public static void error(String msg, Object... args) { + Logger log = getLogger(); + if (log.isErrorEnabled()) { + log.error(msg, args); + } + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/vo/InviteUserListVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/vo/InviteUserListVo.java new file mode 100644 index 0000000..30f022b --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/vo/InviteUserListVo.java @@ -0,0 +1,21 @@ +package com.ruoyi.system.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; + + +@ApiModel("邀请人Vo") +@Data +public class InviteUserListVo { + @ApiModelProperty("用户姓名") + private String username; + + @ApiModelProperty("创建时间") + private Date createTime; + + @ApiModelProperty("剩余分佣次数") + private Integer inviteNum; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/vo/RegionVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/vo/RegionVo.java new file mode 100644 index 0000000..1a59ac5 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/vo/RegionVo.java @@ -0,0 +1,35 @@ +package com.ruoyi.system.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + * @author jqs34 + * @ClassName AppRegionVo + * @description: TODO + * @date 2023年04月06日 + * @version: 1.0 + */ +@Data +public class RegionVo { + + @ApiModelProperty(value = "区域代码") + private String code; + + @ApiModelProperty(value = "区域名称") + private String name; + + @ApiModelProperty(value = "子级列表") + private List<RegionVo> children; + + @ApiModelProperty(value = "id") + private Integer id; + + @ApiModelProperty(value = "父级id") + private Integer parentId; + + @ApiModelProperty(value = "层级") + private Integer level; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/vo/SysOperLogVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/vo/SysOperLogVO.java new file mode 100644 index 0000000..9775650 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/vo/SysOperLogVO.java @@ -0,0 +1,24 @@ +package com.ruoyi.system.vo; + +import com.ruoyi.system.domain.SysOperLog; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +@ApiModel(value = "操作日志VO") +public class SysOperLogVO extends SysOperLog { + + @ApiModelProperty(value = "公司名称") + private String companyName; + + @ApiModelProperty(value = "部门名称") + private String deptName; + + @ApiModelProperty(value = "角色名称") + private String roleName; + + @ApiModelProperty(value = "操作详情") + private String operDetail; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/vo/SysUserVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/vo/SysUserVO.java new file mode 100644 index 0000000..09a4e0d --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/vo/SysUserVO.java @@ -0,0 +1,23 @@ +package com.ruoyi.system.vo; + +import com.ruoyi.common.core.domain.entity.SysUser; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +@Data +@ApiModel(value = "账户列表VO") +public class SysUserVO extends SysUser { + + @ApiModelProperty(value = "身份") + private Integer companyType; + @ApiModelProperty(value = "单位名称") + private String companyName; + @ApiModelProperty(value = "部门") + private List<String> deptList; + @ApiModelProperty(value = "角色") + private String roleName; + +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/vo/UserAccountVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/vo/UserAccountVo.java new file mode 100644 index 0000000..9cef297 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/vo/UserAccountVo.java @@ -0,0 +1,21 @@ +package com.ruoyi.system.vo; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; + +@ApiModel("用户钱包Vo") +@Data +public class UserAccountVo { + @ApiModelProperty("余额") + private BigDecimal balance=BigDecimal.ZERO; + @ApiModelProperty("提现金额") + private BigDecimal WithdrawalBalance=BigDecimal.ZERO; + @ApiModelProperty("待审核金额") + private BigDecimal auditBalance=BigDecimal.ZERO; + @ApiModelProperty("待入账金额") + private BigDecimal entryBalance=BigDecimal.ZERO; +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/vo/UserInfoVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/vo/UserInfoVo.java new file mode 100644 index 0000000..70fa414 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/vo/UserInfoVo.java @@ -0,0 +1,174 @@ +package com.ruoyi.system.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.core.domain.entity.SysDept; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; + +/** + * 用户对象 sys_user + * + * @author ruoyi + */ +@Data +public class UserInfoVo { + @ApiModelProperty(value = "用户id") + private Long user_id; + + @ApiModelProperty(value = "部门id") + private Long deptId; + + @ApiModelProperty(value = "登录名称") + private String user_name; + + @ApiModelProperty(value = "用户名称") + private String nick_name; + + /** 用户邮箱 */ + @ApiModelProperty(value = "用户邮箱") + private String email; + + /** 手机号码 */ + @ApiModelProperty(value = "手机号码") + private String phonenumber; + + /** 用户性别 */ + @ApiModelProperty(value = "用户性别 0=男,1=女,2=未知") + private String sex; + + /** 用户头像 */ + @ApiModelProperty(value = "用户头像") + private String avatar; + + /** 密码 */ + @ApiModelProperty(value = "密码") + private String password; + + /** 帐号状态(0正常 1停用) */ + @ApiModelProperty(value = "帐号状态 0=正常,1=停用") + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + @ApiModelProperty(value = "删除标志(0代表存在 2代表删除)") + private String delFlag; + + @ApiModelProperty(value = "部门对象") + private SysDept dept; + + /** 单位id */ + @ApiModelProperty(value = "单位id") + private Long companyId; + + /** + * 身份证号 + */ + @ApiModelProperty(value = "身份证号") + private String idCard; + + /** + * 家庭住址 + */ + @ApiModelProperty(value = "家庭住址") + private String address; + + /** + * 工种 1=工作负责人 2=技工 3=普工 4=机械工 + */ + @ApiModelProperty(value = "工种 1=工作负责人 2=技工 3=普工 4=机械工") + private Integer workType; + + /** + * 保险单号 + */ + @ApiModelProperty(value = "保险单号") + private String insureNumber; + + /** + * 保险到期时间 + */ + @ApiModelProperty(value = "保险到期时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date insureEndTime; + + /** + * 资质证明单号 + */ + @ApiModelProperty(value = "资质证明单号") + private String qualificationNumber; + + /** + * 资质到期时间 + */ + @ApiModelProperty(value = "资质到期时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date qualificationEndTime; + + /** + * 健康状况(1=合格 0不合格) + */ + @ApiModelProperty(value = "健康状况(1=合格 0不合格)") + private Integer healthCondition; + + /** + * 安全积分 + */ + @ApiModelProperty(value = "安全积分") + private Integer safetyPoints; + + /** + * 安全考试情况 1=合格 0不合格 + */ + @ApiModelProperty(value = "安全考试情况 1=合格 0不合格") + private Integer secureTest; + + /** + * 身份证图片 + */ + @ApiModelProperty(value = "身份证图片") + private String idCardPicture; + /** + * 保单图片 + */ + @ApiModelProperty(value = "保单图片") + private String insurePicture; + /** + * 体检表图片 + */ + @ApiModelProperty(value = "体检表图片") + private String medicalExaminationPicture; + /** + * 资质证明图片 + */ + @ApiModelProperty(value = "资质证明图片") + private String qualificationPicture; + /** + * 二维码(唯一标识) + */ + @ApiModelProperty(value = "二维码(唯一标识)") + private String qrcodeLink; + /** + * 身份 + */ + @ApiModelProperty(value = "身份 1= 分包 2= 新能源 3 = 劳务借工") + private String companyType; + /** + * 工作单位 + */ + @ApiModelProperty(value = "工作单位") + private String companyName; + /** + * 用户角色 + */ + @ApiModelProperty(value = "角色名称") + private String roleName; + /** + * 用户角色id + */ + @ApiModelProperty(value = "角色id") + private Long roleId; + + @ApiModelProperty(value = "类型 1经理 2负责人 3专员") + private Integer userType; +} diff --git a/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml new file mode 100644 index 0000000..ca39f47 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml @@ -0,0 +1,117 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.system.mapper.SysConfigMapper"> + + <resultMap type="SysConfig" id="SysConfigResult"> + <id property="configId" column="config_id" /> + <result property="configName" column="config_name" /> + <result property="configKey" column="config_key" /> + <result property="configValue" column="config_value" /> + <result property="configType" column="config_type" /> + <result property="createBy" column="create_by" /> + <result property="createTime" column="create_time" /> + <result property="updateBy" column="update_by" /> + <result property="updateTime" column="update_time" /> + </resultMap> + + <sql id="selectConfigVo"> + select config_id, config_name, config_key, config_value, config_type, create_by, create_time, update_by, update_time, remark + from sys_config + </sql> + + <!-- 查询条件 --> + <sql id="sqlwhereSearch"> + <where> + <if test="configId !=null"> + and config_id = #{configId} + </if> + <if test="configKey !=null and configKey != ''"> + and config_key = #{configKey} + </if> + </where> + </sql> + + <select id="selectConfig" parameterType="SysConfig" resultMap="SysConfigResult"> + <include refid="selectConfigVo"/> + <include refid="sqlwhereSearch"/> + </select> + + <select id="selectConfigList" parameterType="SysConfig" resultMap="SysConfigResult"> + <include refid="selectConfigVo"/> + <where> + <if test="configName != null and configName != ''"> + AND config_name like concat('%', #{configName}, '%') + </if> + <if test="configType != null and configType != ''"> + AND config_type = #{configType} + </if> + <if test="configKey != null and configKey != ''"> + AND config_key like concat('%', #{configKey}, '%') + </if> + <if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 --> + and date_format(create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d') + </if> + <if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 --> + and date_format(create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d') + </if> + </where> + </select> + + <select id="selectConfigById" parameterType="Long" resultMap="SysConfigResult"> + <include refid="selectConfigVo"/> + where config_id = #{configId} + </select> + + <select id="checkConfigKeyUnique" parameterType="String" resultMap="SysConfigResult"> + <include refid="selectConfigVo"/> + where config_key = #{configKey} limit 1 + </select> + + <insert id="insertConfig" parameterType="SysConfig"> + insert into sys_config ( + <if test="configName != null and configName != '' ">config_name,</if> + <if test="configKey != null and configKey != '' ">config_key,</if> + <if test="configValue != null and configValue != '' ">config_value,</if> + <if test="configType != null and configType != '' ">config_type,</if> + <if test="createBy != null and createBy != ''">create_by,</if> + <if test="remark != null and remark != ''">remark,</if> + create_time + )values( + <if test="configName != null and configName != ''">#{configName},</if> + <if test="configKey != null and configKey != ''">#{configKey},</if> + <if test="configValue != null and configValue != ''">#{configValue},</if> + <if test="configType != null and configType != ''">#{configType},</if> + <if test="createBy != null and createBy != ''">#{createBy},</if> + <if test="remark != null and remark != ''">#{remark},</if> + sysdate() + ) + </insert> + + <update id="updateConfig" parameterType="SysConfig"> + update sys_config + <set> + <if test="configName != null and configName != ''">config_name = #{configName},</if> + <if test="configKey != null and configKey != ''">config_key = #{configKey},</if> + <if test="configValue != null and configValue != ''">config_value = #{configValue},</if> + <if test="configType != null and configType != ''">config_type = #{configType},</if> + <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if> + <if test="remark != null">remark = #{remark},</if> + update_time = sysdate() + </set> + where config_id = #{configId} + </update> + + <delete id="deleteConfigById" parameterType="Long"> + delete from sys_config where config_id = #{configId} + </delete> + + <delete id="deleteConfigByIds" parameterType="Long"> + delete from sys_config where config_id in + <foreach item="configId" collection="array" open="(" separator="," close=")"> + #{configId} + </foreach> + </delete> + +</mapper> \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml new file mode 100644 index 0000000..0ddfcec --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml @@ -0,0 +1,159 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.system.mapper.SysDeptMapper"> + + <resultMap type="SysDept" id="SysDeptResult"> + <id property="deptId" column="dept_id" /> + <result property="parentId" column="parent_id" /> + <result property="ancestors" column="ancestors" /> + <result property="deptName" column="dept_name" /> + <result property="orderNum" column="order_num" /> + <result property="leader" column="leader" /> + <result property="phone" column="phone" /> + <result property="email" column="email" /> + <result property="status" column="status" /> + <result property="delFlag" column="del_flag" /> + <result property="parentName" column="parent_name" /> + <result property="createBy " column="create_by" /> + <result property="createTime" column="create_time" /> + <result property="updateBy" column="update_by" /> + <result property="updateTime" column="update_time" /> + </resultMap> + + <sql id="selectDeptVo"> + select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status, d.del_flag, d.create_by, d.create_time + from sys_dept d + </sql> + + <select id="selectDeptList" parameterType="SysDept" resultMap="SysDeptResult"> + <include refid="selectDeptVo"/> + where d.del_flag = '0' + <if test="deptId != null and deptId != 0"> + AND dept_id = #{deptId} + </if> + <if test="parentId != null and parentId != 0"> + AND parent_id = #{parentId} + </if> + <if test="deptName != null and deptName != ''"> + AND dept_name like concat('%', #{deptName}, '%') + </if> + <if test="status != null and status != ''"> + AND status = #{status} + </if> + <!-- 数据范围过滤 --> + ${params.dataScope} + order by d.parent_id, d.order_num + </select> + + <select id="selectDeptListByRoleId" resultType="Long"> + select d.dept_id + from sys_dept d + left join sys_role_dept rd on d.dept_id = rd.dept_id + where rd.role_id = #{roleId} + <if test="deptCheckStrictly"> + and d.dept_id not in (select d.parent_id from sys_dept d inner join sys_role_dept rd on d.dept_id = rd.dept_id and rd.role_id = #{roleId}) + </if> + order by d.parent_id, d.order_num + </select> + + <select id="selectDeptById" parameterType="Long" resultMap="SysDeptResult"> + select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status, + (select dept_name from sys_dept where dept_id = d.parent_id) parent_name + from sys_dept d + where d.dept_id = #{deptId} + </select> + + <select id="checkDeptExistUser" parameterType="Long" resultType="int"> + select count(1) from sys_user where dept_id = #{deptId} and del_flag = '0' + </select> + + <select id="hasChildByDeptId" parameterType="Long" resultType="int"> + select count(1) from sys_dept + where del_flag = '0' and parent_id = #{deptId} limit 1 + </select> + + <select id="selectChildrenDeptById" parameterType="Long" resultMap="SysDeptResult"> + select * from sys_dept where find_in_set(#{deptId}, ancestors) + </select> + + <select id="selectNormalChildrenDeptById" parameterType="Long" resultType="int"> + select count(*) from sys_dept where status = 0 and del_flag = '0' and find_in_set(#{deptId}, ancestors) + </select> + + <select id="checkDeptNameUnique" resultMap="SysDeptResult"> + <include refid="selectDeptVo"/> + where dept_name=#{deptName} and parent_id = #{parentId} and del_flag = '0' limit 1 + </select> + + <insert id="insertDept" parameterType="SysDept"> + insert into sys_dept( + <if test="deptId != null and deptId != 0">dept_id,</if> + <if test="parentId != null and parentId != 0">parent_id,</if> + <if test="deptName != null and deptName != ''">dept_name,</if> + <if test="ancestors != null and ancestors != ''">ancestors,</if> + <if test="orderNum != null">order_num,</if> + <if test="leader != null and leader != ''">leader,</if> + <if test="phone != null and phone != ''">phone,</if> + <if test="email != null and email != ''">email,</if> + <if test="status != null">status,</if> + <if test="createBy != null and createBy != ''">create_by,</if> + create_time + )values( + <if test="deptId != null and deptId != 0">#{deptId},</if> + <if test="parentId != null and parentId != 0">#{parentId},</if> + <if test="deptName != null and deptName != ''">#{deptName},</if> + <if test="ancestors != null and ancestors != ''">#{ancestors},</if> + <if test="orderNum != null">#{orderNum},</if> + <if test="leader != null and leader != ''">#{leader},</if> + <if test="phone != null and phone != ''">#{phone},</if> + <if test="email != null and email != ''">#{email},</if> + <if test="status != null">#{status},</if> + <if test="createBy != null and createBy != ''">#{createBy},</if> + sysdate() + ) + </insert> + + <update id="updateDept" parameterType="SysDept"> + update sys_dept + <set> + <if test="parentId != null and parentId != 0">parent_id = #{parentId},</if> + <if test="deptName != null and deptName != ''">dept_name = #{deptName},</if> + <if test="ancestors != null and ancestors != ''">ancestors = #{ancestors},</if> + <if test="orderNum != null">order_num = #{orderNum},</if> + <if test="leader != null">leader = #{leader},</if> + <if test="phone != null">phone = #{phone},</if> + <if test="email != null">email = #{email},</if> + <if test="status != null and status != ''">status = #{status},</if> + <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if> + update_time = sysdate() + </set> + where dept_id = #{deptId} + </update> + + <update id="updateDeptChildren" parameterType="java.util.List"> + update sys_dept set ancestors = + <foreach collection="depts" item="item" index="index" + separator=" " open="case dept_id" close="end"> + when #{item.deptId} then #{item.ancestors} + </foreach> + where dept_id in + <foreach collection="depts" item="item" index="index" + separator="," open="(" close=")"> + #{item.deptId} + </foreach> + </update> + + <update id="updateDeptStatusNormal" parameterType="Long"> + update sys_dept set status = '0' where dept_id in + <foreach collection="array" item="deptId" open="(" separator="," close=")"> + #{deptId} + </foreach> + </update> + + <delete id="deleteDeptById" parameterType="Long"> + update sys_dept set del_flag = '2' where dept_id = #{deptId} + </delete> + +</mapper> \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml new file mode 100644 index 0000000..4fae8d7 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml @@ -0,0 +1,127 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.system.mapper.SysDictDataMapper"> + + <resultMap type="SysDictData" id="SysDictDataResult"> + <id property="dictCode" column="dict_code" /> + <result property="dictSort" column="dict_sort" /> + <result property="dictLabel" column="dict_label" /> + <result property="dictValue" column="dict_value" /> + <result property="dictType" column="dict_type" /> + <result property="cssClass" column="css_class" /> + <result property="listClass" column="list_class" /> + <result property="isDefault" column="is_default" /> + <result property="status" column="status" /> + <result property="createBy" column="create_by" /> + <result property="createTime" column="create_time" /> + <result property="updateBy" column="update_by" /> + <result property="updateTime" column="update_time" /> + </resultMap> + + <sql id="selectDictDataVo"> + select dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark + from sys_dict_data + </sql> + + <select id="selectDictDataList" parameterType="SysDictData" resultMap="SysDictDataResult"> + <include refid="selectDictDataVo"/> + <where> + <if test="dictType != null and dictType != ''"> + AND dict_type = #{dictType} + </if> + <if test="dictLabel != null and dictLabel != ''"> + AND dict_label like concat('%', #{dictLabel}, '%') + </if> + <if test="status != null and status != ''"> + AND status = #{status} + </if> + </where> + order by dict_sort asc + </select> + + <select id="selectDictDataByType" parameterType="SysDictData" resultMap="SysDictDataResult"> + <include refid="selectDictDataVo"/> + where status = '0' and dict_type = #{dictType} order by dict_sort asc + </select> + + <select id="selectDictLabel" resultType="String"> + select dict_label from sys_dict_data + where dict_type = #{dictType} and dict_value = #{dictValue} + </select> + + <select id="selectDictDataById" parameterType="Long" resultMap="SysDictDataResult"> + <include refid="selectDictDataVo"/> + where dict_code = #{dictCode} + </select> + + <select id="countDictDataByType" resultType="Integer"> + select count(1) from sys_dict_data where dict_type=#{dictType} + </select> + <select id="selectDictDataByTypeAndValue" resultType="java.lang.String"> + select dict_label from sys_dict_data where dict_type=#{dictType} and dict_value=#{dictValue} + </select> + + <delete id="deleteDictDataById" parameterType="Long"> + delete from sys_dict_data where dict_code = #{dictCode} + </delete> + + <delete id="deleteDictDataByIds" parameterType="Long"> + delete from sys_dict_data where dict_code in + <foreach collection="array" item="dictCode" open="(" separator="," close=")"> + #{dictCode} + </foreach> + </delete> + + <update id="updateDictData" parameterType="SysDictData"> + update sys_dict_data + <set> + <if test="dictSort != null">dict_sort = #{dictSort},</if> + <if test="dictLabel != null and dictLabel != ''">dict_label = #{dictLabel},</if> + <if test="dictValue != null and dictValue != ''">dict_value = #{dictValue},</if> + <if test="dictType != null and dictType != ''">dict_type = #{dictType},</if> + <if test="cssClass != null">css_class = #{cssClass},</if> + <if test="listClass != null">list_class = #{listClass},</if> + <if test="isDefault != null and isDefault != ''">is_default = #{isDefault},</if> + <if test="status != null">status = #{status},</if> + <if test="remark != null">remark = #{remark},</if> + <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if> + update_time = sysdate() + </set> + where dict_code = #{dictCode} + </update> + + <update id="updateDictDataType" parameterType="String"> + update sys_dict_data set dict_type = #{newDictType} where dict_type = #{oldDictType} + </update> + + <insert id="insertDictData" parameterType="SysDictData"> + insert into sys_dict_data( + <if test="dictSort != null">dict_sort,</if> + <if test="dictLabel != null and dictLabel != ''">dict_label,</if> + <if test="dictValue != null and dictValue != ''">dict_value,</if> + <if test="dictType != null and dictType != ''">dict_type,</if> + <if test="cssClass != null and cssClass != ''">css_class,</if> + <if test="listClass != null and listClass != ''">list_class,</if> + <if test="isDefault != null and isDefault != ''">is_default,</if> + <if test="status != null">status,</if> + <if test="remark != null and remark != ''">remark,</if> + <if test="createBy != null and createBy != ''">create_by,</if> + create_time + )values( + <if test="dictSort != null">#{dictSort},</if> + <if test="dictLabel != null and dictLabel != ''">#{dictLabel},</if> + <if test="dictValue != null and dictValue != ''">#{dictValue},</if> + <if test="dictType != null and dictType != ''">#{dictType},</if> + <if test="cssClass != null and cssClass != ''">#{cssClass},</if> + <if test="listClass != null and listClass != ''">#{listClass},</if> + <if test="isDefault != null and isDefault != ''">#{isDefault},</if> + <if test="status != null">#{status},</if> + <if test="remark != null and remark != ''">#{remark},</if> + <if test="createBy != null and createBy != ''">#{createBy},</if> + sysdate() + ) + </insert> + +</mapper> \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml new file mode 100644 index 0000000..55b4075 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml @@ -0,0 +1,105 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.system.mapper.SysDictTypeMapper"> + + <resultMap type="SysDictType" id="SysDictTypeResult"> + <id property="dictId" column="dict_id" /> + <result property="dictName" column="dict_name" /> + <result property="dictType" column="dict_type" /> + <result property="status" column="status" /> + <result property="createBy" column="create_by" /> + <result property="createTime" column="create_time" /> + <result property="updateBy" column="update_by" /> + <result property="updateTime" column="update_time" /> + </resultMap> + + <sql id="selectDictTypeVo"> + select dict_id, dict_name, dict_type, status, create_by, create_time, remark + from sys_dict_type + </sql> + + <select id="selectDictTypeList" parameterType="SysDictType" resultMap="SysDictTypeResult"> + <include refid="selectDictTypeVo"/> + <where> + <if test="dictName != null and dictName != ''"> + AND dict_name like concat('%', #{dictName}, '%') + </if> + <if test="status != null and status != ''"> + AND status = #{status} + </if> + <if test="dictType != null and dictType != ''"> + AND dict_type like concat('%', #{dictType}, '%') + </if> + <if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 --> + and date_format(create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d') + </if> + <if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 --> + and date_format(create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d') + </if> + </where> + </select> + + <select id="selectDictTypeAll" resultMap="SysDictTypeResult"> + <include refid="selectDictTypeVo"/> + </select> + + <select id="selectDictTypeById" parameterType="Long" resultMap="SysDictTypeResult"> + <include refid="selectDictTypeVo"/> + where dict_id = #{dictId} + </select> + + <select id="selectDictTypeByType" parameterType="String" resultMap="SysDictTypeResult"> + <include refid="selectDictTypeVo"/> + where dict_type = #{dictType} + </select> + + <select id="checkDictTypeUnique" parameterType="String" resultMap="SysDictTypeResult"> + <include refid="selectDictTypeVo"/> + where dict_type = #{dictType} limit 1 + </select> + + <delete id="deleteDictTypeById" parameterType="Long"> + delete from sys_dict_type where dict_id = #{dictId} + </delete> + + <delete id="deleteDictTypeByIds" parameterType="Long"> + delete from sys_dict_type where dict_id in + <foreach collection="array" item="dictId" open="(" separator="," close=")"> + #{dictId} + </foreach> + </delete> + + <update id="updateDictType" parameterType="SysDictType"> + update sys_dict_type + <set> + <if test="dictName != null and dictName != ''">dict_name = #{dictName},</if> + <if test="dictType != null and dictType != ''">dict_type = #{dictType},</if> + <if test="status != null">status = #{status},</if> + <if test="remark != null">remark = #{remark},</if> + <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if> + update_time = sysdate() + </set> + where dict_id = #{dictId} + </update> + + <insert id="insertDictType" parameterType="SysDictType"> + insert into sys_dict_type( + <if test="dictName != null and dictName != ''">dict_name,</if> + <if test="dictType != null and dictType != ''">dict_type,</if> + <if test="status != null">status,</if> + <if test="remark != null and remark != ''">remark,</if> + <if test="createBy != null and createBy != ''">create_by,</if> + create_time + )values( + <if test="dictName != null and dictName != ''">#{dictName},</if> + <if test="dictType != null and dictType != ''">#{dictType},</if> + <if test="status != null">#{status},</if> + <if test="remark != null and remark != ''">#{remark},</if> + <if test="createBy != null and createBy != ''">#{createBy},</if> + sysdate() + ) + </insert> + +</mapper> \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysLogininforMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysLogininforMapper.xml new file mode 100644 index 0000000..822d665 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysLogininforMapper.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.system.mapper.SysLogininforMapper"> + + <resultMap type="SysLogininfor" id="SysLogininforResult"> + <id property="infoId" column="info_id" /> + <result property="userName" column="user_name" /> + <result property="status" column="status" /> + <result property="ipaddr" column="ipaddr" /> + <result property="loginLocation" column="login_location" /> + <result property="browser" column="browser" /> + <result property="os" column="os" /> + <result property="msg" column="msg" /> + <result property="loginTime" column="login_time" /> + </resultMap> + + <insert id="insertLogininfor" parameterType="SysLogininfor"> + insert into sys_logininfor (user_name, status, ipaddr, login_location, browser, os, msg, login_time) + values (#{userName}, #{status}, #{ipaddr}, #{loginLocation}, #{browser}, #{os}, #{msg}, sysdate()) + </insert> + + <select id="selectLogininforList" parameterType="SysLogininfor" resultMap="SysLogininforResult"> + select info_id, user_name, ipaddr, login_location, browser, os, status, msg, login_time from sys_logininfor + <where> + <if test="ipaddr != null and ipaddr != ''"> + AND ipaddr like concat('%', #{ipaddr}, '%') + </if> + <if test="status != null and status != ''"> + AND status = #{status} + </if> + <if test="userName != null and userName != ''"> + AND user_name like concat('%', #{userName}, '%') + </if> + <if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 --> + AND login_time >= #{params.beginTime} + </if> + <if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 --> + AND login_time <= #{params.endTime} + </if> + </where> + order by info_id desc + </select> + + <delete id="deleteLogininforByIds" parameterType="Long"> + delete from sys_logininfor where info_id in + <foreach collection="array" item="infoId" open="(" separator="," close=")"> + #{infoId} + </foreach> + </delete> + + <update id="cleanLogininfor"> + truncate table sys_logininfor + </update> + +</mapper> \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml new file mode 100644 index 0000000..17f8a9b --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml @@ -0,0 +1,252 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper + PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.system.mapper.SysMenuMapper"> + + <resultMap type="SysMenu" id="SysMenuResult"> + <id property="menuId" column="menu_id" /> + <result property="menuName" column="menu_name" /> + <result property="parentName" column="parent_name" /> + <result property="parentId" column="parent_id" /> + <result property="orderNum" column="order_num" /> + <result property="path" column="path" /> + <result property="component" column="component" /> + <result property="query" column="query" /> + <result property="isFrame" column="is_frame" /> + <result property="isCache" column="is_cache" /> + <result property="menuType" column="menu_type" /> + <result property="visible" column="visible" /> + <result property="status" column="status" /> + <result property="perms" column="perms" /> + <result property="icon" column="icon" /> + <result property="createBy" column="create_by" /> + <result property="createTime" column="create_time" /> + <result property="updateTime" column="update_time" /> + <result property="updateBy" column="update_by" /> + <result property="remark" column="remark" /> + </resultMap> + + <sql id="selectMenuVo"> + select menu_id, menu_name, parent_id, order_num, path, component, `query`, is_frame, is_cache, menu_type, visible, status, ifnull(perms,'') as perms, icon, create_time + from sys_menu + </sql> + + <select id="selectMenuList" parameterType="SysMenu" resultMap="SysMenuResult"> + <include refid="selectMenuVo"/> + <where> + <if test="menuName != null and menuName != ''"> + AND menu_name like concat('%', #{menuName}, '%') + </if> + <if test="visible != null and visible != ''"> + AND visible = #{visible} + </if> + <if test="status != null and status != ''"> + AND status = #{status} + </if> + </where> + order by parent_id, order_num + </select> + + <select id="selectMenuTreeAll" resultMap="SysMenuResult"> + select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time + from sys_menu m where m.menu_type in ('M', 'C') and m.status = 0 + order by m.parent_id, m.order_num + </select> + + <select id="selectMenuListByUserId" parameterType="SysMenu" resultMap="SysMenuResult"> + select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time + from sys_menu m + left join sys_role_menu rm on m.menu_id = rm.menu_id + left join sys_user_role ur on rm.role_id = ur.role_id + left join sys_role ro on ur.role_id = ro.role_id + where ur.user_id = #{params.userId} + <if test="menuName != null and menuName != ''"> + AND m.menu_name like concat('%', #{menuName}, '%') + </if> + <if test="visible != null and visible != ''"> + AND m.visible = #{visible} + </if> + <if test="status != null and status != ''"> + AND m.status = #{status} + </if> + order by m.parent_id, m.order_num + </select> + + <select id="selectMenuTreeByUserId" parameterType="Long" resultMap="SysMenuResult"> + select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time + from sys_menu m + left join sys_role_menu rm on m.menu_id = rm.menu_id + left join sys_user_role ur on rm.role_id = ur.role_id + left join sys_role ro on ur.role_id = ro.role_id + left join sys_user u on ur.user_id = u.user_id + where u.user_id = #{userId} and m.menu_type in ('M', 'C') and m.status = 0 AND ro.status = 0 + order by m.parent_id, m.order_num + </select> + + <select id="selectMenuListByRoleId" resultType="Long"> + select m.menu_id + from sys_menu m + left join sys_role_menu rm on m.menu_id = rm.menu_id + where rm.role_id = #{roleId} + <if test="menuCheckStrictly"> + and m.menu_id not in (select m.parent_id from sys_menu m inner join sys_role_menu rm on m.menu_id = rm.menu_id and rm.role_id = #{roleId}) + </if> + order by m.parent_id, m.order_num + </select> + + <select id="selectMenuPerms" resultType="String"> + select distinct m.perms + from sys_menu m + left join sys_role_menu rm on m.menu_id = rm.menu_id + left join sys_user_role ur on rm.role_id = ur.role_id + </select> + + <select id="selectMenuPermsByUserId" parameterType="Long" resultType="String"> + select distinct m.perms + from sys_menu m + left join sys_role_menu rm on m.menu_id = rm.menu_id + left join sys_user_role ur on rm.role_id = ur.role_id + left join sys_role r on r.role_id = ur.role_id + where m.status = '0' and r.status = '0' and ur.user_id = #{userId} + </select> + + <select id="selectMenuPermsByRoleId" parameterType="Long" resultType="String"> + select distinct m.perms + from sys_menu m + left join sys_role_menu rm on m.menu_id = rm.menu_id + where m.status = '0' and rm.role_id = #{roleId} + </select> + + <select id="selectMenuById" parameterType="Long" resultMap="SysMenuResult"> + <include refid="selectMenuVo"/> + where menu_id = #{menuId} + </select> + + <select id="hasChildByMenuId" resultType="Integer"> + select count(1) from sys_menu where parent_id = #{menuId} + </select> + + <select id="checkMenuNameUnique" parameterType="SysMenu" resultMap="SysMenuResult"> + <include refid="selectMenuVo"/> + where menu_name=#{menuName} and parent_id = #{parentId} limit 1 + </select> + <select id="selectListByRoleId" resultType="com.ruoyi.common.core.domain.entity.SysMenu"> + select sm.menu_id AS menuId, sm.menu_name AS menuName, sm.parent_id AS parentId, sm.order_num AS orderNum, sm.`path` AS path, sm.component AS component, + sm.`query` AS query, sm.is_frame AS isFrame,sm.is_cache AS isCache, sm.menu_type AS menuType, sm.visible AS visible, sm.status AS status, + ifnull(sm.perms,'') as perms, sm.icon AS icon, sm.create_time AS createTime + from sys_role_menu srm + left join sys_menu sm on srm.menu_id = sm.menu_id + WHERE srm.role_id = #{roleId} + </select> + + <select id="getAllInIds" resultType="com.ruoyi.common.core.domain.entity.SysMenu"> + select + menu_id AS menuId, + menu_name AS menuName, + parent_id AS parentId, + order_num AS orderNum, + `path` AS path, + component AS component, + `query` AS query, + is_frame AS isFrame, + is_cache AS isCache, + menu_type AS menuType, + visible AS visible, + STATUS AS STATUS, + IFNULL( perms, '' ) AS perms, + icon AS icon, + create_time AS createTime + from sys_menu where menu_id in + <foreach collection="menusId" close=")" index="index" item="id" open="(" separator=","> + #{id} + </foreach> + </select> + <select id="selectList" resultType="com.ruoyi.common.core.domain.entity.SysMenu"> + select + menu_id AS menuId, + menu_name AS menuName, + parent_id AS parentId, + order_num AS orderNum, + `path` AS path, + component AS component, + `query` AS query, + is_frame AS isFrame, + is_cache AS isCache, + menu_type AS menuType, + visible AS visible, + STATUS AS STATUS, + IFNULL( perms, '' ) AS perms, + icon AS icon, + create_time AS createTime + from sys_menu + </select> + + <update id="updateMenu" parameterType="SysMenu"> + update sys_menu + <set> + <if test="menuName != null and menuName != ''">menu_name = #{menuName},</if> + <if test="parentId != null">parent_id = #{parentId},</if> + <if test="orderNum != null">order_num = #{orderNum},</if> + <if test="path != null and path != ''">path = #{path},</if> + <if test="component != null">component = #{component},</if> + <if test="query != null">`query` = #{query},</if> + <if test="isFrame != null and isFrame != ''">is_frame = #{isFrame},</if> + <if test="isCache != null and isCache != ''">is_cache = #{isCache},</if> + <if test="menuType != null and menuType != ''">menu_type = #{menuType},</if> + <if test="visible != null">visible = #{visible},</if> + <if test="status != null">status = #{status},</if> + <if test="perms !=null">perms = #{perms},</if> + <if test="icon !=null and icon != ''">icon = #{icon},</if> + <if test="remark != null and remark != ''">remark = #{remark},</if> + <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if> + update_time = sysdate() + </set> + where menu_id = #{menuId} + </update> + + <insert id="insertMenu" parameterType="SysMenu"> + insert into sys_menu( + <if test="menuId != null and menuId != 0">menu_id,</if> + <if test="parentId != null and parentId != 0">parent_id,</if> + <if test="menuName != null and menuName != ''">menu_name,</if> + <if test="orderNum != null">order_num,</if> + <if test="path != null and path != ''">path,</if> + <if test="component != null and component != ''">component,</if> + <if test="query != null and query != ''">`query`,</if> + <if test="isFrame != null and isFrame != ''">is_frame,</if> + <if test="isCache != null and isCache != ''">is_cache,</if> + <if test="menuType != null and menuType != ''">menu_type,</if> + <if test="visible != null">visible,</if> + <if test="status != null">status,</if> + <if test="perms !=null and perms != ''">perms,</if> + <if test="icon != null and icon != ''">icon,</if> + <if test="remark != null and remark != ''">remark,</if> + <if test="createBy != null and createBy != ''">create_by,</if> + create_time + )values( + <if test="menuId != null and menuId != 0">#{menuId},</if> + <if test="parentId != null and parentId != 0">#{parentId},</if> + <if test="menuName != null and menuName != ''">#{menuName},</if> + <if test="orderNum != null">#{orderNum},</if> + <if test="path != null and path != ''">#{path},</if> + <if test="component != null and component != ''">#{component},</if> + <if test="query != null and query != ''">#{query},</if> + <if test="isFrame != null and isFrame != ''">#{isFrame},</if> + <if test="isCache != null and isCache != ''">#{isCache},</if> + <if test="menuType != null and menuType != ''">#{menuType},</if> + <if test="visible != null">#{visible},</if> + <if test="status != null">#{status},</if> + <if test="perms !=null and perms != ''">#{perms},</if> + <if test="icon != null and icon != ''">#{icon},</if> + <if test="remark != null and remark != ''">#{remark},</if> + <if test="createBy != null and createBy != ''">#{createBy},</if> + sysdate() + ) + </insert> + + <delete id="deleteMenuById" parameterType="Long"> + delete from sys_menu where menu_id = #{menuId} + </delete> + +</mapper> \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml new file mode 100644 index 0000000..65d3079 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.system.mapper.SysNoticeMapper"> + + <resultMap type="SysNotice" id="SysNoticeResult"> + <result property="noticeId" column="notice_id" /> + <result property="noticeTitle" column="notice_title" /> + <result property="noticeType" column="notice_type" /> + <result property="noticeContent" column="notice_content" /> + <result property="status" column="status" /> + <result property="createBy" column="create_by" /> + <result property="createTime" column="create_time" /> + <result property="updateBy" column="update_by" /> + <result property="updateTime" column="update_time" /> + <result property="remark" column="remark" /> + </resultMap> + + <sql id="selectNoticeVo"> + select notice_id, notice_title, notice_type, cast(notice_content as char) as notice_content, status, create_by, create_time, update_by, update_time, remark + from sys_notice + </sql> + + <select id="selectNoticeById" parameterType="Long" resultMap="SysNoticeResult"> + <include refid="selectNoticeVo"/> + where notice_id = #{noticeId} + </select> + + <select id="selectNoticeList" parameterType="SysNotice" resultMap="SysNoticeResult"> + <include refid="selectNoticeVo"/> + <where> + <if test="noticeTitle != null and noticeTitle != ''"> + AND notice_title like concat('%', #{noticeTitle}, '%') + </if> + <if test="noticeType != null and noticeType != ''"> + AND notice_type = #{noticeType} + </if> + <if test="createBy != null and createBy != ''"> + AND create_by like concat('%', #{createBy}, '%') + </if> + </where> + </select> + + <insert id="insertNotice" parameterType="SysNotice"> + insert into sys_notice ( + <if test="noticeTitle != null and noticeTitle != '' ">notice_title, </if> + <if test="noticeType != null and noticeType != '' ">notice_type, </if> + <if test="noticeContent != null and noticeContent != '' ">notice_content, </if> + <if test="status != null and status != '' ">status, </if> + <if test="remark != null and remark != ''">remark,</if> + <if test="createBy != null and createBy != ''">create_by,</if> + create_time + )values( + <if test="noticeTitle != null and noticeTitle != ''">#{noticeTitle}, </if> + <if test="noticeType != null and noticeType != ''">#{noticeType}, </if> + <if test="noticeContent != null and noticeContent != ''">#{noticeContent}, </if> + <if test="status != null and status != ''">#{status}, </if> + <if test="remark != null and remark != ''">#{remark},</if> + <if test="createBy != null and createBy != ''">#{createBy},</if> + sysdate() + ) + </insert> + + <update id="updateNotice" parameterType="SysNotice"> + update sys_notice + <set> + <if test="noticeTitle != null and noticeTitle != ''">notice_title = #{noticeTitle}, </if> + <if test="noticeType != null and noticeType != ''">notice_type = #{noticeType}, </if> + <if test="noticeContent != null">notice_content = #{noticeContent}, </if> + <if test="status != null and status != ''">status = #{status}, </if> + <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if> + update_time = sysdate() + </set> + where notice_id = #{noticeId} + </update> + + <delete id="deleteNoticeById" parameterType="Long"> + delete from sys_notice where notice_id = #{noticeId} + </delete> + + <delete id="deleteNoticeByIds" parameterType="Long"> + delete from sys_notice where notice_id in + <foreach item="noticeId" collection="array" open="(" separator="," close=")"> + #{noticeId} + </foreach> + </delete> + +</mapper> \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml new file mode 100644 index 0000000..cc568e7 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml @@ -0,0 +1,102 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.system.mapper.SysOperLogMapper"> + + <resultMap type="SysOperLog" id="SysOperLogResult"> + <id property="operId" column="oper_id" /> + <result property="title" column="title" /> + <result property="businessType" column="business_type" /> + <result property="method" column="method" /> + <result property="requestMethod" column="request_method" /> + <result property="operatorType" column="operator_type" /> + <result property="operName" column="oper_name" /> + <result property="deptName" column="dept_name" /> + <result property="operUrl" column="oper_url" /> + <result property="operIp" column="oper_ip" /> + <result property="operLocation" column="oper_location" /> + <result property="operParam" column="oper_param" /> + <result property="jsonResult" column="json_result" /> + <result property="status" column="status" /> + <result property="errorMsg" column="error_msg" /> + <result property="operTime" column="oper_time" /> + <result property="costTime" column="cost_time" /> + <result property="companyName" column="companyName" /> + <result property="roleName" column="roleName" /> + <result property="phonenumber" column="phonenumber" /> + <result property="userId" column="userId" /> + <result property="nickName" column="nickName" /> + </resultMap> + + <sql id="selectOperLogVo"> + select oper_id, title, business_type, `method`, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, + json_result, status, error_msg, oper_time, cost_time,companyName,roleName,phonenumber,userId,nickName + from sys_oper_log + </sql> + + <insert id="insertOperlog" parameterType="SysOperLog"> + insert into sys_oper_log(title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, cost_time,companyName,roleName,phonenumber,userId,nickName, oper_time) + values (#{title}, #{businessType}, #{method}, #{requestMethod}, #{operatorType}, #{operName}, #{deptName}, #{operUrl}, #{operIp}, #{operLocation}, #{operParam}, #{jsonResult}, #{status}, #{errorMsg}, #{costTime},#{companyName},#{roleName},#{phonenumber},#{userId},#{nickName}, sysdate()) + </insert> + + <select id="selectOperLogList" parameterType="SysOperLog" resultMap="SysOperLogResult"> + <include refid="selectOperLogVo"/> + <where> + <if test="operIp != null and operIp != ''"> + AND oper_ip like concat('%', #{operIp}, '%') + </if> + <if test="title != null and title != ''"> + AND title like concat('%', #{title}, '%') + </if> + <if test="businessType != null"> + AND business_type = #{businessType} + </if> + <if test="businessTypes != null and businessTypes.length > 0"> + AND business_type in + <foreach collection="businessTypes" item="businessType" open="(" separator="," close=")"> + #{businessType} + </foreach> + </if> + <if test="status != null"> + AND status = #{status} + </if> + <if test="operName != null and operName != ''"> + AND oper_name like concat('%', #{operName}, '%') + </if> + <if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 --> + AND oper_time >= #{params.beginTime} + </if> + <if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 --> + AND oper_time <= #{params.endTime} + </if> + </where> + order by oper_id desc + </select> + + <delete id="deleteOperLogByIds" parameterType="Long"> + delete from sys_oper_log where oper_id in + <foreach collection="operIds" item="operId" open="(" separator="," close=")"> + #{operId} + </foreach> + </delete> + + <select id="selectOperLogById" parameterType="Long" resultMap="SysOperLogResult"> + <include refid="selectOperLogVo"/> + where oper_id = #{operId} + </select> + <select id="selectOperLogPageList" resultType="com.ruoyi.system.vo.SysOperLogVO"> + select sol.oper_id AS operId, sol.title AS title, sol.business_type AS businessType, sol.`method` AS method, sol.request_method AS requestMethod, + sol.operator_type AS operatorType,sol.oper_name AS operName,sol.dept_name AS deptName, sol.oper_url AS operUrl, sol.oper_ip AS operIp, + sol.oper_location AS operLocation, sol.oper_param AS operLocation,sol.json_result AS jsonResult, sol.status AS status,sol.error_msg AS errorMsg, + sol.oper_time AS operTime, sol.cost_time AS costTime,sol.companyName AS companyName,sol.roleName AS roleName,sol.phonenumber AS phonenumber, + sol.userId AS userId,sol.nickName AS nickName + from sys_oper_log sol + ORDER BY sol.oper_time DESC + </select> + + <update id="cleanOperLog"> + truncate table sys_oper_log + </update> + +</mapper> \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml new file mode 100644 index 0000000..227c459 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.system.mapper.SysPostMapper"> + + <resultMap type="SysPost" id="SysPostResult"> + <id property="postId" column="post_id" /> + <result property="postCode" column="post_code" /> + <result property="postName" column="post_name" /> + <result property="postSort" column="post_sort" /> + <result property="status" column="status" /> + <result property="createBy" column="create_by" /> + <result property="createTime" column="create_time" /> + <result property="updateBy" column="update_by" /> + <result property="updateTime" column="update_time" /> + <result property="remark" column="remark" /> + </resultMap> + + <sql id="selectPostVo"> + select post_id, post_code, post_name, post_sort, status, create_by, create_time, remark + from sys_post + </sql> + + <select id="selectPostList" parameterType="SysPost" resultMap="SysPostResult"> + <include refid="selectPostVo"/> + <where> + <if test="postCode != null and postCode != ''"> + AND post_code like concat('%', #{postCode}, '%') + </if> + <if test="status != null and status != ''"> + AND status = #{status} + </if> + <if test="postName != null and postName != ''"> + AND post_name like concat('%', #{postName}, '%') + </if> + </where> + </select> + + <select id="selectPostAll" resultMap="SysPostResult"> + <include refid="selectPostVo"/> + </select> + + <select id="selectPostById" parameterType="Long" resultMap="SysPostResult"> + <include refid="selectPostVo"/> + where post_id = #{postId} + </select> + + <select id="selectPostListByUserId" parameterType="Long" resultType="Long"> + select p.post_id + from sys_post p + left join sys_user_post up on up.post_id = p.post_id + left join sys_user u on u.user_id = up.user_id + where u.user_id = #{userId} + </select> + + <select id="selectPostsByUserName" parameterType="String" resultMap="SysPostResult"> + select p.post_id, p.post_name, p.post_code + from sys_post p + left join sys_user_post up on up.post_id = p.post_id + left join sys_user u on u.user_id = up.user_id + where u.user_name = #{userName} + </select> + + <select id="checkPostNameUnique" parameterType="String" resultMap="SysPostResult"> + <include refid="selectPostVo"/> + where post_name=#{postName} limit 1 + </select> + + <select id="checkPostCodeUnique" parameterType="String" resultMap="SysPostResult"> + <include refid="selectPostVo"/> + where post_code=#{postCode} limit 1 + </select> + + <update id="updatePost" parameterType="SysPost"> + update sys_post + <set> + <if test="postCode != null and postCode != ''">post_code = #{postCode},</if> + <if test="postName != null and postName != ''">post_name = #{postName},</if> + <if test="postSort != null">post_sort = #{postSort},</if> + <if test="status != null and status != ''">status = #{status},</if> + <if test="remark != null">remark = #{remark},</if> + <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if> + update_time = sysdate() + </set> + where post_id = #{postId} + </update> + + <insert id="insertPost" parameterType="SysPost" useGeneratedKeys="true" keyProperty="postId"> + insert into sys_post( + <if test="postId != null and postId != 0">post_id,</if> + <if test="postCode != null and postCode != ''">post_code,</if> + <if test="postName != null and postName != ''">post_name,</if> + <if test="postSort != null">post_sort,</if> + <if test="status != null and status != ''">status,</if> + <if test="remark != null and remark != ''">remark,</if> + <if test="createBy != null and createBy != ''">create_by,</if> + create_time + )values( + <if test="postId != null and postId != 0">#{postId},</if> + <if test="postCode != null and postCode != ''">#{postCode},</if> + <if test="postName != null and postName != ''">#{postName},</if> + <if test="postSort != null">#{postSort},</if> + <if test="status != null and status != ''">#{status},</if> + <if test="remark != null and remark != ''">#{remark},</if> + <if test="createBy != null and createBy != ''">#{createBy},</if> + sysdate() + ) + </insert> + + <delete id="deletePostById" parameterType="Long"> + delete from sys_post where post_id = #{postId} + </delete> + + <delete id="deletePostByIds" parameterType="Long"> + delete from sys_post where post_id in + <foreach collection="array" item="postId" open="(" separator="," close=")"> + #{postId} + </foreach> + </delete> + +</mapper> \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml new file mode 100644 index 0000000..7c4139b --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.system.mapper.SysRoleDeptMapper"> + + <resultMap type="SysRoleDept" id="SysRoleDeptResult"> + <result property="roleId" column="role_id" /> + <result property="deptId" column="dept_id" /> + </resultMap> + + <delete id="deleteRoleDeptByRoleId" parameterType="Long"> + delete from sys_role_dept where role_id=#{roleId} + </delete> + + <select id="selectCountRoleDeptByDeptId" resultType="Integer"> + select count(1) from sys_role_dept where dept_id=#{deptId} + </select> + + <delete id="deleteRoleDept" parameterType="Long"> + delete from sys_role_dept where role_id in + <foreach collection="array" item="roleId" open="(" separator="," close=")"> + #{roleId} + </foreach> + </delete> + + <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> \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml new file mode 100644 index 0000000..f3dfeeb --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml @@ -0,0 +1,234 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.system.mapper.SysRoleMapper"> + + <resultMap type="SysRole" id="SysRoleResult"> + <id property="roleId" column="role_id" /> + <result property="roleName" column="role_name" /> + <result property="roleKey" column="role_key" /> + <result property="roleSort" column="role_sort" /> + <result property="dataScope" column="data_scope" /> + <result property="menuCheckStrictly" column="menu_check_strictly" /> + <result property="deptCheckStrictly" column="dept_check_strictly" /> + <result property="status" column="status" /> + <result property="delFlag" column="del_flag" /> + <result property="createBy" column="create_by" /> + <result property="createTime" column="create_time" /> + <result property="updateBy" column="update_by" /> + <result property="updateTime" column="update_time" /> + <result property="remark" column="remark" /> + <result property="removeDays" column="removeDays" /> + <result property="postType" column="postType" /> + </resultMap> + + <sql id="selectRoleVo"> + select distinct r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.menu_check_strictly, r.dept_check_strictly, + r.status, r.del_flag, r.create_time, r.remark,r.postType,r.removeDays + from sys_role r + left join sys_user_role ur on ur.role_id = r.role_id + left join sys_user u on u.user_id = ur.user_id + left join sys_dept d on u.dept_id = d.dept_id + </sql> + + <select id="selectRoleList" parameterType="SysRole" resultMap="SysRoleResult"> + <include refid="selectRoleVo"/> + where r.del_flag = '0' + <if test="roleId != null and roleId != 0"> + AND r.role_id = #{roleId} + </if> + <if test="roleName != null and roleName != ''"> + AND r.role_name like concat('%', #{roleName}, '%') + </if> + <if test="status != null and status != ''"> + AND r.status = #{status} + </if> + <if test="roleKey != null and roleKey != ''"> + AND r.role_key like concat('%', #{roleKey}, '%') + </if> + order by r.role_sort + </select> + + <select id="selectRolePermissionByUserId" parameterType="Long" resultMap="SysRoleResult"> + <include refid="selectRoleVo"/> + WHERE r.del_flag = '0' and ur.user_id = #{userId} + </select> + + <select id="selectRoleAll" resultMap="SysRoleResult"> + <include refid="selectRoleVo"/> + </select> + + <select id="selectRoleListByUserId" parameterType="Long" resultType="Long"> + select r.role_id + from sys_role r + left join sys_user_role ur on ur.role_id = r.role_id + left join sys_user u on u.user_id = ur.user_id + where u.user_id = #{userId} + </select> + + <select id="selectRoleById" parameterType="Long" resultMap="SysRoleResult"> + <include refid="selectRoleVo"/> + where r.role_id = #{roleId} + </select> + + <select id="selectRolesByUserName" parameterType="String" resultMap="SysRoleResult"> + <include refid="selectRoleVo"/> + WHERE r.del_flag = '0' and u.user_name = #{userName} + </select> + + <select id="checkRoleNameUnique" parameterType="String" resultMap="SysRoleResult"> + <include refid="selectRoleVo"/> + where r.role_name=#{roleName} and r.del_flag = '0' limit 1 + </select> + + <select id="checkRoleKeyUnique" parameterType="String" resultMap="SysRoleResult"> + <include refid="selectRoleVo"/> + where r.role_key=#{roleKey} and r.del_flag = '0' limit 1 + </select> + <select id="selectCountByRoleName" resultType="java.lang.Integer"> + select count(*) from sys_role + <where> + <if test="roleName != null and roleName != ''"> + AND role_name = #{roleName} + </if> + AND del_flag = 0 + </where> + </select> + <select id="selectPageList" resultType="com.ruoyi.common.core.domain.entity.SysRole"> + select a.role_id AS roleId, a.role_name AS roleName, a.role_key AS roleKey, a.role_sort AS roleSort, a.data_scope AS dataScope, + a.menu_check_strictly AS menuCheckStrictly, a.dept_check_strictly AS deptCheckStrictly,a.status AS status, a.del_flag AS delFlag, + a.create_time AS createTime,a.create_by AS createBy,a.postType AS postType,a.removeDays AS removeDays, + IFNULL(b.userCount,0) as userCount + from sys_role a + LEFT JOIN + (SELECT + r.role_id AS roleId, + COUNT(ur.user_id) AS userCount + FROM sys_role r + LEFT JOIN sys_user_role ur ON r.role_id = ur.role_id + where r.del_flag = 0 + GROUP BY r.role_id) b on a.role_id = b.roleId + <where> + <if test="query.roleName != null and query.roleName != ''"> + AND a.role_name LIKE concat('%',#{query.roleName},'%') + </if> + <if test="query.status != null"> + AND a.status = #{query.status} + </if> + AND a.del_flag = 0 + </where> + </select> + <select id="selectCount" resultType="java.lang.Integer"> + select count(*) from sys_role + <where> + <if test="status != null"> + AND status = #{status} + </if> + AND del_flag = 0 + </where> + </select> + <select id="selectListByDelFlag" resultType="com.ruoyi.common.core.domain.entity.SysRole"> + select role_id AS roleId, role_name AS roleName, role_key AS roleKey, role_sort AS roleSort, data_scope AS dataScope, + menu_check_strictly AS menuCheckStrictly, dept_check_strictly AS deptCheckStrictly,status AS status, del_flag AS delFlag, + create_time AS createTime,create_by AS createBy,postType AS postType,removeDays AS removeDays + from sys_role where del_flag = 0 + </select> + + <select id="selectRoleByUserId" resultType="com.ruoyi.common.core.domain.entity.SysRole"> + select distinct r.role_id AS roleId, r.role_name AS roleName, r.role_key AS roleKey, r.role_sort AS roleSort, r.data_scope AS dataScope, + r.menu_check_strictly AS menuCheckStrictly, r.dept_check_strictly AS deptCheckStrictly,r.status AS status, + r.del_flag AS delFlag, r.create_time AS createTime,r.create_by AS createBy,r.postType AS postType,r.removeDays AS removeDays + from sys_role r + left join sys_user_role ur on ur.role_id = r.role_id + where ur.user_id = #{userId} + </select> + <select id="selectByUserId" resultType="java.lang.String"> + select t2.role_name from sys_user_role t1 + left join sys_role t2 on t1.role_id = t2.role_id + where t1.user_id = #{userId} + </select> + <select id="selectRoleByUserIds" resultType="com.ruoyi.common.core.domain.entity.SysRole"> + select + a.user_id as role_id, + b.nick_name as role_name + from sys_user_role a + left join sys_user b on a.user_id = b.user_id + where a.role_id in + <foreach item="item" index="index" collection="roleIds" open="(" separator="," close=")"> + #{item} + </foreach> + </select> + + <insert id="insertRole" parameterType="SysRole" useGeneratedKeys="true" keyProperty="roleId"> + insert into sys_role( + <if test="roleId != null and roleId != 0">role_id,</if> + <if test="roleName != null and roleName != ''">role_name,</if> + <if test="roleKey != null and roleKey != ''">role_key,</if> + <if test="roleSort != null">role_sort,</if> + <if test="dataScope != null and dataScope != ''">data_scope,</if> + <if test="menuCheckStrictly != null">menu_check_strictly,</if> + <if test="deptCheckStrictly != null">dept_check_strictly,</if> + <if test="status != null and status != ''">status,</if> + <if test="remark != null and remark != ''">remark,</if> + <if test="createBy != null and createBy != ''">create_by,</if> + <if test="removeDays != null">removeDays,</if> + <if test="postType != null">postType,</if> + create_time + )values( + <if test="roleId != null and roleId != 0">#{roleId},</if> + <if test="roleName != null and roleName != ''">#{roleName},</if> + <if test="roleKey != null and roleKey != ''">#{roleKey},</if> + <if test="roleSort != null">#{roleSort},</if> + <if test="dataScope != null and dataScope != ''">#{dataScope},</if> + <if test="menuCheckStrictly != null">#{menuCheckStrictly},</if> + <if test="deptCheckStrictly != null">#{deptCheckStrictly},</if> + <if test="status != null and status != ''">#{status},</if> + <if test="remark != null and remark != ''">#{remark},</if> + <if test="createBy != null and createBy != ''">#{createBy},</if> + <if test="removeDays != null">#{removeDays},</if> + <if test="postType != null">#{postType},</if> + sysdate() + ) + </insert> + + <update id="updateRole" parameterType="SysRole"> + update sys_role + <set> + <if test="roleName != null and roleName != ''">role_name = #{roleName},</if> + <if test="roleKey != null and roleKey != ''">role_key = #{roleKey},</if> + <if test="roleSort != null">role_sort = #{roleSort},</if> + <if test="dataScope != null and dataScope != ''">data_scope = #{dataScope},</if> + <if test="menuCheckStrictly != null">menu_check_strictly = #{menuCheckStrictly},</if> + <if test="deptCheckStrictly != null">dept_check_strictly = #{deptCheckStrictly},</if> + <if test="status != null and status != ''">status = #{status},</if> + <if test="remark != null">remark = #{remark},</if> + <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if> + <if test="removeDays != null">removeDays = #{removeDays},</if> + <if test="postType != null">postType = #{postType},</if> + update_time = sysdate() + </set> + where role_id = #{roleId} + </update> + <update id="updateStatus" parameterType="SysRole"> + update sys_role + <set> + <if test="status != null">status = #{status},</if> + <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if> + update_time = sysdate() + </set> + where role_id = #{roleId} + </update> + + <delete id="deleteRoleById" parameterType="Long"> + update sys_role set del_flag = '2' where role_id = #{roleId} + </delete> + + <delete id="deleteRoleByIds" parameterType="Long"> + update sys_role set del_flag = '2' where role_id in + <foreach collection="roleIds" item="roleId" open="(" separator="," close=")"> + #{roleId} + </foreach> + </delete> + +</mapper> \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml new file mode 100644 index 0000000..2853301 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.system.mapper.SysRoleMenuMapper"> + + <resultMap type="SysRoleMenu" id="SysRoleMenuResult"> + <result property="roleId" column="role_id" /> + <result property="menuId" column="menu_id" /> + </resultMap> + + <select id="checkMenuExistRole" resultType="Integer"> + select count(1) from sys_role_menu where menu_id = #{menuId} + </select> + + <delete id="deleteRoleMenuByRoleId" parameterType="Long"> + delete from sys_role_menu where role_id=#{roleId} + </delete> + + <delete id="deleteRoleMenu" parameterType="Long"> + delete from sys_role_menu where role_id in + <foreach collection="ids" item="roleId" open="(" separator="," close=")"> + #{roleId} + </foreach> + </delete> + + <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> \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml new file mode 100644 index 0000000..a3fff3f --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml @@ -0,0 +1,356 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.system.mapper.SysUserMapper"> + + <resultMap type="SysUser" id="SysUserResult"> + <id property="userId" column="user_id" /> + <result property="deptId" column="dept_id" /> + <result property="userName" column="user_name" /> + <result property="nickName" column="nick_name" /> + <result property="email" column="email" /> + <result property="phonenumber" column="phonenumber" /> + <result property="sex" column="sex" /> + <result property="avatar" column="avatar" /> + <result property="password" column="password" /> + <result property="status" column="status" /> + <result property="delFlag" column="del_flag" /> + <result property="loginIp" column="login_ip" /> + <result property="loginDate" column="login_date" /> + <result property="createBy" column="create_by" /> + <result property="createTime" column="create_time" /> + <result property="updateBy" column="update_by" /> + <result property="updateTime" column="update_time" /> + <result property="remark" column="remark" /> + <result property="ifBlack" column="ifBlack" /> + <result property="districtId" column="districtId" /> + <association property="dept" javaType="SysDept" resultMap="deptResult" /> + <collection property="roles" javaType="java.util.List" resultMap="RoleResult" /> + </resultMap> + + <resultMap id="deptResult" type="SysDept"> + <id property="deptId" column="dept_id" /> + <result property="parentId" column="parent_id" /> + <result property="deptName" column="dept_name" /> + <result property="ancestors" column="ancestors" /> + <result property="orderNum" column="order_num" /> + <result property="leader" column="leader" /> + <result property="status" column="dept_status" /> + </resultMap> + + <resultMap id="RoleResult" type="SysRole"> + <id property="roleId" column="role_id" /> + <result property="roleName" column="role_name" /> + <result property="roleKey" column="role_key" /> + <result property="roleSort" column="role_sort" /> + <result property="dataScope" column="data_scope" /> + <result property="status" column="role_status" /> + </resultMap> + + <sql id="selectUserVo"> + select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, + d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.status as dept_status, + r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status + from sys_user u + left join sys_dept d on u.dept_id = d.dept_id + left join sys_user_role ur on u.user_id = ur.user_id + left join sys_role r on r.role_id = ur.role_id + </sql> + + <select id="selectUserList" parameterType="SysUser" resultMap="SysUserResult"> + select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, + u.create_by, u.create_time, u.remark, d.dept_name, d.leader from sys_user u + left join sys_dept d on u.dept_id = d.dept_id + where u.del_flag = '0' + <if test="userId != null and userId != 0"> + AND u.user_id = #{userId} + </if> + <if test="userName != null and userName != ''"> + AND u.user_name like concat('%', #{userName}, '%') + </if> + <if test="status != null and status != ''"> + AND u.status = #{status} + </if> + <if test="phonenumber != null and phonenumber != ''"> + AND u.phonenumber like concat('%', #{phonenumber}, '%') + </if> + <if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 --> + AND date_format(u.create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d') + </if> + <if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 --> + AND date_format(u.create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d') + </if> + <if test="deptId != null and deptId != 0"> + AND (u.dept_id = #{deptId} OR u.dept_id IN ( SELECT t.dept_id FROM sys_dept t WHERE find_in_set(#{deptId}, ancestors) )) + </if> + <!-- 数据范围过滤 --> + ${params.dataScope} + </select> + + <select id="selectAllocatedList" parameterType="SysUser" resultMap="SysUserResult"> + select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time + from sys_user u + left join sys_dept d on u.dept_id = d.dept_id + left join sys_user_role ur on u.user_id = ur.user_id + left join sys_role r on r.role_id = ur.role_id + where u.del_flag = '0' and r.role_id = #{roleId} + <if test="userName != null and userName != ''"> + AND u.user_name like concat('%', #{userName}, '%') + </if> + <if test="phonenumber != null and phonenumber != ''"> + AND u.phonenumber like concat('%', #{phonenumber}, '%') + </if> + <!-- 数据范围过滤 --> + ${params.dataScope} + </select> + + <select id="selectUnallocatedList" parameterType="SysUser" resultMap="SysUserResult"> + select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_time + from sys_user u + left join sys_dept d on u.dept_id = d.dept_id + left join sys_user_role ur on u.user_id = ur.user_id + left join sys_role r on r.role_id = ur.role_id + where u.del_flag = '0' and (r.role_id != #{roleId} or r.role_id IS NULL) + and u.user_id not in (select u.user_id from sys_user u inner join sys_user_role ur on u.user_id = ur.user_id and ur.role_id = #{roleId}) + <if test="userName != null and userName != ''"> + AND u.user_name like concat('%', #{userName}, '%') + </if> + <if test="phonenumber != null and phonenumber != ''"> + AND u.phonenumber like concat('%', #{phonenumber}, '%') + </if> + <!-- 数据范围过滤 --> + ${params.dataScope} + </select> + + <select id="selectUserByUserName" parameterType="String" resultMap="SysUserResult"> + <include refid="selectUserVo"/> + where u.user_name = #{userName} and u.del_flag = '0' + </select> + + <select id="selectUserById" parameterType="Long" resultType="com.ruoyi.common.core.domain.entity.SysUser"> + select u.user_id AS userId, u.dept_id AS deptId, u.user_name AS userName, u.nick_name AS nickName, u.email AS email, u.avatar AS avatar, + u.phonenumber AS phonenumber, u.sex AS sex, u.status AS status, u.del_flag AS delFlag, u.login_ip AS loginIp, + u.login_date AS loginDate, u.create_by AS createBy, u.create_time AS createTime, u.remark AS remark,u.ifBlack AS ifBlack, u.districtId AS districtId, + ur.role_id AS roleId,sr.role_name AS roleName,u.deptName as deptName + from sys_user u + left join sys_user_role ur on u.user_id = ur.user_id + left join sys_role sr on ur.role_id = sr.role_id + left join sys_dept sd on u.dept_id = sd.dept_id + where u.user_id = #{userId} and u.del_flag = 0 + </select> + + <select id="checkUserNameUnique" parameterType="String" resultMap="SysUserResult"> + select user_id, user_name from sys_user where user_name = #{userName} and del_flag = '0' limit 1 + </select> + + <select id="checkPhoneUnique" parameterType="String" resultMap="SysUserResult"> + select user_id, phonenumber from sys_user where phonenumber = #{phonenumber} and del_flag = '0' limit 1 + </select> + + <select id="checkEmailUnique" parameterType="String" resultMap="SysUserResult"> + select user_id, email from sys_user where email = #{email} and del_flag = '0' limit 1 + </select> + <select id="selectUserByIds" resultType="com.ruoyi.common.core.domain.entity.SysUser"> + select user_id AS userId, dept_id AS deptId, user_name AS userName, nick_name AS nickName, email AS email, avatar AS avatar, phonenumber AS phonenumber + from sys_user where user_id in + <foreach collection="userIds" separator="," item="userId" open="(" close=")"> + #{userId} + </foreach> + </select> + <select id="selectList" resultType="com.ruoyi.common.core.domain.entity.SysUser"> + select u.user_id AS userId, u.dept_id AS deptId, u.user_name AS userName, u.nick_name AS nickName, u.email AS email, u.avatar AS avatar, + u.phonenumber AS phonenumber, u.sex AS sex, u.status AS status, u.del_flag AS delFlag, u.login_ip AS loginIp, + u.login_date AS loginDate, u.create_by AS createBy, u.create_time AS createTime, u.remark AS remark,u.ifBlack AS ifBlack, u.districtId AS districtId + from sys_user u + WHERE u.del_flag = 0 + </select> + <select id="selectCount" resultType="java.lang.Integer"> + select count(*) from sys_user + <where> + <if test="status != null"> + AND status = #{status} + </if> + AND del_flag = 0 + </where> + </select> + <select id="selectListByNamePhone" resultType="com.ruoyi.common.core.domain.entity.SysUser"> + select u.user_id AS userId, u.dept_id AS deptId, u.user_name AS userName, u.nick_name AS nickName, u.email AS email, u.avatar AS avatar, + u.phonenumber AS phonenumber, u.sex AS sex, u.status AS status, u.del_flag AS delFlag, u.login_ip AS loginIp, + u.login_date AS loginDate, u.create_by AS createBy, u.create_time AS createTime, u.remark AS remark,u.ifBlack AS ifBlack, u.districtId AS districtId + from sys_user u + WHERE u.del_flag = 0 + <if test="name != null and name != ''"> + AND (u.nick_name LIKE concat('%',#{name},'%') + OR u.phonenumber LIKE concat('%',#{name},'%')) + </if> + </select> + <select id="selectUserByUserNameList" resultType="com.ruoyi.common.core.domain.entity.SysUser"> + select u.user_id AS userId, u.dept_id AS deptId, u.user_name AS userName, u.nick_name AS nickName, u.email AS email, u.avatar AS avatar, + u.phonenumber AS phonenumber + from sys_user u + WHERE u.del_flag = 0 + <if test="names != null and names.size()>0"> + AND u.user_name IN + <foreach collection="names" close=")" open="(" item="name" separator=","> + #{name} + </foreach> + </if> + </select> + <select id="userInfo" resultType="com.ruoyi.system.vo.UserInfoVo"> + select t1.*,t2.companyName,t2.companyType ,tq.qrcodeLink from sys_user t1 + left join t_company t2 on t2.id = t1.companyId + left join t_qrcode tq on t1.user_id = tq.otherId and tq.type=1 + where t1.user_id = #{id} + </select> + <select id="selectByPhone" resultType="com.ruoyi.common.core.domain.entity.SysUser"> + select u.user_id AS userId, u.dept_id AS deptId, u.user_name AS userName, u.nick_name AS nickName, u.email AS email, u.avatar AS avatar, + u.phonenumber AS phonenumber, u.sex AS sex, u.status AS status, u.del_flag AS delFlag, u.login_ip AS loginIp, + u.login_date AS loginDate, u.create_by AS createBy, u.create_time AS createTime, u.remark AS remark + from sys_user u where u.phonenumber = #{phonenumber} and u.status = 0 and u.del_flag = 0 + </select> + <select id="getUserInfoBy" resultType="com.ruoyi.system.vo.UserInfoVo"> + select t1.*,t2.companyName,t2.companyType from sys_user t1 + left join t_company t2 on t2.id = t1.companyId + where t1.singleNum = #{singleNum} + </select> + <select id="getUserRole" resultType="java.lang.Long"> + select role_id from sys_user_role where sys_user_role.user_id = #{userId} + </select> + <select id="selectAllList" resultType="com.ruoyi.common.core.domain.entity.SysUser"> + select * from sys_user + </select> + <select id="pageList" resultType="com.ruoyi.system.vo.SysUserVO"> + select u.user_id AS userId, u.dept_id AS deptId, u.user_name AS userName, u.nick_name AS nickName, u.email AS email, u.avatar AS avatar,u.disable_remark AS disableRemark, + u.phonenumber AS phonenumber, u.sex AS sex, u.status AS status, u.del_flag AS delFlag, u.login_ip AS loginIp,u.operating_time AS operatingTime,u.operating_person AS operatingPerson, + u.login_date AS loginDate, u.create_by AS createBy, u.create_time AS createTime, u.remark AS remark,u.ifBlack AS ifBlack, u.districtId AS districtId, + r.role_id AS roleId, r.role_name AS roleName, r.role_key AS roleKey, r.role_sort AS roleSort, r.data_scope AS dataScope, r.status as role_status,u.deptName as deptName + from sys_user u + left join sys_user_role ur on u.user_id = ur.user_id + left join sys_role r on r.role_id = ur.role_id + WHERE u.del_flag = 0 + <if test="query.nickNameOrPhone != null and query.nickNameOrPhone != ''"> + AND (u.nick_name LIKE concat('%',#{query.nickNameOrPhone},'%') + OR u.phonenumber LIKE concat('%',#{query.nickNameOrPhone},'%')) + </if> + <if test="query.status != null and query.status != ''"> + AND u.status = #{query.status} + </if> + <if test="query.deptIds != null and query.deptIds.size()>0"> + AND u.user_id IN (select DISTINCT user_id from t_dept_to_user where dept_id IN + <foreach collection="query.deptIds" close=")" open="(" item="deptId" separator=","> + #{deptId} + </foreach>) + </if> + <if test="query.roleIds != null and query.roleIds.size()>0"> + AND r.role_id IN + <foreach collection="query.roleIds" close=")" open="(" item="roleId" separator=","> + #{roleId} + </foreach> + </if> + ORDER BY u.create_time DESC + </select> + <select id="selectIdByPhone" resultType="java.lang.Long"> + select user_id from sys_user where phonenumber = #{phonenumber} and status = 0 and del_flag = 0 + </select> + + <insert id="insertUser" parameterType="SysUser" useGeneratedKeys="true" keyProperty="userId"> + insert into sys_user( + <if test="userId != null and userId != 0">user_id,</if> + <if test="deptId != null and deptId != 0">dept_id,</if> + <if test="userName != null and userName != ''">user_name,</if> + <if test="deptName != null and deptName != ''">deptName,</if> + <if test="nickName != null and nickName != ''">nick_name,</if> + <if test="email != null and email != ''">email,</if> + <if test="avatar != null and avatar != ''">avatar,</if> + <if test="phonenumber != null and phonenumber != ''">phonenumber,</if> + <if test="sex != null and sex != ''">sex,</if> + <if test="password != null and password != ''">password,</if> + <if test="status != null and status != ''">status,</if> + <if test="createBy != null and createBy != ''">create_by,</if> + <if test="remark != null and remark != ''">remark,</if> + <if test="ifBlack != null">ifBlack,</if> + <if test="districtId != null">districtId,</if> + create_time + )values( + <if test="userId != null and userId != ''">#{userId},</if> + <if test="deptId != null and deptId != ''">#{deptId},</if> + <if test="userName != null and userName != ''">#{userName},</if> + <if test="deptName != null and deptName != ''">#{deptName},</if> + + <if test="nickName != null and nickName != ''">#{nickName},</if> + <if test="email != null and email != ''">#{email},</if> + <if test="avatar != null and avatar != ''">#{avatar},</if> + <if test="phonenumber != null and phonenumber != ''">#{phonenumber},</if> + <if test="sex != null and sex != ''">#{sex},</if> + <if test="password != null and password != ''">#{password},</if> + <if test="status != null and status != ''">#{status},</if> + <if test="createBy != null and createBy != ''">#{createBy},</if> + <if test="remark != null and remark != ''">#{remark},</if> + <if test="ifBlack != null">#{ifBlack},</if> + <if test="districtId != null">#{districtId},</if> + sysdate() + ) + </insert> + + <update id="updateUser" parameterType="SysUser"> + update sys_user + <set> + <if test="deptId != null and deptId != 0">dept_id = #{deptId},</if> + <if test="userName != null and userName != ''">user_name = #{userName},</if> + <if test="nickName != null and nickName != ''">nick_name = #{nickName},</if> + <if test="deptName != null and deptName != ''">deptName = #{deptName},</if> + <if test="email != null ">email = #{email},</if> + <if test="phonenumber != null ">phonenumber = #{phonenumber},</if> + <if test="sex != null and sex != ''">sex = #{sex},</if> + <if test="avatar != null and avatar != ''">avatar = #{avatar},</if> + <if test="password != null and password != ''">password = #{password},</if> + <if test="status != null and status != ''">status = #{status},</if> + <if test="loginIp != null and loginIp != ''">login_ip = #{loginIp},</if> + <if test="loginDate != null">login_date = #{loginDate},</if> + <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if> + <if test="remark != null">remark = #{remark},</if> + <if test="ifBlack != null">ifBlack = #{ifBlack},</if> + <if test="districtId != null">districtId = #{districtId},</if> + <if test="disableRemark != null">disable_remark = #{disableRemark},</if> + <if test="operatingTime != null">operating_time = #{operatingTime},</if> + <if test="operatingPerson != null">operating_person = #{operatingPerson},</if> + update_time = sysdate() + </set> + where user_id = #{userId} + </update> + + <update id="updateUserStatus" parameterType="SysUser"> + update sys_user set status = #{status} where user_id = #{userId} + </update> + + <update id="updateUserAvatar" parameterType="SysUser"> + update sys_user set avatar = #{avatar} where user_name = #{userName} + </update> + + <update id="resetUserPwd" parameterType="SysUser"> + update sys_user set password = #{password} where user_name = #{userName} + </update> + <update id="updateUserIfBlack"> + update sys_user set ifBlack = 0,safetyPoints = 12,update_time = sysdate() + where user_id IN + <foreach collection="ids" separator="," item="userId" open="(" close=")"> + #{userId} + </foreach> + </update> + <update id="updatePassword"> + update sys_user set password = #{s} where user_id = #{id} + </update> + + <delete id="deleteUserById" parameterType="Long"> + update sys_user set del_flag = '2' where user_id = #{userId} + </delete> + + <delete id="deleteUserByIds" parameterType="Long"> + update sys_user set del_flag = '2' where user_id in + <foreach collection="ids" item="userId" open="(" separator="," close=")"> + #{userId} + </foreach> + </delete> + +</mapper> \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml new file mode 100644 index 0000000..2b90bc4 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.system.mapper.SysUserPostMapper"> + + <resultMap type="SysUserPost" id="SysUserPostResult"> + <result property="userId" column="user_id" /> + <result property="postId" column="post_id" /> + </resultMap> + + <delete id="deleteUserPostByUserId" parameterType="Long"> + delete from sys_user_post where user_id=#{userId} + </delete> + + <select id="countUserPostById" resultType="Integer"> + select count(1) from sys_user_post where post_id=#{postId} + </select> + + <delete id="deleteUserPost" parameterType="Long"> + delete from sys_user_post where user_id in + <foreach collection="array" item="userId" open="(" separator="," close=")"> + #{userId} + </foreach> + </delete> + + <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> \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml new file mode 100644 index 0000000..9f93e27 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper +PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" +"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.system.mapper.SysUserRoleMapper"> + + <resultMap type="SysUserRole" id="SysUserRoleResult"> + <result property="userId" column="user_id" /> + <result property="roleId" column="role_id" /> + </resultMap> + + <delete id="deleteUserRoleByUserId" parameterType="Long"> + delete from sys_user_role where user_id=#{userId} + </delete> + + <select id="countUserRoleByRoleId" resultType="Integer"> + select count(1) from sys_user_role where role_id=#{roleId} + </select> + + <delete id="deleteUserRole" parameterType="Long"> + delete from sys_user_role where user_id in + <foreach collection="ids" item="userId" open="(" separator="," close=")"> + #{userId} + </foreach> + </delete> + + <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> + <insert id="insertUserRole"> + insert into sys_user_role(user_id, role_id) values (#{userRole.userId},#{userRole.roleId}) + </insert> + <insert id="insertBatchUserDept"> + insert into t_dept_to_user(user_id, dept_id) values + <foreach item="item" index="index" collection="deptToUserList" separator=","> + (#{item.userId},#{item.deptId}) + </foreach> + </insert> + + <delete id="deleteUserRoleInfo" parameterType="SysUserRole"> + delete from sys_user_role where user_id=#{userId} and role_id=#{roleId} + </delete> + + <delete id="deleteUserRoleInfos"> + delete from sys_user_role where role_id=#{roleId} and user_id in + <foreach collection="userIds" item="userId" open="(" separator="," close=")"> + #{userId} + </foreach> + </delete> +</mapper> \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/TbAccountDetailMapper.xml b/ruoyi-system/src/main/resources/mapper/system/TbAccountDetailMapper.xml new file mode 100644 index 0000000..4e3e5b7 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/TbAccountDetailMapper.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.system.mapper.TbAccountDetailMapper"> + + <!-- 通用查询映射结果 --> + <resultMap id="BaseResultMap" type="com.stylefeng.guns.rest.model.TbAccountDetail"> + <id column="id" property="id" /> + <result column="user_id" property="userId" /> + <result column="type" property="type" /> + <result column="category" property="category" /> + <result column="status" property="status" /> + <result column="create_time" property="createTime" /> + </resultMap> + +</mapper> diff --git a/ruoyi-system/src/main/resources/mapper/system/TbAddressMapper.xml b/ruoyi-system/src/main/resources/mapper/system/TbAddressMapper.xml new file mode 100644 index 0000000..387d29d --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/TbAddressMapper.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.system.mapper.TbAddressMapper"> + + <!-- 通用查询映射结果 --> + <resultMap id="BaseResultMap" type="com.stylefeng.guns.rest.model.TbAddress"> + <id column="id" property="id" /> + <result column="user_id" property="userId" /> + <result column="city" property="city" /> + <result column="city_code" property="cityCode" /> + <result column="province" property="province" /> + <result column="province_code" property="provinceCode" /> + <result column="area" property="area" /> + <result column="area_code" property="areaCode" /> + <result column="username" property="username" /> + <result column="phone" property="phone" /> + <result column="detail" property="detail" /> + <result column="create_time" property="createTime" /> + <result column="is_delete" property="isDelete" /> + </resultMap> + +</mapper> diff --git a/ruoyi-system/src/main/resources/mapper/system/TbBankMapper.xml b/ruoyi-system/src/main/resources/mapper/system/TbBankMapper.xml new file mode 100644 index 0000000..b24bd4e --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/TbBankMapper.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.system.mapper.TbBankMapper"> + + <!-- 通用查询映射结果 --> + <resultMap id="BaseResultMap" type="com.stylefeng.guns.rest.model.TbBank"> + <id column="id" property="id" /> + <result column="user_id" property="userId" /> + <result column="card_no" property="cardNo" /> + <result column="username" property="username" /> + <result column="create_time" property="createTime" /> + <result column="is_delete" property="isDelete" /> + </resultMap> + +</mapper> diff --git a/ruoyi-system/src/main/resources/mapper/system/TbCompanyMapper.xml b/ruoyi-system/src/main/resources/mapper/system/TbCompanyMapper.xml new file mode 100644 index 0000000..a1dc6b4 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/TbCompanyMapper.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.system.mapper.TbCompanyMapper"> + + <!-- 通用查询映射结果 --> + <resultMap id="BaseResultMap" type="com.ruoyi.system.model.TbCompany"> + <id column="id" property="id" /> + <result column="user_id" property="userId" /> + <result column="company_name" property="companyName" /> + <result column="city" property="city" /> + <result column="city_code" property="cityCode" /> + <result column="province" property="province" /> + <result column="province_code" property="provinceCode" /> + <result column="area" property="area" /> + <result column="area_code" property="areaCode" /> + <result column="establish_time" property="establishTime" /> + <result column="company_category" property="companyCategory" /> + <result column="company_industry_id" property="companyIndustryId" /> + <result column="taxpayer_type" property="taxpayerType" /> + <result column="tax_credit" property="taxCredit" /> + <result column="estimated_days" property="estimatedDays" /> + <result column="official_seal_num" property="officialSealNum" /> + <result column="paid_in_funds" property="paidInFunds" /> + <result column="link" property="link" /> + <result column="invoice_limit" property="invoiceLimit" /> + <result column="registered_capital" property="registeredCapital" /> + <result column="high_tech_enterprise_technology" property="highTechEnterpriseTechnology" /> + <result column="social_security" property="socialSecurity" /> + <result column="tendering" property="tendering" /> + <result column="have_trademark" property="haveTrademark" /> + <result column="trademark_num" property="trademarkNum" /> + <result column="have_patent" property="havePatent" /> + <result column="patent_num" property="patentNum" /> + <result column="have_soft_works" property="haveSoftWorks" /> + <result column="soft_works_num" property="softWorksNum" /> + <result column="rename_money" property="renameMoney" /> + <result column="rename_day" property="renameDay" /> + <result column="relocation_area_money" property="relocationAreaMoney" /> + <result column="relocation_area_day" property="relocationAreaDay" /> + <result column="sale_money" property="saleMoney" /> + <result column="phone" property="phone" /> + <result column="recipient" property="recipient" /> + <result column="recipient_address" property="recipientAddress" /> + <result column="information" property="information" /> + <result column="remark" property="remark" /> + <result column="create_time" property="createTime" /> + </resultMap> + +</mapper> diff --git a/ruoyi-system/src/main/resources/mapper/system/TbMessageMapper.xml b/ruoyi-system/src/main/resources/mapper/system/TbMessageMapper.xml new file mode 100644 index 0000000..c6c6f7c --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/TbMessageMapper.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.system.mapper.TbMessageMapper"> + + <!-- 通用查询映射结果 --> + <resultMap id="BaseResultMap" type="com.stylefeng.guns.rest.model.TbMessage"> + <id column="id" property="id" /> + <result column="user_id" property="userId" /> + <result column="message" property="message" /> + <result column="is_read" property="isRead" /> + <result column="create_time" property="createTime" /> + </resultMap> + +</mapper> diff --git a/ruoyi-system/src/main/resources/mapper/system/TbOpeningBankMapper.xml b/ruoyi-system/src/main/resources/mapper/system/TbOpeningBankMapper.xml new file mode 100644 index 0000000..10d9124 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/TbOpeningBankMapper.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.system.mapper.TbOpeningBankMapper"> + + <!-- 通用查询映射结果 --> + <resultMap id="BaseResultMap" type="com.ruoyi.system.model.TbOpeningBank"> + <id column="id" property="id" /> + <result column="company_id" property="companyId" /> + <result column="name" property="name" /> + <result column="category" property="category" /> + <result column="have_foreign_currency" property="haveForeignCurrency" /> + <result column="create_time" property="createTime" /> + </resultMap> + +</mapper> diff --git a/ruoyi-system/src/main/resources/mapper/system/TbPermitMapper.xml b/ruoyi-system/src/main/resources/mapper/system/TbPermitMapper.xml new file mode 100644 index 0000000..50bab29 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/TbPermitMapper.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.system.mapper.TbPermitMapper"> + + <!-- 通用查询映射结果 --> + <resultMap id="BaseResultMap" type="com.ruoyi.system.model.TbPermit"> + <id column="id" property="id" /> + <result column="company_id" property="companyId" /> + <result column="name" property="name" /> + <result column="level" property="level" /> + <result column="expire_ time" property="expire time" /> + <result column="create_time" property="createTime" /> + </resultMap> + +</mapper> diff --git a/ruoyi-system/src/main/resources/mapper/system/TbRegionMapper.xml b/ruoyi-system/src/main/resources/mapper/system/TbRegionMapper.xml new file mode 100644 index 0000000..1884681 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/TbRegionMapper.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.system.mapper.TbRegionMapper"> + + <!-- 通用查询映射结果 --> + <resultMap id="BaseResultMap" type="com.ruoyi.system.model.TbRegion"> + <id column="id" property="id" /> + <result column="name" property="name" /> + <result column="code" property="code" /> + <result column="citycode" property="citycode" /> + <result column="parent_id" property="parentId" /> + <result column="english" property="english" /> + </resultMap> + +</mapper> diff --git a/ruoyi-system/src/main/resources/mapper/system/TbUserMapper.xml b/ruoyi-system/src/main/resources/mapper/system/TbUserMapper.xml new file mode 100644 index 0000000..3447b43 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/TbUserMapper.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.system.mapper.TbUserMapper"> + + <!-- 通用查询映射结果 --> + +</mapper> diff --git a/ruoyi-system/src/main/resources/mapper/system/TbWithdrawalMapper.xml b/ruoyi-system/src/main/resources/mapper/system/TbWithdrawalMapper.xml new file mode 100644 index 0000000..afb3822 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/TbWithdrawalMapper.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.ruoyi.system.mapper.TbWithdrawalMapper"> + + <!-- 通用查询映射结果 --> + <resultMap id="BaseResultMap" type="com.ruoyi.system.model.TbWithdrawal"> + <id column="id" property="id" /> + <result column="user_id" property="userId" /> + <result column="money" property="money" /> + <result column="status" property="status" /> + <result column="remark" property="remark" /> + <result column="img" property="img" /> + <result column="create_time" property="createTime" /> + </resultMap> + +</mapper> -- Gitblit v1.7.1