From 3c5cb1715b44c15d4fa97f25352b4a9724a8749c Mon Sep 17 00:00:00 2001 From: 86191 <2160251938@qq.com> Date: Sun, 29 Sep 2024 09:18:17 +0800 Subject: [PATCH] =?UTF-8?q?feat():saas=E7=B3=BB=E7=BB=9F=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 49 + LICENSE | 21 + README.md | 26 + cloud-auth/pom.xml | 80 ++ .../com/muyu/auth/CloudAuthApplication.java | 19 + .../muyu/auth/controller/TokenController.java | 72 + .../java/com/muyu/auth/form/LoginBody.java | 35 + .../java/com/muyu/auth/form/RegisterBody.java | 10 + .../muyu/auth/service/SysLoginService.java | 127 ++ .../muyu/auth/service/SysPasswordService.java | 76 + .../auth/service/SysRecordLogService.java | 44 + cloud-auth/src/main/resources/banner.txt | 2 + cloud-auth/src/main/resources/logback/dev.xml | 74 + .../src/main/resources/logback/prod.xml | 89 ++ .../src/main/resources/logback/test.xml | 89 ++ cloud-common/cloud-common-api-doc/pom.xml | 33 + .../api/doc/config/SpringDocConfig.java | 34 + ...ot.autoconfigure.AutoConfiguration.imports | 1 + cloud-common/cloud-common-core/pom.xml | 169 +++ .../muyu/common/core/annotation/Excel.java | 176 +++ .../muyu/common/core/annotation/Excels.java | 17 + .../common/core/constant/CacheConstants.java | 58 + .../muyu/common/core/constant/Constants.java | 134 ++ .../common/core/constant/GenConstants.java | 186 +++ .../muyu/common/core/constant/HttpStatus.java | 93 ++ .../core/constant/ScheduleConstants.java | 56 + .../core/constant/SecurityConstants.java | 53 + .../core/constant/ServiceNameConstants.java | 33 + .../common/core/constant/TokenConstants.java | 24 + .../common/core/constant/UserConstants.java | 113 ++ .../core/context/SecurityContextHolder.java | 83 ++ .../com/muyu/common/core/domain/Result.java | 112 ++ .../muyu/common/core/enums/UserStatus.java | 26 + .../core/exception/CaptchaException.java | 14 + .../core/exception/CheckedException.java | 26 + .../core/exception/DemoModeException.java | 13 + .../core/exception/GlobalException.java | 51 + .../core/exception/InnerAuthException.java | 14 + .../core/exception/PreAuthorizeException.java | 13 + .../core/exception/ServiceException.java | 65 + .../common/core/exception/UtilException.java | 22 + .../exception/auth/NotLoginException.java | 14 + .../auth/NotPermissionException.java | 20 + .../core/exception/auth/NotRoleException.java | 20 + .../core/exception/base/BaseException.java | 69 + .../core/exception/file/FileException.java | 17 + .../FileNameLengthLimitExceededException.java | 14 + .../file/FileSizeLimitExceededException.java | 14 + .../exception/file/FileUploadException.java | 52 + .../file/InvalidExtensionException.java | 67 + .../core/exception/job/TaskException.java | 29 + .../user/CaptchaExpireException.java | 14 + .../core/exception/user/UserException.java | 16 + .../user/UserPasswordNotMatchException.java | 14 + .../muyu/common/core/feign/FeginConfig.java | 19 + .../com/muyu/common/core/text/CharsetKit.java | 94 ++ .../com/muyu/common/core/text/Convert.java | 903 ++++++++++++ .../muyu/common/core/text/StrFormatter.java | 77 ++ .../com/muyu/common/core/utils/DateUtils.java | 158 +++ .../muyu/common/core/utils/ExceptionUtil.java | 35 + .../com/muyu/common/core/utils/JwtUtils.java | 176 +++ .../com/muyu/common/core/utils/PageUtils.java | 32 + .../muyu/common/core/utils/ServletUtils.java | 285 ++++ .../muyu/common/core/utils/SpringUtils.java | 114 ++ .../muyu/common/core/utils/StringUtils.java | 504 +++++++ .../common/core/utils/bean/BeanUtils.java | 107 ++ .../core/utils/bean/BeanValidators.java | 22 + .../common/core/utils/file/FileTypeUtils.java | 85 ++ .../common/core/utils/file/FileUtils.java | 222 +++ .../common/core/utils/file/ImageUtils.java | 69 + .../common/core/utils/file/MimeTypeUtils.java | 56 + .../common/core/utils/html/EscapeUtil.java | 145 ++ .../common/core/utils/html/HTMLFilter.java | 498 +++++++ .../muyu/common/core/utils/ip/IpUtils.java | 331 +++++ .../core/utils/poi/ExcelHandlerAdapter.java | 23 + .../muyu/common/core/utils/poi/ExcelUtil.java | 1230 +++++++++++++++++ .../core/utils/reflect/ReflectUtils.java | 324 +++++ .../muyu/common/core/utils/sign/Base64.java | 256 ++++ .../muyu/common/core/utils/sql/SqlUtil.java | 59 + .../muyu/common/core/utils/uuid/IdUtils.java | 44 + .../com/muyu/common/core/utils/uuid/Seq.java | 78 ++ .../com/muyu/common/core/utils/uuid/UUID.java | 450 ++++++ .../core/validation/ValidationConfig.java | 14 + .../core/web/controller/BaseController.java | 129 ++ .../common/core/web/domain/BaseEntity.java | 80 ++ .../common/core/web/domain/TreeEntity.java | 47 + .../muyu/common/core/web/page/PageDomain.java | 93 ++ .../common/core/web/page/TableDataInfo.java | 45 + .../common/core/web/page/TableSupport.java | 53 + .../java/com/muyu/common/core/xss/Xss.java | 27 + .../muyu/common/core/xss/XssValidator.java | 31 + ...ot.autoconfigure.AutoConfiguration.imports | 2 + cloud-common/cloud-common-datascope/pom.xml | 27 + .../datascope/annotation/DataScope.java | 28 + .../datascope/aspect/DataScopeAspect.java | 147 ++ ...ot.autoconfigure.AutoConfiguration.imports | 1 + cloud-common/cloud-common-datasource/pom.xml | 35 + .../common/datasource/annotation/Master.java | 18 + .../common/datasource/annotation/Slave.java | 18 + cloud-common/cloud-common-log/pom.xml | 27 + .../com/muyu/common/log/annotation/Log.java | 46 + .../com/muyu/common/log/aspect/LogAspect.java | 220 +++ .../muyu/common/log/enums/BusinessStatus.java | 18 + .../muyu/common/log/enums/BusinessType.java | 58 + .../muyu/common/log/enums/OperatorType.java | 23 + .../log/filter/PropertyPreExcludeFilter.java | 20 + .../common/log/service/AsyncLogService.java | 27 + ...ot.autoconfigure.AutoConfiguration.imports | 2 + cloud-common/cloud-common-rabbit/pom.xml | 35 + .../rabbit/RabbitListenerConfigurer.java | 41 + ...ot.autoconfigure.AutoConfiguration.imports | 1 + cloud-common/cloud-common-redis/pom.xml | 33 + .../FastJson2JsonRedisSerializer.java | 49 + .../common/redis/configure/RedisConfig.java | 41 + .../common/redis/service/RedisService.java | 258 ++++ ...ot.autoconfigure.AutoConfiguration.imports | 2 + cloud-common/cloud-common-saas/pom.xml | 38 + .../many/datasource/ManyDataSource.java | 110 ++ .../constents/DatasourceContent.java | 19 + .../domain/model/DataSourceInfo.java | 50 + .../factory/DruidDataSourceFactory.java | 40 + .../holder/DynamicDataSourceHolder.java | 42 + .../datasource/role/DynamicDataSource.java | 55 + .../common/saas/contents/SaaSConstant.java | 12 + .../cloud/common/saas/domain/Datasource.java | 26 + .../common/saas/domain/model/EntInfo.java | 25 + .../common/saas/exception/SaaSException.java | 28 + .../saas/interceptor/SaaSInterceptor.java | 53 + .../saas/interceptor/WebMvcSaaSConfig.java | 31 + ...ot.autoconfigure.AutoConfiguration.imports | 3 + cloud-common/cloud-common-seata/pom.xml | 27 + cloud-common/cloud-common-security/pom.xml | 39 + .../annotation/EnableCustomConfig.java | 26 + .../annotation/EnableMyFeignClients.java | 27 + .../common/security/annotation/InnerAuth.java | 18 + .../common/security/annotation/Logical.java | 18 + .../security/annotation/RequiresLogin.java | 16 + .../annotation/RequiresPermissions.java | 25 + .../security/annotation/RequiresRoles.java | 25 + .../security/aspect/InnerAuthAspect.java | 46 + .../security/aspect/PreAuthorizeAspect.java | 89 ++ .../muyu/common/security/auth/AuthLogic.java | 327 +++++ .../muyu/common/security/auth/AuthUtil.java | 154 +++ .../security/config/ApplicationConfig.java | 21 + .../common/security/config/WebMvcConfig.java | 32 + .../feign/FeignAutoConfiguration.java | 18 + .../feign/FeignRequestInterceptor.java | 48 + .../handler/GlobalExceptionHandler.java | 147 ++ .../interceptor/HeaderInterceptor.java | 50 + .../common/security/service/TokenService.java | 153 ++ .../muyu/common/security/utils/DictUtils.java | 71 + .../common/security/utils/SecurityUtils.java | 109 ++ ...ot.autoconfigure.AutoConfiguration.imports | 5 + cloud-common/cloud-common-system/pom.xml | 32 + .../muyu/common/system/domain/Datasource.java | 26 + .../muyu/common/system/domain/LoginUser.java | 66 + .../muyu/common/system/domain/SysDept.java | 108 ++ .../common/system/domain/SysDictData.java | 104 ++ .../common/system/domain/SysDictType.java | 62 + .../muyu/common/system/domain/SysFile.java | 27 + .../common/system/domain/SysFirmUser.java | 25 + .../common/system/domain/SysLogininfor.java | 64 + .../muyu/common/system/domain/SysOperLog.java | 129 ++ .../muyu/common/system/domain/SysRole.java | 127 ++ .../muyu/common/system/domain/SysUser.java | 178 +++ .../system/remote/RemoteFileService.java | 29 + .../system/remote/RemoteLogService.java | 42 + .../system/remote/RemoteSaaSService.java | 29 + .../system/remote/RemoteUserService.java | 47 + .../factory/RemoteFileFallbackFactory.java | 31 + .../factory/RemoteLogFallbackFactory.java | 37 + .../factory/RemoteSaaSFallbackFactory.java | 33 + .../factory/RemoteUserFallbackFactory.java | 46 + ...ot.autoconfigure.AutoConfiguration.imports | 4 + cloud-common/cloud-common-xxl/pom.xml | 37 + .../com/muyu/common/xxl/XXLJobConfig.java | 28 + .../com/muyu/common/xxl/XxlJobProperties.java | 63 + .../common/xxl/demo/XxlJobDemoService.java | 38 + ...ot.autoconfigure.AutoConfiguration.imports | 3 + cloud-common/pom.xml | 33 + cloud-gateway/pom.xml | 103 ++ .../muyu/gateway/CloudGatewayApplication.java | 17 + .../muyu/gateway/config/CaptchaConfig.java | 82 ++ .../muyu/gateway/config/GatewayConfig.java | 21 + .../gateway/config/KaptchaTextCreator.java | 61 + .../config/RouterFunctionConfiguration.java | 29 + .../config/properties/CaptchaProperties.java | 41 + .../properties/IgnoreWhiteProperties.java | 31 + .../config/properties/XssProperties.java | 44 + .../com/muyu/gateway/filter/AuthFilter.java | 122 ++ .../gateway/filter/BlackListUrlFilter.java | 58 + .../gateway/filter/CacheRequestFilter.java | 75 + .../gateway/filter/ValidateCodeFilter.java | 69 + .../com/muyu/gateway/filter/XssFilter.java | 114 ++ .../handler/GatewayExceptionHandler.java | 47 + .../handler/SentinelFallbackHandler.java | 35 + .../gateway/handler/ValidateCodeHandler.java | 37 + .../gateway/model/resp/CaptchaCodeResp.java | 25 + .../gateway/service/ValidateCodeService.java | 23 + .../service/impl/ValidateCodeServiceImpl.java | 109 ++ .../muyu/gateway/utils/WebFrameworkUtils.java | 105 ++ cloud-gateway/src/main/resources/banner.txt | 2 + .../src/main/resources/logback/dev.xml | 74 + .../src/main/resources/logback/prod.xml | 88 ++ .../src/main/resources/logback/test.xml | 88 ++ cloud-modules/cloud-modules-file/pom.xml | 82 ++ .../com/muyu/file/CloudFileApplication.java | 17 + .../com/muyu/file/config/MinioConfig.java | 72 + .../com/muyu/file/config/ResourcesConfig.java | 48 + .../file/controller/SysFileController.java | 44 + .../muyu/file/service/ISysFileService.java | 21 + .../file/service/LocalSysFileServiceImpl.java | 50 + .../file/service/MinioSysFileServiceImpl.java | 50 + .../com/muyu/file/utils/FileUploadUtils.java | 163 +++ .../src/main/resources/banner.txt | 2 + .../src/main/resources/logback/dev.xml | 74 + .../src/main/resources/logback/prod.xml | 88 ++ .../src/main/resources/logback/test.xml | 88 ++ cloud-modules/cloud-modules-gen/pom.xml | 93 ++ .../com/muyu/gen/CloudGenApplication.java | 21 + .../java/com/muyu/gen/config/GenConfig.java | 65 + .../muyu/gen/controller/GenController.java | 236 ++++ .../java/com/muyu/gen/domain/GenTable.java | 378 +++++ .../com/muyu/gen/domain/GenTableColumn.java | 358 +++++ .../com/muyu/gen/domain/GenTableResp.java | 43 + .../muyu/gen/mapper/GenTableColumnMapper.java | 64 + .../com/muyu/gen/mapper/GenTableMapper.java | 91 ++ .../service/GenTableColumnServiceImpl.java | 74 + .../muyu/gen/service/GenTableServiceImpl.java | 537 +++++++ .../gen/service/IGenTableColumnService.java | 47 + .../muyu/gen/service/IGenTableService.java | 129 ++ .../main/java/com/muyu/gen/util/GenUtils.java | 229 +++ .../muyu/gen/util/VelocityInitializer.java | 30 + .../java/com/muyu/gen/util/VelocityUtils.java | 357 +++++ .../src/main/resources/banner.txt | 2 + .../src/main/resources/logback/dev.xml | 74 + .../src/main/resources/logback/prod.xml | 88 ++ .../src/main/resources/logback/test.xml | 88 ++ .../mapper/generator/GenTableColumnMapper.xml | 132 ++ .../mapper/generator/GenTableMapper.xml | 234 ++++ .../main/resources/vm/java/controller.java.vm | 123 ++ .../src/main/resources/vm/java/domain.java.vm | 103 ++ .../src/main/resources/vm/java/mapper.java.vm | 20 + .../main/resources/vm/java/service.java.vm | 37 + .../resources/vm/java/serviceImpl.java.vm | 115 ++ .../main/resources/vm/java/sub-domain.java.vm | 78 ++ .../src/main/resources/vm/js/api.js.vm | 44 + .../src/main/resources/vm/sql/sql.vm | 22 + .../main/resources/vm/vue/index-tree.vue.vm | 505 +++++++ .../src/main/resources/vm/vue/index.vue.vm | 602 ++++++++ .../resources/vm/vue/v3/index-tree.vue.vm | 474 +++++++ .../src/main/resources/vm/vue/v3/index.vue.vm | 590 ++++++++ .../src/main/resources/vm/vue/v3/readme.txt | 1 + .../src/main/resources/vm/xml/mapper.xml.vm | 135 ++ cloud-modules/cloud-modules-system/pom.xml | 100 ++ .../muyu/system/CloudSystemApplication.java | 23 + .../controller/SysConfigController.java | 122 ++ .../system/controller/SysDeptController.java | 113 ++ .../controller/SysDictDataController.java | 107 ++ .../controller/SysDictTypeController.java | 115 ++ .../controller/SysLogininforController.java | 80 ++ .../system/controller/SysMenuController.java | 137 ++ .../controller/SysNoticeController.java | 80 ++ .../controller/SysOperlogController.java | 67 + .../system/controller/SysPostController.java | 110 ++ .../controller/SysProfileController.java | 134 ++ .../system/controller/SysRoleController.java | 216 +++ .../system/controller/SysUserController.java | 299 ++++ .../controller/SysUserOnlineController.java | 69 + .../com/muyu/system/domain/SysConfig.java | 81 ++ .../java/com/muyu/system/domain/SysMenu.java | 138 ++ .../com/muyu/system/domain/SysNotice.java | 58 + .../java/com/muyu/system/domain/SysPost.java | 78 ++ .../com/muyu/system/domain/SysRoleDept.java | 27 + .../com/muyu/system/domain/SysRoleMenu.java | 28 + .../com/muyu/system/domain/SysUserOnline.java | 52 + .../com/muyu/system/domain/SysUserPost.java | 28 + .../com/muyu/system/domain/SysUserRole.java | 27 + .../muyu/system/domain/resp/AuthRoleResp.java | 33 + .../muyu/system/domain/resp/DeptTreeResp.java | 32 + .../muyu/system/domain/resp/ProfileResp.java | 34 + .../system/domain/resp/RoleMenuTreeResp.java | 31 + .../domain/resp/UserDetailInfoResp.java | 47 + .../muyu/system/domain/resp/UserInfoResp.java | 36 + .../com/muyu/system/domain/vo/MetaVo.java | 54 + .../com/muyu/system/domain/vo/RouterVo.java | 62 + .../com/muyu/system/domain/vo/TreeSelect.java | 56 + .../muyu/system/mapper/SysConfigMapper.java | 12 + .../com/muyu/system/mapper/SysDeptMapper.java | 131 ++ .../muyu/system/mapper/SysDictDataMapper.java | 106 ++ .../muyu/system/mapper/SysDictTypeMapper.java | 92 ++ .../system/mapper/SysLogininforMapper.java | 45 + .../com/muyu/system/mapper/SysMenuMapper.java | 138 ++ .../muyu/system/mapper/SysNoticeMapper.java | 67 + .../muyu/system/mapper/SysOperLogMapper.java | 52 + .../com/muyu/system/mapper/SysPostMapper.java | 110 ++ .../muyu/system/mapper/SysRoleDeptMapper.java | 50 + .../com/muyu/system/mapper/SysRoleMapper.java | 119 ++ .../muyu/system/mapper/SysRoleMenuMapper.java | 50 + .../com/muyu/system/mapper/SysUserMapper.java | 145 ++ .../muyu/system/mapper/SysUserPostMapper.java | 49 + .../muyu/system/mapper/SysUserRoleMapper.java | 69 + .../com/muyu/system/rabbit/RabbitTest.java | 53 + .../muyu/system/service/SysConfigService.java | 44 + .../muyu/system/service/SysDeptService.java | 138 ++ .../system/service/SysDictDataService.java | 66 + .../system/service/SysDictTypeService.java | 106 ++ .../system/service/SysLogininforService.java | 43 + .../muyu/system/service/SysMenuService.java | 161 +++ .../muyu/system/service/SysNoticeService.java | 67 + .../system/service/SysOperLogService.java | 54 + .../system/service/SysPermissionService.java | 30 + .../muyu/system/service/SysPostService.java | 110 ++ .../muyu/system/service/SysRoleService.java | 191 +++ .../system/service/SysUserOnlineService.java | 52 + .../muyu/system/service/SysUserService.java | 231 ++++ .../service/impl/SysConfigServiceImpl.java | 114 ++ .../service/impl/SysDeptServiceImpl.java | 318 +++++ .../service/impl/SysDictDataServiceImpl.java | 108 ++ .../service/impl/SysDictTypeServiceImpl.java | 210 +++ .../impl/SysLogininforServiceImpl.java | 64 + .../service/impl/SysMenuServiceImpl.java | 501 +++++++ .../service/impl/SysNoticeServiceImpl.java | 93 ++ .../service/impl/SysOperLogServiceImpl.java | 77 ++ .../impl/SysPermissionServiceImpl.java | 77 ++ .../service/impl/SysPostServiceImpl.java | 174 +++ .../service/impl/SysRoleServiceImpl.java | 399 ++++++ .../impl/SysUserOnlineServiceImpl.java | 85 ++ .../service/impl/SysUserServiceImpl.java | 509 +++++++ .../src/main/resources/banner.txt | 2 + .../src/main/resources/logback/dev.xml | 74 + .../src/main/resources/logback/prod.xml | 81 ++ .../src/main/resources/logback/test.xml | 81 ++ .../mapper/system/SysConfigMapper.xml | 128 ++ .../resources/mapper/system/SysDeptMapper.xml | 183 +++ .../mapper/system/SysDictDataMapper.xml | 143 ++ .../mapper/system/SysDictTypeMapper.xml | 107 ++ .../mapper/system/SysLogininforMapper.xml | 54 + .../resources/mapper/system/SysMenuMapper.xml | 258 ++++ .../mapper/system/SysNoticeMapper.xml | 100 ++ .../mapper/system/SysOperLogMapper.xml | 103 ++ .../resources/mapper/system/SysPostMapper.xml | 131 ++ .../mapper/system/SysRoleDeptMapper.xml | 38 + .../resources/mapper/system/SysRoleMapper.xml | 163 +++ .../mapper/system/SysRoleMenuMapper.xml | 38 + .../resources/mapper/system/SysUserMapper.xml | 277 ++++ .../mapper/system/SysUserPostMapper.xml | 38 + .../mapper/system/SysUserRoleMapper.xml | 51 + .../cloud-modules-vehiclegateway/pom.xml | 120 ++ .../com/muyu/VehicleGatewayApplication.java | 13 + .../com/muyu/vehicle/VehicleInstance.java | 132 ++ .../java/com/muyu/vehicle/VehicleThread.java | 24 + .../com/muyu/vehicle/api/ClientAdmin.java | 12 + .../com/muyu/vehicle/core/LocalContainer.java | 24 + .../vehicle/core/VehicleConfiguration.java | 13 + .../muyu/web/common/ScheduledThreadPool.java | 33 + .../muyu/web/controller/testController.java | 40 + .../com/muyu/web/domain/MqttProperties.java | 42 + .../com/muyu/web/domain/ServerConfig.java | 76 + .../java/com/muyu/web/domain/VehicleInfo.java | 92 ++ .../web/domain/model/MqttServerModel.java | 37 + .../muyu/web/domain/model/PositionModel.java | 33 + .../web/domain/model/ServerConfigModel.java | 53 + .../web/domain/req/VehicleConnectionReq.java | 34 + .../web/service/VehicleInstanceService.java | 12 + .../impl/VehicleInstanceServiceImpl.java | 25 + .../main/java/com/muyu/web/utils/MD5Util.java | 52 + .../src/main/resources/banner.txt | 2 + .../src/main/resources/logback/dev.xml | 74 + .../src/main/resources/logback/prod.xml | 81 ++ .../src/main/resources/logback/test.xml | 81 ++ cloud-modules/cloud-modules-wechat/pom.xml | 59 + .../muyu/wechat/CloudWeChatApplication.java | 18 + .../wechat/controller/WxTestController.java | 155 +++ .../java/com/muyu/wechat/domain/Message.java | 29 + .../java/com/muyu/wechat/message/Article.java | 28 + .../com/muyu/wechat/message/NewMessage.java | 34 + .../com/muyu/wechat/util/OkHttpUtils.java | 41 + .../java/com/muyu/wechat/util/TokenUtil.java | 40 + cloud-modules/pom.xml | 35 + cloud-modules/saas/pom.xml | 25 + cloud-modules/saas/saas-common/pom.xml | 37 + .../java/com/muyu/common/domain/CarType.java | 18 + .../java/com/muyu/common/domain/DataType.java | 37 + .../com/muyu/common/domain/Enterprise.java | 33 + .../muyu/common/domain/MessageTemplate.java | 136 ++ .../common/domain/MessageTemplateType.java | 73 + .../java/com/muyu/common/domain/SysCar.java | 30 + .../muyu/common/domain/SysCarEnterprise.java | 59 + .../com/muyu/common/domain/SysCarFault.java | 116 ++ .../muyu/common/domain/SysCarFaultLog.java | 58 + .../common/domain/SysCarFaultMessage.java | 42 + .../com/muyu/common/domain/SysCarLog.java | 25 + .../java/com/muyu/common/domain/Template.java | 50 + .../java/com/muyu/common/domain/WarnLogs.java | 72 + .../java/com/muyu/common/domain/WarnRule.java | 62 + .../com/muyu/common/domain/WarnStrategy.java | 44 + .../domain/database/ElectronicFence.java | 123 ++ .../domain/database/ElectronicFenceGroup.java | 114 ++ .../common/domain/database/FenceGroupMid.java | 32 + .../domain/req/ElectroicFenceAddReq.java | 74 + .../domain/req/ElectroicFenceListReq.java | 65 + .../domain/req/ElectroicFenceUpdReq.java | 70 + .../req/ElectronicFenceGroupAddReq.java | 60 + .../req/ElectronicFenceGroupListReq.java | 56 + .../req/ElectronicFenceGroupUpdReq.java | 66 + .../domain/req/FenceAndGroupBoundReq.java | 35 + .../muyu/common/domain/req/FenceWayReq.java | 20 + .../com/muyu/common/domain/req/SysCarReq.java | 20 + .../common/domain/req/WarnStrategyReq.java | 36 + .../muyu/common/domain/resp/CarTypeResp.java | 19 + .../domain/resp/ElectronicFenceGroupResp.java | 60 + .../domain/resp/ElectronicFenceResp.java | 68 + .../domain/resp/GroupFenceListresp.java | 52 + .../common/domain/resp/SysCarFaultLogVo.java | 24 + .../com/muyu/common/domain/resp/SysCarVo.java | 26 + .../muyu/common/domain/resp/WarnLogsResp.java | 17 + .../muyu/common/domain/resp/WarnRuleResp.java | 19 + .../common/domain/resp/WarnStrategyResp.java | 24 + .../domain/utils/ElectricFenceModel.java | 75 + .../domain/utils/ElectricFenceResultTmp.java | 53 + .../domain/utils/ElectronicFenceResult.java | 55 + .../domain/utils/ElectronicFenceSetting.java | 63 + .../java/com/muyu/common/util/PageUtils.java | 50 + cloud-modules/saas/saas-server/pom.xml | 117 ++ .../java/com/muyu/server/SaasApplication.java | 19 + .../server/controller/CarTypeController.java | 33 + .../server/controller/DataTypeController.java | 47 + .../controller/ElectronicFenceController.java | 138 ++ .../ElectronicFenceGroupController.java | 95 ++ .../controller/EnterpriseController.java | 113 ++ .../MessageTemplateTypeController.java | 102 ++ .../server/controller/SysCarController.java | 58 + .../controller/SysCarFaultController.java | 137 ++ .../controller/SysCarFaultLogController.java | 91 ++ .../SysCarFaultMessageController.java | 74 + .../controller/SysCarLogController.java | 31 + .../server/controller/TemplateController.java | 63 + .../controller/TemplateNeedController.java | 23 + .../server/controller/TemplateService.java | 41 + .../server/controller/WarnLogsController.java | 56 + .../server/controller/WarnRuleController.java | 90 ++ .../controller/WarnStrategyController.java | 89 ++ .../form/DeleteEnterpriseByIds.java | 17 + .../controller/form/InsertEnterprise.java | 24 + .../controller/form/SearchEnterpriseName.java | 28 + .../controller/form/UpdateEnterprise.java | 30 + .../com/muyu/server/mapper/CarTypeMapper.java | 12 + .../muyu/server/mapper/DataTypeMapper.java | 17 + .../mapper/ElectronicFenceGroupMapper.java | 16 + .../server/mapper/ElectronicFenceMapper.java | 17 + .../com/muyu/server/mapper/EnterpriseDao.java | 30 + .../server/mapper/FenceGroupMidMapper.java | 16 + .../mapper/MessageTemplateTypeMapper.java | 19 + .../server/mapper/SysCarFaultLogMapper.java | 34 + .../muyu/server/mapper/SysCarFaultMapper.java | 34 + .../mapper/SysCarFaultMessageMapper.java | 29 + .../muyu/server/mapper/SysCarLogMapper.java | 9 + .../com/muyu/server/mapper/SysCarMapper.java | 27 + .../muyu/server/mapper/TemplateMapper.java | 23 + .../server/mapper/TemplateNeedMapper.java | 11 + .../muyu/server/mapper/WarnLogsMapper.java | 24 + .../muyu/server/mapper/WarnRuleMapper.java | 29 + .../server/mapper/WarnStrategyMapper.java | 29 + .../muyu/server/service/CarTypeService.java | 14 + .../muyu/server/service/DataTypeService.java | 15 + .../service/ElectronicFenceGroupService.java | 25 + .../service/ElectronicFenceService.java | 78 ++ .../server/service/EnterpriseService.java | 26 + .../server/service/FenceGroupMidService.java | 25 + .../service/MessageTemplateTypeService.java | 30 + .../server/service/SysCarFaultLogService.java | 25 + .../service/SysCarFaultMessageService.java | 31 + .../server/service/SysCarFaultService.java | 43 + .../muyu/server/service/SysCarLogService.java | 15 + .../muyu/server/service/SysCarService.java | 23 + .../server/service/TemplateNeedService.java | 9 + .../muyu/server/service/TemplateService.java | 25 + .../muyu/server/service/WarnLogsService.java | 35 + .../muyu/server/service/WarnRuleService.java | 45 + .../server/service/WarnStrategyService.java | 56 + .../service/impl/CarTypeServiceImpl.java | 28 + .../service/impl/DataTypeServiceImpl.java | 24 + .../impl/ElectronicFenceGroupServiceImpl.java | 85 ++ .../impl/ElectronicFenceServiceImpl.java | 147 ++ .../service/impl/EnterpriseServiceImpl.java | 96 ++ .../impl/FenceGroupMidServiceImpl.java | 56 + .../impl/MessageTemplateTypeServiceImpl.java | 74 + .../impl/SysCarFaultLogServiceImpl.java | 62 + .../impl/SysCarFaultMessageServiceImpl.java | 51 + .../service/impl/SysCarFaultServiceImpl.java | 90 ++ .../service/impl/SysCarLogServiceImpl.java | 27 + .../service/impl/SysCarServiceImpl.java | 48 + .../service/impl/TemplateNeedServiceImpl.java | 22 + .../service/impl/TemplateServiceImpl.java | 87 ++ .../service/impl/WarnLogsServiceImpl.java | 55 + .../service/impl/WarnRuleServiceImpl.java | 69 + .../service/impl/WarnStrategyServiceImpl.java | 87 ++ .../saas-server/src/main/resources/banner.txt | 2 + .../src/main/resources/logback/dev.xml | 74 + .../src/main/resources/logback/prod.xml | 81 ++ .../src/main/resources/logback/test.xml | 81 ++ .../main/resources/mapper/CarTypeMapper.xml | 17 + .../resources/mapper/EnterpriseMapper.xml | 67 + .../main/resources/mapper/SysCarMapper.xml | 77 ++ .../resources/mapper/TemplateNeedMapper.xml | 10 + .../main/resources/mapper/WarnLogsMapper.xml | 31 + .../main/resources/mapper/WarnRuleMapper.xml | 23 + .../resources/mapper/WarnStrategyMapper.xml | 35 + .../mapper/breakdown/SysCarFaultLogMapper.xml | 66 + .../mapper/breakdown/SysCarFaultMapper.xml | 140 ++ .../mapper/message/DataTypeMapper.xml | 7 + .../message/MessageTemplateTypeMapper.xml | 7 + .../mapper/message/TemplateMapper.xml | 23 + cloud-visual/cloud-visual-monitor/pom.xml | 75 + .../monitor/CloudMonitorApplication.java | 18 + .../monitor/config/WebSecurityConfigurer.java | 37 + .../src/main/resources/banner.txt | 2 + .../src/main/resources/logback/dev.xml | 74 + .../src/main/resources/logback/prod.xml | 74 + .../src/main/resources/logback/test.xml | 74 + cloud-visual/pom.xml | 22 + init-file/cloud-seata.sql | 118 ++ init-file/cloud-system.sql | 682 +++++++++ init-file/nacos_config.zip | Bin 0 -> 7550 bytes init-file/xxl-init.sql | 122 ++ pom.xml | 356 +++++ skywalking/agent/LICENSE | 233 ++++ skywalking/agent/NOTICE | 299 ++++ .../apm-toolkit-kafka-activation-9.2.0.jar | Bin 0 -> 12086 bytes ...apm-toolkit-log4j-1.x-activation-9.2.0.jar | Bin 0 -> 33392 bytes ...apm-toolkit-log4j-2.x-activation-9.2.0.jar | Bin 0 -> 45715 bytes ...m-toolkit-logback-1.x-activation-9.2.0.jar | Bin 0 -> 51489 bytes .../apm-toolkit-logging-common-9.2.0.jar | Bin 0 -> 11940 bytes .../apm-toolkit-meter-activation-9.2.0.jar | Bin 0 -> 28085 bytes ...pm-toolkit-micrometer-activation-9.2.0.jar | Bin 0 -> 186501 bytes ...m-toolkit-opentracing-activation-9.2.0.jar | Bin 0 -> 35571 bytes .../apm-toolkit-trace-activation-9.2.0.jar | Bin 0 -> 113805 bytes .../apm-toolkit-webflux-activation-9.2.0.jar | Bin 0 -> 27347 bytes .../apm-jdk-forkjoinpool-plugin-9.2.0.jar | Bin 0 -> 15834 bytes .../apm-jdk-http-plugin-9.2.0.jar | Bin 0 -> 21541 bytes .../apm-jdk-threading-plugin-9.2.0.jar | Bin 0 -> 20918 bytes .../apm-jdk-threadpool-plugin-9.2.0.jar | Bin 0 -> 16806 bytes skywalking/agent/config/agent.config | 306 ++++ .../apm-impala-jdbc-2.6.x-plugin-9.2.0.jar | Bin 0 -> 30786 bytes skywalking/agent/licenses/LICENSE-asm.txt | 26 + .../apm-customize-enhance-plugin-9.2.0.jar | Bin 0 -> 33367 bytes .../apm-ehcache-2.x-plugin-9.2.0.jar | Bin 0 -> 33401 bytes .../apm-fastjson-1.x-plugin-9.2.0.jar | Bin 0 -> 24700 bytes .../apm-gson-2.x-plugin-9.2.0.jar | Bin 0 -> 17272 bytes .../apm-guava-cache-plugin-9.2.0.jar | Bin 0 -> 19012 bytes .../apm-jackson-2.x-plugin-9.2.0.jar | Bin 0 -> 21118 bytes .../apm-kotlin-coroutine-plugin-9.2.0.jar | Bin 0 -> 14409 bytes .../apm-mybatis-3.x-plugin-9.2.0.jar | Bin 0 -> 16871 bytes .../apm-nacos-client-2.x-plugin-9.2.0.jar | Bin 0 -> 24597 bytes .../apm-netty-http-4.1.x-plugin-9.2.0.jar | Bin 0 -> 57102 bytes .../apm-quartz-scheduler-2.x-plugin-9.2.0.jar | Bin 0 -> 17342 bytes .../apm-resttemplate-6.x-plugin-9.2.0.jar | Bin 0 -> 47227 bytes .../apm-sentinel-1.x-plugin-9.2.0.jar | Bin 0 -> 24347 bytes .../apm-shenyu-2.4.x-plugin-9.2.0.jar | Bin 0 -> 45020 bytes .../apm-spring-annotation-plugin-9.2.0.jar | Bin 0 -> 17981 bytes ...pring-cloud-gateway-2.0.x-plugin-9.2.0.jar | Bin 0 -> 39386 bytes ...pring-cloud-gateway-2.1.x-plugin-9.2.0.jar | Bin 0 -> 44848 bytes ...-spring-cloud-gateway-3.x-plugin-9.2.0.jar | Bin 0 -> 41083 bytes ...-spring-cloud-gateway-4.x-plugin-9.2.0.jar | Bin 0 -> 41244 bytes .../apm-spring-tx-plugin-9.2.0.jar | Bin 0 -> 18956 bytes .../apm-spring-webflux-5.x-plugin-9.2.0.jar | Bin 0 -> 17874 bytes .../apm-spring-webflux-6.x-plugin-9.2.0.jar | Bin 0 -> 18062 bytes ...-springmvc-annotation-6.x-plugin-9.2.0.jar | Bin 0 -> 31461 bytes .../apm-trace-ignore-plugin-9.2.0.jar | Bin 0 -> 17834 bytes .../apm-zookeeper-3.4.x-plugin-9.2.0.jar | Bin 0 -> 21029 bytes .../trace-sampler-cpu-policy-plugin-9.2.0.jar | Bin 0 -> 12369 bytes .../kafka-reporter-plugin-9.2.0.jar | Bin 0 -> 3566533 bytes .../lz4-java-1.6.0.jar | Bin 0 -> 639985 bytes .../snappy-java-1.1.7.3.jar | Bin 0 -> 2021167 bytes .../zstd-jni-1.4.3-1.jar | Bin 0 -> 4056079 bytes .../plugins/apm-activemq-5.x-plugin-9.2.0.jar | Bin 0 -> 21279 bytes ...rtemis-jakarta-client-2.x-plugin-9.2.0.jar | Bin 0 -> 25626 bytes .../plugins/apm-aerospike-plugin-9.2.0.jar | Bin 0 -> 16171 bytes .../apm-armeria-0.84.x-plugin-9.2.0.jar | Bin 0 -> 17447 bytes .../apm-armeria-0.85.x-plugin-9.2.0.jar | Bin 0 -> 25524 bytes .../apm-armeria-1.0.x-plugin-9.2.0.jar | Bin 0 -> 14442 bytes .../apm-asynchttpclient-2.x-plugin-9.2.0.jar | Bin 0 -> 15722 bytes .../agent/plugins/apm-avro-plugin-9.2.0.jar | Bin 0 -> 25758 bytes .../plugins/apm-canal-1.x-plugin-9.2.0.jar | Bin 0 -> 19074 bytes ...cassandra-java-driver-3.x-plugin-9.2.0.jar | Bin 0 -> 24566 bytes .../apm-clickhouse-0.3.1-plugin-9.2.0.jar | Bin 0 -> 23626 bytes .../apm-clickhouse-0.3.2.x-plugin-9.2.0.jar | Bin 0 -> 50779 bytes .../plugins/apm-cxf-3.x-plugin-9.2.0.jar | Bin 0 -> 19396 bytes .../plugins/apm-dubbo-2.7.x-plugin-9.2.0.jar | Bin 0 -> 20766 bytes .../plugins/apm-dubbo-3.x-plugin-9.2.0.jar | Bin 0 -> 16760 bytes .../agent/plugins/apm-dubbo-plugin-9.2.0.jar | Bin 0 -> 17511 bytes .../apm-elastic-job-2.x-plugin-9.2.0.jar | Bin 0 -> 12745 bytes .../apm-elasticjob-3.x-plugin-9.2.0.jar | Bin 0 -> 13019 bytes .../apm-elasticsearch-5.x-plugin-9.2.0.jar | Bin 0 -> 36970 bytes .../apm-elasticsearch-6.x-plugin-9.2.0.jar | Bin 0 -> 98571 bytes .../apm-elasticsearch-7.x-plugin-9.2.0.jar | Bin 0 -> 31771 bytes ...pm-feign-default-http-9.x-plugin-9.2.0.jar | Bin 0 -> 23216 bytes .../apm-finagle-6.25.x-plugin-9.2.0.jar | Bin 0 -> 50637 bytes ...y-2.x-4.x-work-threadpool-plugin-9.2.0.jar | Bin 0 -> 14164 bytes .../plugins/apm-grizzly-2.x-plugin-9.2.0.jar | Bin 0 -> 17526 bytes .../plugins/apm-grpc-1.x-plugin-9.2.0.jar | Bin 0 -> 55432 bytes .../apm-guava-eventbus-plugin-9.2.0.jar | Bin 0 -> 19159 bytes .../agent/plugins/apm-h2-1.x-plugin-9.2.0.jar | Bin 0 -> 22215 bytes .../apm-hbase-1.x-2.x-plugin-9.2.0.jar | Bin 0 -> 19383 bytes .../apm-hikaricp-3.x-4.x-plugin-9.2.0.jar | Bin 0 -> 20900 bytes .../apm-httpClient-4.x-plugin-9.2.0.jar | Bin 0 -> 20761 bytes .../apm-httpasyncclient-4.x-plugin-9.2.0.jar | Bin 0 -> 33294 bytes .../apm-httpclient-3.x-plugin-9.2.0.jar | Bin 0 -> 13892 bytes .../apm-httpclient-5.x-plugin-9.2.0.jar | Bin 0 -> 29829 bytes .../plugins/apm-httpclient-commons-9.2.0.jar | Bin 0 -> 10202 bytes .../apm-hutool-http-5.x-plugin-9.2.0.jar | Bin 0 -> 13518 bytes .../plugins/apm-hystrix-1.x-plugin-9.2.0.jar | Bin 0 -> 31398 bytes .../plugins/apm-influxdb-2.x-plugin-9.2.0.jar | Bin 0 -> 17576 bytes .../agent/plugins/apm-jdbc-commons-9.2.0.jar | Bin 0 -> 105592 bytes .../plugins/apm-jersey-2.x-plugin-9.2.0.jar | Bin 0 -> 13237 bytes .../plugins/apm-jersey-3.x-plugin-9.2.0.jar | Bin 0 -> 13247 bytes .../apm-jetty-client-11.x-plugin-9.2.0.jar | Bin 0 -> 13328 bytes .../apm-jetty-client-9.0-plugin-9.2.0.jar | Bin 0 -> 22487 bytes .../apm-jetty-client-9.x-plugin-9.2.0.jar | Bin 0 -> 22981 bytes .../apm-jetty-server-11.x-plugin-9.2.0.jar | Bin 0 -> 19196 bytes .../apm-jetty-server-9.x-plugin-9.2.0.jar | Bin 0 -> 19412 bytes .../apm-jetty-thread-pool-plugin-9.2.0.jar | Bin 0 -> 13021 bytes .../agent/plugins/apm-kafka-commons-9.2.0.jar | Bin 0 -> 11710 bytes .../agent/plugins/apm-kafka-plugin-9.2.0.jar | Bin 0 -> 46524 bytes ...-kylin-jdbc-2.6.x-3.x-4.x-plugin-9.2.0.jar | Bin 0 -> 30777 bytes .../plugins/apm-lettuce-5.x-plugin-9.2.0.jar | Bin 0 -> 29536 bytes .../plugins/apm-light4j-plugin-9.2.0.jar | Bin 0 -> 15156 bytes .../plugins/apm-mariadb-2.x-plugin-9.2.0.jar | Bin 0 -> 31263 bytes .../plugins/apm-mongodb-2.x-plugin-9.2.0.jar | Bin 0 -> 34231 bytes .../plugins/apm-mongodb-3.x-plugin-9.2.0.jar | Bin 0 -> 37903 bytes .../plugins/apm-mongodb-4.x-plugin-9.2.0.jar | Bin 0 -> 79873 bytes .../agent/plugins/apm-mssql-commons-9.2.0.jar | Bin 0 -> 21810 bytes .../plugins/apm-mssql-jdbc-plugin-9.2.0.jar | Bin 0 -> 16172 bytes .../apm-mssql-jtds-1.x-plugin-9.2.0.jar | Bin 0 -> 14463 bytes .../plugins/apm-mysql-5.x-plugin-9.2.0.jar | Bin 0 -> 31098 bytes .../plugins/apm-mysql-6.x-plugin-9.2.0.jar | Bin 0 -> 32097 bytes .../plugins/apm-mysql-8.x-plugin-9.2.0.jar | Bin 0 -> 30503 bytes .../agent/plugins/apm-mysql-commons-9.2.0.jar | Bin 0 -> 20552 bytes .../plugins/apm-neo4j-4.x-plugin-9.2.0.jar | Bin 0 -> 29423 bytes .../apm-netty-socketio-plugin-9.2.0.jar | Bin 0 -> 22613 bytes .../apm-nutz-http-1.x-plugin-9.2.0.jar | Bin 0 -> 17056 bytes ...m-nutz-mvc-annotation-1.x-plugin-9.2.0.jar | Bin 0 -> 15996 bytes .../plugins/apm-okhttp-3.x-plugin-9.2.0.jar | Bin 0 -> 19774 bytes .../plugins/apm-okhttp-4.x-plugin-9.2.0.jar | Bin 0 -> 19813 bytes .../agent/plugins/apm-okhttp-common-9.2.0.jar | Bin 0 -> 19361 bytes .../plugins/apm-play-2.x-plugin-9.2.0.jar | Bin 0 -> 16508 bytes .../apm-postgresql-8.x-plugin-9.2.0.jar | Bin 0 -> 46943 bytes .../apm-pulsar-2.2-2.7-plugin-9.2.0.jar | Bin 0 -> 14734 bytes .../plugins/apm-pulsar-2.8.x-plugin-9.2.0.jar | Bin 0 -> 15839 bytes .../agent/plugins/apm-pulsar-common-9.2.0.jar | Bin 0 -> 37821 bytes .../agent/plugins/apm-quasar-plugin-9.2.0.jar | Bin 0 -> 12882 bytes .../plugins/apm-rabbitmq-plugin-9.2.0.jar | Bin 0 -> 20026 bytes .../plugins/apm-redisson-3.x-plugin-9.2.0.jar | Bin 0 -> 34243 bytes .../apm-resttemplate-3.x-plugin-9.2.0.jar | Bin 0 -> 39039 bytes .../apm-resttemplate-4.3.x-plugin-9.2.0.jar | Bin 0 -> 39059 bytes .../plugins/apm-rocketMQ-5.x-plugin-9.2.0.jar | Bin 0 -> 37342 bytes .../plugins/apm-rocketmq-3.x-plugin-9.2.0.jar | Bin 0 -> 31792 bytes .../plugins/apm-rocketmq-4.x-plugin-9.2.0.jar | Bin 0 -> 37282 bytes ...-rocketmq-client-java-5.x-plugin-9.2.0.jar | Bin 0 -> 45197 bytes ...vicecomb-java-chassis-2.x-plugin-9.2.0.jar | Bin 0 -> 18009 bytes .../apm-sharding-sphere-3.x-plugin-9.2.0.jar | Bin 0 -> 22514 bytes ...apm-sharding-sphere-4.1.0-plugin-9.2.0.jar | Bin 0 -> 23642 bytes .../apm-shardingsphere-4.0.x-plugin-9.2.0.jar | Bin 0 -> 23621 bytes .../apm-shardingsphere-5.0.0-plugin-9.2.0.jar | Bin 0 -> 29925 bytes .../plugins/apm-solrj-7.x-plugin-9.2.0.jar | Bin 0 -> 22123 bytes ...m-spring-async-annotation-plugin-9.2.0.jar | Bin 0 -> 13216 bytes ...pm-spring-cloud-feign-1.x-plugin-9.2.0.jar | Bin 0 -> 11590 bytes ...pm-spring-cloud-feign-2.x-plugin-9.2.0.jar | Bin 0 -> 11598 bytes ...pring-concurrent-util-4.x-plugin-9.2.0.jar | Bin 0 -> 27946 bytes .../plugins/apm-spring-core-patch-9.2.0.jar | Bin 0 -> 28010 bytes .../apm-spring-kafka-1.x-plugin-9.2.0.jar | Bin 0 -> 14063 bytes .../apm-spring-kafka-2.x-plugin-9.2.0.jar | Bin 0 -> 14806 bytes ...ring-scheduled-annotation-plugin-9.2.0.jar | Bin 0 -> 16865 bytes ...-springmvc-annotation-3.x-plugin-9.2.0.jar | Bin 0 -> 21047 bytes ...-springmvc-annotation-4.x-plugin-9.2.0.jar | Bin 0 -> 25525 bytes ...-springmvc-annotation-5.x-plugin-9.2.0.jar | Bin 0 -> 31620 bytes ...apm-springmvc-annotation-commons-9.2.0.jar | Bin 0 -> 35906 bytes .../apm-spymemcached-2.x-plugin-9.2.0.jar | Bin 0 -> 15300 bytes .../plugins/apm-struts2-2.x-plugin-9.2.0.jar | Bin 0 -> 13051 bytes .../apm-tomcat-thread-pool-plugin-9.2.0.jar | Bin 0 -> 12784 bytes .../plugins/apm-undertow-2.x-plugin-9.2.0.jar | Bin 0 -> 34411 bytes ...dertow-worker-thread-pool-plugin-9.2.0.jar | Bin 0 -> 14769 bytes .../apm-vertx-core-3.x-plugin-9.2.0.jar | Bin 0 -> 86153 bytes .../apm-vertx-core-4.x-plugin-9.2.0.jar | Bin 0 -> 18945 bytes .../apm-xmemcached-2.x-plugin-9.2.0.jar | Bin 0 -> 24405 bytes .../plugins/apm-xxl-job-2.x-plugin-9.2.0.jar | Bin 0 -> 24987 bytes .../plugins/baidu-brpc-3.x-plugin-9.2.0.jar | Bin 0 -> 17826 bytes .../agent/plugins/baidu-brpc-plugin-9.2.0.jar | Bin 0 -> 16911 bytes .../agent/plugins/dbcp-2.x-plugin-9.2.0.jar | Bin 0 -> 21413 bytes .../agent/plugins/druid-1.x-plugin-9.2.0.jar | Bin 0 -> 21780 bytes .../dubbo-2.7.x-conflict-patch-9.2.0.jar | Bin 0 -> 17844 bytes .../dubbo-3.x-conflict-patch-9.2.0.jar | Bin 0 -> 17919 bytes .../plugins/dubbo-conflict-patch-9.2.0.jar | Bin 0 -> 17156 bytes .../plugins/elasticsearch-common-9.2.0.jar | Bin 0 -> 9465 bytes .../graphql-12.x-15.x-plugin-9.2.0.jar | Bin 0 -> 12602 bytes .../plugins/graphql-16plus-plugin-9.2.0.jar | Bin 0 -> 12522 bytes .../plugins/graphql-8.x-plugin-9.2.0.jar | Bin 0 -> 12907 bytes .../plugins/graphql-9.x-plugin-9.2.0.jar | Bin 0 -> 12481 bytes .../plugins/jedis-2.x-3.x-plugin-9.2.0.jar | Bin 0 -> 36797 bytes .../agent/plugins/jedis-4.x-plugin-9.2.0.jar | Bin 0 -> 36043 bytes .../plugins/jsonrpc4j-1.x-plugin-9.2.0.jar | Bin 0 -> 25108 bytes .../micronaut-http-client-plugin-9.2.0.jar | Bin 0 -> 22440 bytes .../micronaut-http-server-plugin-9.2.0.jar | Bin 0 -> 24217 bytes .../agent/plugins/motan-plugin-9.2.0.jar | Bin 0 -> 17747 bytes .../nats-2.14.x-2.15.x-plugin-9.2.0.jar | Bin 0 -> 40153 bytes .../agent/plugins/okhttp-2.x-plugin-9.2.0.jar | Bin 0 -> 28906 bytes .../resteasy-server-3.x-plugin-9.2.0.jar | Bin 0 -> 15928 bytes .../resteasy-server-4.x-plugin-9.2.0.jar | Bin 0 -> 16153 bytes .../resteasy-server-6.x-plugin-9.2.0.jar | Bin 0 -> 16156 bytes .../plugins/resttemplate-commons-9.2.0.jar | Bin 0 -> 34645 bytes .../agent/plugins/sofa-rpc-plugin-9.2.0.jar | Bin 0 -> 21788 bytes .../agent/plugins/spring-commons-9.2.0.jar | Bin 0 -> 8704 bytes ...ing-webflux-5.x-webclient-plugin-9.2.0.jar | Bin 0 -> 18956 bytes ...ing-webflux-6.x-webclient-plugin-9.2.0.jar | Bin 0 -> 18938 bytes .../agent/plugins/thrift-plugin-9.2.0.jar | Bin 0 -> 60936 bytes .../agent/plugins/tomcat-10x-plugin-9.2.0.jar | Bin 0 -> 23390 bytes .../plugins/tomcat-7.x-8.x-plugin-9.2.0.jar | Bin 0 -> 23680 bytes .../websphere-liberty-23.x-plugin-9.2.0.jar | Bin 0 -> 29204 bytes skywalking/agent/skywalking-agent.jar | Bin 0 -> 22355074 bytes skywalking/ins | 10 + .../install/elasticsearch/elasticsearch.sh | 10 + skywalking/install/skywalking-net.sh | 2 + .../install/skywalking/skywalking-oap.sh | 11 + .../install/skywalking/skywalking-ui.sh | 8 + skywalking/show | 6 + .../job-admin/config/application.properties | 33 + xxl-job/job-admin/depXXLAdmin.sh | 8 + xxl-job/mysql/config/my.cnf | 66 + xxl-job/mysql/depXXLMysql.sh | 9 + xxl-job/show | 13 + xxl-job/xxl-net.sh | 1 + 732 files changed, 43700 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 cloud-auth/pom.xml create mode 100644 cloud-auth/src/main/java/com/muyu/auth/CloudAuthApplication.java create mode 100644 cloud-auth/src/main/java/com/muyu/auth/controller/TokenController.java create mode 100644 cloud-auth/src/main/java/com/muyu/auth/form/LoginBody.java create mode 100644 cloud-auth/src/main/java/com/muyu/auth/form/RegisterBody.java create mode 100644 cloud-auth/src/main/java/com/muyu/auth/service/SysLoginService.java create mode 100644 cloud-auth/src/main/java/com/muyu/auth/service/SysPasswordService.java create mode 100644 cloud-auth/src/main/java/com/muyu/auth/service/SysRecordLogService.java create mode 100644 cloud-auth/src/main/resources/banner.txt create mode 100644 cloud-auth/src/main/resources/logback/dev.xml create mode 100644 cloud-auth/src/main/resources/logback/prod.xml create mode 100644 cloud-auth/src/main/resources/logback/test.xml create mode 100644 cloud-common/cloud-common-api-doc/pom.xml create mode 100644 cloud-common/cloud-common-api-doc/src/main/java/com/muyu/common/api/doc/config/SpringDocConfig.java create mode 100644 cloud-common/cloud-common-api-doc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 cloud-common/cloud-common-core/pom.xml create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/annotation/Excel.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/annotation/Excels.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/CacheConstants.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/Constants.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/GenConstants.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/HttpStatus.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/ScheduleConstants.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/SecurityConstants.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/ServiceNameConstants.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/TokenConstants.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/UserConstants.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/context/SecurityContextHolder.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/domain/Result.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/enums/UserStatus.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/CaptchaException.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/CheckedException.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/DemoModeException.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/GlobalException.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/InnerAuthException.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/PreAuthorizeException.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/ServiceException.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/UtilException.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/auth/NotLoginException.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/auth/NotPermissionException.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/auth/NotRoleException.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/base/BaseException.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/FileException.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/FileNameLengthLimitExceededException.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/FileSizeLimitExceededException.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/FileUploadException.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/InvalidExtensionException.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/job/TaskException.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/user/CaptchaExpireException.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/user/UserException.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/user/UserPasswordNotMatchException.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/feign/FeginConfig.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/text/CharsetKit.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/text/Convert.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/text/StrFormatter.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/DateUtils.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/ExceptionUtil.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/JwtUtils.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/PageUtils.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/ServletUtils.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/SpringUtils.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/StringUtils.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/bean/BeanUtils.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/bean/BeanValidators.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/file/FileTypeUtils.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/file/FileUtils.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/file/ImageUtils.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/file/MimeTypeUtils.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/html/EscapeUtil.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/html/HTMLFilter.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/ip/IpUtils.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/poi/ExcelHandlerAdapter.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/poi/ExcelUtil.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/reflect/ReflectUtils.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/sign/Base64.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/sql/SqlUtil.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/uuid/IdUtils.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/uuid/Seq.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/uuid/UUID.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/validation/ValidationConfig.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/controller/BaseController.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/domain/BaseEntity.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/domain/TreeEntity.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/page/PageDomain.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/page/TableDataInfo.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/page/TableSupport.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/xss/Xss.java create mode 100644 cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/xss/XssValidator.java create mode 100644 cloud-common/cloud-common-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 cloud-common/cloud-common-datascope/pom.xml create mode 100644 cloud-common/cloud-common-datascope/src/main/java/com/muyu/common/datascope/annotation/DataScope.java create mode 100644 cloud-common/cloud-common-datascope/src/main/java/com/muyu/common/datascope/aspect/DataScopeAspect.java create mode 100644 cloud-common/cloud-common-datascope/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 cloud-common/cloud-common-datasource/pom.xml create mode 100644 cloud-common/cloud-common-datasource/src/main/java/com/muyu/common/datasource/annotation/Master.java create mode 100644 cloud-common/cloud-common-datasource/src/main/java/com/muyu/common/datasource/annotation/Slave.java create mode 100644 cloud-common/cloud-common-log/pom.xml create mode 100644 cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/annotation/Log.java create mode 100644 cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/aspect/LogAspect.java create mode 100644 cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/enums/BusinessStatus.java create mode 100644 cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/enums/BusinessType.java create mode 100644 cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/enums/OperatorType.java create mode 100644 cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/filter/PropertyPreExcludeFilter.java create mode 100644 cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/service/AsyncLogService.java create mode 100644 cloud-common/cloud-common-log/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 cloud-common/cloud-common-rabbit/pom.xml create mode 100644 cloud-common/cloud-common-rabbit/src/main/java/com/muyu/common/rabbit/RabbitListenerConfigurer.java create mode 100644 cloud-common/cloud-common-rabbit/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 cloud-common/cloud-common-redis/pom.xml create mode 100644 cloud-common/cloud-common-redis/src/main/java/com/muyu/common/redis/configure/FastJson2JsonRedisSerializer.java create mode 100644 cloud-common/cloud-common-redis/src/main/java/com/muyu/common/redis/configure/RedisConfig.java create mode 100644 cloud-common/cloud-common-redis/src/main/java/com/muyu/common/redis/service/RedisService.java create mode 100644 cloud-common/cloud-common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 cloud-common/cloud-common-saas/pom.xml create mode 100644 cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/many/datasource/ManyDataSource.java create mode 100644 cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/many/datasource/constents/DatasourceContent.java create mode 100644 cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/many/datasource/domain/model/DataSourceInfo.java create mode 100644 cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/many/datasource/factory/DruidDataSourceFactory.java create mode 100644 cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/many/datasource/holder/DynamicDataSourceHolder.java create mode 100644 cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/many/datasource/role/DynamicDataSource.java create mode 100644 cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/saas/contents/SaaSConstant.java create mode 100644 cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/saas/domain/Datasource.java create mode 100644 cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/saas/domain/model/EntInfo.java create mode 100644 cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/saas/exception/SaaSException.java create mode 100644 cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/saas/interceptor/SaaSInterceptor.java create mode 100644 cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/saas/interceptor/WebMvcSaaSConfig.java create mode 100644 cloud-common/cloud-common-saas/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 cloud-common/cloud-common-seata/pom.xml create mode 100644 cloud-common/cloud-common-security/pom.xml create mode 100644 cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/EnableCustomConfig.java create mode 100644 cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/EnableMyFeignClients.java create mode 100644 cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/InnerAuth.java create mode 100644 cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/Logical.java create mode 100644 cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/RequiresLogin.java create mode 100644 cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/RequiresPermissions.java create mode 100644 cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/RequiresRoles.java create mode 100644 cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/aspect/InnerAuthAspect.java create mode 100644 cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/aspect/PreAuthorizeAspect.java create mode 100644 cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/auth/AuthLogic.java create mode 100644 cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/auth/AuthUtil.java create mode 100644 cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/config/ApplicationConfig.java create mode 100644 cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/config/WebMvcConfig.java create mode 100644 cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/feign/FeignAutoConfiguration.java create mode 100644 cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/feign/FeignRequestInterceptor.java create mode 100644 cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/handler/GlobalExceptionHandler.java create mode 100644 cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/interceptor/HeaderInterceptor.java create mode 100644 cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/service/TokenService.java create mode 100644 cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/utils/DictUtils.java create mode 100644 cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/utils/SecurityUtils.java create mode 100644 cloud-common/cloud-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 cloud-common/cloud-common-system/pom.xml create mode 100644 cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/Datasource.java create mode 100644 cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/LoginUser.java create mode 100644 cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysDept.java create mode 100644 cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysDictData.java create mode 100644 cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysDictType.java create mode 100644 cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysFile.java create mode 100644 cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysFirmUser.java create mode 100644 cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysLogininfor.java create mode 100644 cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysOperLog.java create mode 100644 cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysRole.java create mode 100644 cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysUser.java create mode 100644 cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/RemoteFileService.java create mode 100644 cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/RemoteLogService.java create mode 100644 cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/RemoteSaaSService.java create mode 100644 cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/RemoteUserService.java create mode 100644 cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/factory/RemoteFileFallbackFactory.java create mode 100644 cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/factory/RemoteLogFallbackFactory.java create mode 100644 cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/factory/RemoteSaaSFallbackFactory.java create mode 100644 cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/factory/RemoteUserFallbackFactory.java create mode 100644 cloud-common/cloud-common-system/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 cloud-common/cloud-common-xxl/pom.xml create mode 100644 cloud-common/cloud-common-xxl/src/main/java/com/muyu/common/xxl/XXLJobConfig.java create mode 100644 cloud-common/cloud-common-xxl/src/main/java/com/muyu/common/xxl/XxlJobProperties.java create mode 100644 cloud-common/cloud-common-xxl/src/main/java/com/muyu/common/xxl/demo/XxlJobDemoService.java create mode 100644 cloud-common/cloud-common-xxl/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 cloud-common/pom.xml create mode 100644 cloud-gateway/pom.xml create mode 100644 cloud-gateway/src/main/java/com/muyu/gateway/CloudGatewayApplication.java create mode 100644 cloud-gateway/src/main/java/com/muyu/gateway/config/CaptchaConfig.java create mode 100644 cloud-gateway/src/main/java/com/muyu/gateway/config/GatewayConfig.java create mode 100644 cloud-gateway/src/main/java/com/muyu/gateway/config/KaptchaTextCreator.java create mode 100644 cloud-gateway/src/main/java/com/muyu/gateway/config/RouterFunctionConfiguration.java create mode 100644 cloud-gateway/src/main/java/com/muyu/gateway/config/properties/CaptchaProperties.java create mode 100644 cloud-gateway/src/main/java/com/muyu/gateway/config/properties/IgnoreWhiteProperties.java create mode 100644 cloud-gateway/src/main/java/com/muyu/gateway/config/properties/XssProperties.java create mode 100644 cloud-gateway/src/main/java/com/muyu/gateway/filter/AuthFilter.java create mode 100644 cloud-gateway/src/main/java/com/muyu/gateway/filter/BlackListUrlFilter.java create mode 100644 cloud-gateway/src/main/java/com/muyu/gateway/filter/CacheRequestFilter.java create mode 100644 cloud-gateway/src/main/java/com/muyu/gateway/filter/ValidateCodeFilter.java create mode 100644 cloud-gateway/src/main/java/com/muyu/gateway/filter/XssFilter.java create mode 100644 cloud-gateway/src/main/java/com/muyu/gateway/handler/GatewayExceptionHandler.java create mode 100644 cloud-gateway/src/main/java/com/muyu/gateway/handler/SentinelFallbackHandler.java create mode 100644 cloud-gateway/src/main/java/com/muyu/gateway/handler/ValidateCodeHandler.java create mode 100644 cloud-gateway/src/main/java/com/muyu/gateway/model/resp/CaptchaCodeResp.java create mode 100644 cloud-gateway/src/main/java/com/muyu/gateway/service/ValidateCodeService.java create mode 100644 cloud-gateway/src/main/java/com/muyu/gateway/service/impl/ValidateCodeServiceImpl.java create mode 100644 cloud-gateway/src/main/java/com/muyu/gateway/utils/WebFrameworkUtils.java create mode 100644 cloud-gateway/src/main/resources/banner.txt create mode 100644 cloud-gateway/src/main/resources/logback/dev.xml create mode 100644 cloud-gateway/src/main/resources/logback/prod.xml create mode 100644 cloud-gateway/src/main/resources/logback/test.xml create mode 100644 cloud-modules/cloud-modules-file/pom.xml create mode 100644 cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/CloudFileApplication.java create mode 100644 cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/config/MinioConfig.java create mode 100644 cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/config/ResourcesConfig.java create mode 100644 cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/controller/SysFileController.java create mode 100644 cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/service/ISysFileService.java create mode 100644 cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/service/LocalSysFileServiceImpl.java create mode 100644 cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/service/MinioSysFileServiceImpl.java create mode 100644 cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/utils/FileUploadUtils.java create mode 100644 cloud-modules/cloud-modules-file/src/main/resources/banner.txt create mode 100644 cloud-modules/cloud-modules-file/src/main/resources/logback/dev.xml create mode 100644 cloud-modules/cloud-modules-file/src/main/resources/logback/prod.xml create mode 100644 cloud-modules/cloud-modules-file/src/main/resources/logback/test.xml create mode 100644 cloud-modules/cloud-modules-gen/pom.xml create mode 100644 cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/CloudGenApplication.java create mode 100644 cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/config/GenConfig.java create mode 100644 cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/controller/GenController.java create mode 100644 cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/domain/GenTable.java create mode 100644 cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/domain/GenTableColumn.java create mode 100644 cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/domain/GenTableResp.java create mode 100644 cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/mapper/GenTableColumnMapper.java create mode 100644 cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/mapper/GenTableMapper.java create mode 100644 cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/service/GenTableColumnServiceImpl.java create mode 100644 cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/service/GenTableServiceImpl.java create mode 100644 cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/service/IGenTableColumnService.java create mode 100644 cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/service/IGenTableService.java create mode 100644 cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/util/GenUtils.java create mode 100644 cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/util/VelocityInitializer.java create mode 100644 cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/util/VelocityUtils.java create mode 100644 cloud-modules/cloud-modules-gen/src/main/resources/banner.txt create mode 100644 cloud-modules/cloud-modules-gen/src/main/resources/logback/dev.xml create mode 100644 cloud-modules/cloud-modules-gen/src/main/resources/logback/prod.xml create mode 100644 cloud-modules/cloud-modules-gen/src/main/resources/logback/test.xml create mode 100644 cloud-modules/cloud-modules-gen/src/main/resources/mapper/generator/GenTableColumnMapper.xml create mode 100644 cloud-modules/cloud-modules-gen/src/main/resources/mapper/generator/GenTableMapper.xml create mode 100644 cloud-modules/cloud-modules-gen/src/main/resources/vm/java/controller.java.vm create mode 100644 cloud-modules/cloud-modules-gen/src/main/resources/vm/java/domain.java.vm create mode 100644 cloud-modules/cloud-modules-gen/src/main/resources/vm/java/mapper.java.vm create mode 100644 cloud-modules/cloud-modules-gen/src/main/resources/vm/java/service.java.vm create mode 100644 cloud-modules/cloud-modules-gen/src/main/resources/vm/java/serviceImpl.java.vm create mode 100644 cloud-modules/cloud-modules-gen/src/main/resources/vm/java/sub-domain.java.vm create mode 100644 cloud-modules/cloud-modules-gen/src/main/resources/vm/js/api.js.vm create mode 100644 cloud-modules/cloud-modules-gen/src/main/resources/vm/sql/sql.vm create mode 100644 cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/index-tree.vue.vm create mode 100644 cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/index.vue.vm create mode 100644 cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/v3/index-tree.vue.vm create mode 100644 cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/v3/index.vue.vm create mode 100644 cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/v3/readme.txt create mode 100644 cloud-modules/cloud-modules-gen/src/main/resources/vm/xml/mapper.xml.vm create mode 100644 cloud-modules/cloud-modules-system/pom.xml create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/CloudSystemApplication.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysConfigController.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysDeptController.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysDictDataController.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysDictTypeController.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysLogininforController.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysMenuController.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysNoticeController.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysOperlogController.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysPostController.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysProfileController.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysRoleController.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysUserController.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysUserOnlineController.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysConfig.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysMenu.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysNotice.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysPost.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysRoleDept.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysRoleMenu.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysUserOnline.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysUserPost.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysUserRole.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/AuthRoleResp.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/DeptTreeResp.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/ProfileResp.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/RoleMenuTreeResp.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/UserDetailInfoResp.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/UserInfoResp.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/vo/MetaVo.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/vo/RouterVo.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/vo/TreeSelect.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysConfigMapper.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysDeptMapper.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysDictDataMapper.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysDictTypeMapper.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysLogininforMapper.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysMenuMapper.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysNoticeMapper.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysOperLogMapper.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysPostMapper.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysRoleDeptMapper.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysRoleMapper.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysRoleMenuMapper.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysUserMapper.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysUserPostMapper.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysUserRoleMapper.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/rabbit/RabbitTest.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysConfigService.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysDeptService.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysDictDataService.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysDictTypeService.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysLogininforService.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysMenuService.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysNoticeService.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysOperLogService.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysPermissionService.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysPostService.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysRoleService.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysUserOnlineService.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysUserService.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysConfigServiceImpl.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysDeptServiceImpl.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysDictDataServiceImpl.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysDictTypeServiceImpl.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysLogininforServiceImpl.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysMenuServiceImpl.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysNoticeServiceImpl.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysOperLogServiceImpl.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysPermissionServiceImpl.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysPostServiceImpl.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysRoleServiceImpl.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysUserOnlineServiceImpl.java create mode 100644 cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysUserServiceImpl.java create mode 100644 cloud-modules/cloud-modules-system/src/main/resources/banner.txt create mode 100644 cloud-modules/cloud-modules-system/src/main/resources/logback/dev.xml create mode 100644 cloud-modules/cloud-modules-system/src/main/resources/logback/prod.xml create mode 100644 cloud-modules/cloud-modules-system/src/main/resources/logback/test.xml create mode 100644 cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysConfigMapper.xml create mode 100644 cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysDeptMapper.xml create mode 100644 cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysDictDataMapper.xml create mode 100644 cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysDictTypeMapper.xml create mode 100644 cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysLogininforMapper.xml create mode 100644 cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysMenuMapper.xml create mode 100644 cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysNoticeMapper.xml create mode 100644 cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysOperLogMapper.xml create mode 100644 cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysPostMapper.xml create mode 100644 cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml create mode 100644 cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysRoleMapper.xml create mode 100644 cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml create mode 100644 cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysUserMapper.xml create mode 100644 cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysUserPostMapper.xml create mode 100644 cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysUserRoleMapper.xml create mode 100644 cloud-modules/cloud-modules-vehiclegateway/pom.xml create mode 100644 cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/VehicleGatewayApplication.java create mode 100644 cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/vehicle/VehicleInstance.java create mode 100644 cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/vehicle/VehicleThread.java create mode 100644 cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/vehicle/api/ClientAdmin.java create mode 100644 cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/vehicle/core/LocalContainer.java create mode 100644 cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/vehicle/core/VehicleConfiguration.java create mode 100644 cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/common/ScheduledThreadPool.java create mode 100644 cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/controller/testController.java create mode 100644 cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/domain/MqttProperties.java create mode 100644 cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/domain/ServerConfig.java create mode 100644 cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/domain/VehicleInfo.java create mode 100644 cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/domain/model/MqttServerModel.java create mode 100644 cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/domain/model/PositionModel.java create mode 100644 cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/domain/model/ServerConfigModel.java create mode 100644 cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/domain/req/VehicleConnectionReq.java create mode 100644 cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/service/VehicleInstanceService.java create mode 100644 cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/service/impl/VehicleInstanceServiceImpl.java create mode 100644 cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/utils/MD5Util.java create mode 100644 cloud-modules/cloud-modules-vehiclegateway/src/main/resources/banner.txt create mode 100644 cloud-modules/cloud-modules-vehiclegateway/src/main/resources/logback/dev.xml create mode 100644 cloud-modules/cloud-modules-vehiclegateway/src/main/resources/logback/prod.xml create mode 100644 cloud-modules/cloud-modules-vehiclegateway/src/main/resources/logback/test.xml create mode 100644 cloud-modules/cloud-modules-wechat/pom.xml create mode 100644 cloud-modules/cloud-modules-wechat/src/main/java/com/muyu/wechat/CloudWeChatApplication.java create mode 100644 cloud-modules/cloud-modules-wechat/src/main/java/com/muyu/wechat/controller/WxTestController.java create mode 100644 cloud-modules/cloud-modules-wechat/src/main/java/com/muyu/wechat/domain/Message.java create mode 100644 cloud-modules/cloud-modules-wechat/src/main/java/com/muyu/wechat/message/Article.java create mode 100644 cloud-modules/cloud-modules-wechat/src/main/java/com/muyu/wechat/message/NewMessage.java create mode 100644 cloud-modules/cloud-modules-wechat/src/main/java/com/muyu/wechat/util/OkHttpUtils.java create mode 100644 cloud-modules/cloud-modules-wechat/src/main/java/com/muyu/wechat/util/TokenUtil.java create mode 100644 cloud-modules/pom.xml create mode 100644 cloud-modules/saas/pom.xml create mode 100644 cloud-modules/saas/saas-common/pom.xml create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/CarType.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/DataType.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/Enterprise.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/MessageTemplate.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/MessageTemplateType.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/SysCar.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/SysCarEnterprise.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/SysCarFault.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/SysCarFaultLog.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/SysCarFaultMessage.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/SysCarLog.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/Template.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/WarnLogs.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/WarnRule.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/WarnStrategy.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/database/ElectronicFence.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/database/ElectronicFenceGroup.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/database/FenceGroupMid.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/ElectroicFenceAddReq.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/ElectroicFenceListReq.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/ElectroicFenceUpdReq.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/ElectronicFenceGroupAddReq.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/ElectronicFenceGroupListReq.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/ElectronicFenceGroupUpdReq.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/FenceAndGroupBoundReq.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/FenceWayReq.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/SysCarReq.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/WarnStrategyReq.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/CarTypeResp.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/ElectronicFenceGroupResp.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/ElectronicFenceResp.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/GroupFenceListresp.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/SysCarFaultLogVo.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/SysCarVo.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/WarnLogsResp.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/WarnRuleResp.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/WarnStrategyResp.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/utils/ElectricFenceModel.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/utils/ElectricFenceResultTmp.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/utils/ElectronicFenceResult.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/utils/ElectronicFenceSetting.java create mode 100644 cloud-modules/saas/saas-common/src/main/java/com/muyu/common/util/PageUtils.java create mode 100644 cloud-modules/saas/saas-server/pom.xml create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/SaasApplication.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/CarTypeController.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/DataTypeController.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/ElectronicFenceController.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/ElectronicFenceGroupController.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/EnterpriseController.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/MessageTemplateTypeController.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/SysCarController.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/SysCarFaultController.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/SysCarFaultLogController.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/SysCarFaultMessageController.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/SysCarLogController.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/TemplateController.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/TemplateNeedController.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/TemplateService.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/WarnLogsController.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/WarnRuleController.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/WarnStrategyController.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/form/DeleteEnterpriseByIds.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/form/InsertEnterprise.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/form/SearchEnterpriseName.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/form/UpdateEnterprise.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/CarTypeMapper.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/DataTypeMapper.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/ElectronicFenceGroupMapper.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/ElectronicFenceMapper.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/EnterpriseDao.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/FenceGroupMidMapper.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/MessageTemplateTypeMapper.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/SysCarFaultLogMapper.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/SysCarFaultMapper.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/SysCarFaultMessageMapper.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/SysCarLogMapper.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/SysCarMapper.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/TemplateMapper.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/TemplateNeedMapper.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/WarnLogsMapper.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/WarnRuleMapper.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/WarnStrategyMapper.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/CarTypeService.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/DataTypeService.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/ElectronicFenceGroupService.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/ElectronicFenceService.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/EnterpriseService.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/FenceGroupMidService.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/MessageTemplateTypeService.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/SysCarFaultLogService.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/SysCarFaultMessageService.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/SysCarFaultService.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/SysCarLogService.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/SysCarService.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/TemplateNeedService.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/TemplateService.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/WarnLogsService.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/WarnRuleService.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/WarnStrategyService.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/impl/CarTypeServiceImpl.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/impl/DataTypeServiceImpl.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/impl/ElectronicFenceGroupServiceImpl.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/impl/ElectronicFenceServiceImpl.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/impl/EnterpriseServiceImpl.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/impl/FenceGroupMidServiceImpl.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/impl/MessageTemplateTypeServiceImpl.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/impl/SysCarFaultLogServiceImpl.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/impl/SysCarFaultMessageServiceImpl.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/impl/SysCarFaultServiceImpl.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/impl/SysCarLogServiceImpl.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/impl/SysCarServiceImpl.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/impl/TemplateNeedServiceImpl.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/impl/TemplateServiceImpl.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/impl/WarnLogsServiceImpl.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/impl/WarnRuleServiceImpl.java create mode 100644 cloud-modules/saas/saas-server/src/main/java/com/muyu/server/service/impl/WarnStrategyServiceImpl.java create mode 100644 cloud-modules/saas/saas-server/src/main/resources/banner.txt create mode 100644 cloud-modules/saas/saas-server/src/main/resources/logback/dev.xml create mode 100644 cloud-modules/saas/saas-server/src/main/resources/logback/prod.xml create mode 100644 cloud-modules/saas/saas-server/src/main/resources/logback/test.xml create mode 100644 cloud-modules/saas/saas-server/src/main/resources/mapper/CarTypeMapper.xml create mode 100644 cloud-modules/saas/saas-server/src/main/resources/mapper/EnterpriseMapper.xml create mode 100644 cloud-modules/saas/saas-server/src/main/resources/mapper/SysCarMapper.xml create mode 100644 cloud-modules/saas/saas-server/src/main/resources/mapper/TemplateNeedMapper.xml create mode 100644 cloud-modules/saas/saas-server/src/main/resources/mapper/WarnLogsMapper.xml create mode 100644 cloud-modules/saas/saas-server/src/main/resources/mapper/WarnRuleMapper.xml create mode 100644 cloud-modules/saas/saas-server/src/main/resources/mapper/WarnStrategyMapper.xml create mode 100644 cloud-modules/saas/saas-server/src/main/resources/mapper/breakdown/SysCarFaultLogMapper.xml create mode 100644 cloud-modules/saas/saas-server/src/main/resources/mapper/breakdown/SysCarFaultMapper.xml create mode 100644 cloud-modules/saas/saas-server/src/main/resources/mapper/message/DataTypeMapper.xml create mode 100644 cloud-modules/saas/saas-server/src/main/resources/mapper/message/MessageTemplateTypeMapper.xml create mode 100644 cloud-modules/saas/saas-server/src/main/resources/mapper/message/TemplateMapper.xml create mode 100644 cloud-visual/cloud-visual-monitor/pom.xml create mode 100644 cloud-visual/cloud-visual-monitor/src/main/java/com/muyu/modules/monitor/CloudMonitorApplication.java create mode 100644 cloud-visual/cloud-visual-monitor/src/main/java/com/muyu/modules/monitor/config/WebSecurityConfigurer.java create mode 100644 cloud-visual/cloud-visual-monitor/src/main/resources/banner.txt create mode 100644 cloud-visual/cloud-visual-monitor/src/main/resources/logback/dev.xml create mode 100644 cloud-visual/cloud-visual-monitor/src/main/resources/logback/prod.xml create mode 100644 cloud-visual/cloud-visual-monitor/src/main/resources/logback/test.xml create mode 100644 cloud-visual/pom.xml create mode 100644 init-file/cloud-seata.sql create mode 100644 init-file/cloud-system.sql create mode 100644 init-file/nacos_config.zip create mode 100644 init-file/xxl-init.sql create mode 100644 pom.xml create mode 100644 skywalking/agent/LICENSE create mode 100644 skywalking/agent/NOTICE create mode 100644 skywalking/agent/activations/apm-toolkit-kafka-activation-9.2.0.jar create mode 100644 skywalking/agent/activations/apm-toolkit-log4j-1.x-activation-9.2.0.jar create mode 100644 skywalking/agent/activations/apm-toolkit-log4j-2.x-activation-9.2.0.jar create mode 100644 skywalking/agent/activations/apm-toolkit-logback-1.x-activation-9.2.0.jar create mode 100644 skywalking/agent/activations/apm-toolkit-logging-common-9.2.0.jar create mode 100644 skywalking/agent/activations/apm-toolkit-meter-activation-9.2.0.jar create mode 100644 skywalking/agent/activations/apm-toolkit-micrometer-activation-9.2.0.jar create mode 100644 skywalking/agent/activations/apm-toolkit-opentracing-activation-9.2.0.jar create mode 100644 skywalking/agent/activations/apm-toolkit-trace-activation-9.2.0.jar create mode 100644 skywalking/agent/activations/apm-toolkit-webflux-activation-9.2.0.jar create mode 100644 skywalking/agent/bootstrap-plugins/apm-jdk-forkjoinpool-plugin-9.2.0.jar create mode 100644 skywalking/agent/bootstrap-plugins/apm-jdk-http-plugin-9.2.0.jar create mode 100644 skywalking/agent/bootstrap-plugins/apm-jdk-threading-plugin-9.2.0.jar create mode 100644 skywalking/agent/bootstrap-plugins/apm-jdk-threadpool-plugin-9.2.0.jar create mode 100644 skywalking/agent/config/agent.config create mode 100644 skywalking/agent/expired-plugins/apm-impala-jdbc-2.6.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/licenses/LICENSE-asm.txt create mode 100644 skywalking/agent/optional-plugins/apm-customize-enhance-plugin-9.2.0.jar create mode 100644 skywalking/agent/optional-plugins/apm-ehcache-2.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/optional-plugins/apm-fastjson-1.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/optional-plugins/apm-gson-2.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/optional-plugins/apm-guava-cache-plugin-9.2.0.jar create mode 100644 skywalking/agent/optional-plugins/apm-jackson-2.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/optional-plugins/apm-kotlin-coroutine-plugin-9.2.0.jar create mode 100644 skywalking/agent/optional-plugins/apm-mybatis-3.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/optional-plugins/apm-nacos-client-2.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/optional-plugins/apm-netty-http-4.1.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/optional-plugins/apm-quartz-scheduler-2.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/optional-plugins/apm-resttemplate-6.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/optional-plugins/apm-sentinel-1.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/optional-plugins/apm-shenyu-2.4.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/optional-plugins/apm-spring-annotation-plugin-9.2.0.jar create mode 100644 skywalking/agent/optional-plugins/apm-spring-cloud-gateway-2.0.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/optional-plugins/apm-spring-cloud-gateway-2.1.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/optional-plugins/apm-spring-cloud-gateway-3.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/optional-plugins/apm-spring-cloud-gateway-4.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/optional-plugins/apm-spring-tx-plugin-9.2.0.jar create mode 100644 skywalking/agent/optional-plugins/apm-spring-webflux-5.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/optional-plugins/apm-spring-webflux-6.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/optional-plugins/apm-springmvc-annotation-6.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/optional-plugins/apm-trace-ignore-plugin-9.2.0.jar create mode 100644 skywalking/agent/optional-plugins/apm-zookeeper-3.4.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/optional-plugins/trace-sampler-cpu-policy-plugin-9.2.0.jar create mode 100644 skywalking/agent/optional-reporter-plugins/kafka-reporter-plugin-9.2.0.jar create mode 100644 skywalking/agent/optional-reporter-plugins/lz4-java-1.6.0.jar create mode 100644 skywalking/agent/optional-reporter-plugins/snappy-java-1.1.7.3.jar create mode 100644 skywalking/agent/optional-reporter-plugins/zstd-jni-1.4.3-1.jar create mode 100644 skywalking/agent/plugins/apm-activemq-5.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-activemq-artemis-jakarta-client-2.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-aerospike-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-armeria-0.84.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-armeria-0.85.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-armeria-1.0.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-asynchttpclient-2.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-avro-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-canal-1.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-cassandra-java-driver-3.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-clickhouse-0.3.1-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-clickhouse-0.3.2.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-cxf-3.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-dubbo-2.7.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-dubbo-3.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-dubbo-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-elastic-job-2.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-elasticjob-3.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-elasticsearch-5.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-elasticsearch-6.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-elasticsearch-7.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-feign-default-http-9.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-finagle-6.25.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-grizzly-2.x-4.x-work-threadpool-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-grizzly-2.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-grpc-1.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-guava-eventbus-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-h2-1.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-hbase-1.x-2.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-hikaricp-3.x-4.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-httpClient-4.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-httpasyncclient-4.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-httpclient-3.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-httpclient-5.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-httpclient-commons-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-hutool-http-5.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-hystrix-1.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-influxdb-2.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-jdbc-commons-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-jersey-2.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-jersey-3.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-jetty-client-11.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-jetty-client-9.0-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-jetty-client-9.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-jetty-server-11.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-jetty-server-9.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-jetty-thread-pool-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-kafka-commons-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-kafka-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-kylin-jdbc-2.6.x-3.x-4.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-lettuce-5.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-light4j-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-mariadb-2.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-mongodb-2.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-mongodb-3.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-mongodb-4.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-mssql-commons-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-mssql-jdbc-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-mssql-jtds-1.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-mysql-5.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-mysql-6.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-mysql-8.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-mysql-commons-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-neo4j-4.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-netty-socketio-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-nutz-http-1.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-nutz-mvc-annotation-1.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-okhttp-3.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-okhttp-4.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-okhttp-common-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-play-2.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-postgresql-8.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-pulsar-2.2-2.7-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-pulsar-2.8.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-pulsar-common-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-quasar-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-rabbitmq-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-redisson-3.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-resttemplate-3.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-resttemplate-4.3.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-rocketMQ-5.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-rocketmq-3.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-rocketmq-4.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-rocketmq-client-java-5.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-servicecomb-java-chassis-2.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-sharding-sphere-3.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-sharding-sphere-4.1.0-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-shardingsphere-4.0.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-shardingsphere-5.0.0-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-solrj-7.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-spring-async-annotation-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-spring-cloud-feign-1.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-spring-cloud-feign-2.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-spring-concurrent-util-4.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-spring-core-patch-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-spring-kafka-1.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-spring-kafka-2.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-spring-scheduled-annotation-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-springmvc-annotation-3.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-springmvc-annotation-4.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-springmvc-annotation-5.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-springmvc-annotation-commons-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-spymemcached-2.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-struts2-2.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-tomcat-thread-pool-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-undertow-2.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-undertow-worker-thread-pool-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-vertx-core-3.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-vertx-core-4.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-xmemcached-2.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/apm-xxl-job-2.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/baidu-brpc-3.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/baidu-brpc-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/dbcp-2.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/druid-1.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/dubbo-2.7.x-conflict-patch-9.2.0.jar create mode 100644 skywalking/agent/plugins/dubbo-3.x-conflict-patch-9.2.0.jar create mode 100644 skywalking/agent/plugins/dubbo-conflict-patch-9.2.0.jar create mode 100644 skywalking/agent/plugins/elasticsearch-common-9.2.0.jar create mode 100644 skywalking/agent/plugins/graphql-12.x-15.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/graphql-16plus-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/graphql-8.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/graphql-9.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/jedis-2.x-3.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/jedis-4.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/jsonrpc4j-1.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/micronaut-http-client-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/micronaut-http-server-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/motan-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/nats-2.14.x-2.15.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/okhttp-2.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/resteasy-server-3.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/resteasy-server-4.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/resteasy-server-6.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/resttemplate-commons-9.2.0.jar create mode 100644 skywalking/agent/plugins/sofa-rpc-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/spring-commons-9.2.0.jar create mode 100644 skywalking/agent/plugins/spring-webflux-5.x-webclient-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/spring-webflux-6.x-webclient-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/thrift-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/tomcat-10x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/tomcat-7.x-8.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/plugins/websphere-liberty-23.x-plugin-9.2.0.jar create mode 100644 skywalking/agent/skywalking-agent.jar create mode 100644 skywalking/ins create mode 100644 skywalking/install/elasticsearch/elasticsearch.sh create mode 100644 skywalking/install/skywalking-net.sh create mode 100644 skywalking/install/skywalking/skywalking-oap.sh create mode 100644 skywalking/install/skywalking/skywalking-ui.sh create mode 100644 skywalking/show create mode 100644 xxl-job/job-admin/config/application.properties create mode 100644 xxl-job/job-admin/depXXLAdmin.sh create mode 100644 xxl-job/mysql/config/my.cnf create mode 100644 xxl-job/mysql/depXXLMysql.sh create mode 100644 xxl-job/show create mode 100644 xxl-job/xxl-net.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..872a7a6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,49 @@ +###################################################################### +# Build Tools + +.gradle +/build/ +!gradle/wrapper/gradle-wrapper.jar + +target/ +!.mvn/wrapper/maven-wrapper.jar + +out +###################################################################### +# IDE + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +*.yml + +### IntelliJ IDEA ### +.idea +logs +*.iws +*.iml +*.ipr + +### JRebel ### +rebel.xml +### NetBeans ### +nbproject/private/ +build/* +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ + +###################################################################### +# Others +*.log +*.xml.versionsBackup +*.swp + +!*/build/*.java +!*/build/*.html +!*/build/*.xml diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bd95df1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 若依 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..f64b4ef --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +## 系统模块 + +~~~ +com.muyu +├── cloud-ui // 前端框架 [80] +├── cloud-gateway // 网关模块 [8080] +├── cloud-auth // 认证中心 [9200] +├── cloud-common // 通用模块 +│ └── cloud-common-core // 核心模块 +│ └── cloud-common-datascope // 权限范围 +│ └── cloud-common-datasource // 多数据源 +│ └── cloud-common-log // 日志记录 +│ └── cloud-common-redis // 缓存服务 +│ └── cloud-common-seata // 分布式事务 +│ └── cloud-common-security // 安全模块 +│ └── cloud-common-swagger // 系统接口 +│ └── cloud-common-system // 系统基础 +├── cloud-modules // 业务模块 +│ └── cloud-system // 系统模块 [9201] +│ └── cloud-gen // 代码生成 [9202] +│ └── cloud-job // 定时任务 [9203] +│ └── cloud-file // 文件服务 [9300] +├── cloud-visual // 图形化管理模块 +│ └── cloud-visual-monitor // 监控中心 [9100] +├──pom.xml // 公共依赖 +~~~ diff --git a/cloud-auth/pom.xml b/cloud-auth/pom.xml new file mode 100644 index 0000000..0a76aae --- /dev/null +++ b/cloud-auth/pom.xml @@ -0,0 +1,80 @@ + + + com.muyu + cloud-server + 3.6.3 + + 4.0.0 + + cloud-auth + + + cloud-auth认证授权中心 + + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + com.muyu + cloud-common-security + + + + + com.muyu + cloud-common-api-doc + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + + diff --git a/cloud-auth/src/main/java/com/muyu/auth/CloudAuthApplication.java b/cloud-auth/src/main/java/com/muyu/auth/CloudAuthApplication.java new file mode 100644 index 0000000..e6ea4fd --- /dev/null +++ b/cloud-auth/src/main/java/com/muyu/auth/CloudAuthApplication.java @@ -0,0 +1,19 @@ +package com.muyu.auth; + +import com.muyu.common.security.annotation.EnableMyFeignClients; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; + +/** + * 认证授权中心 + * + * @author muyu + */ +@EnableMyFeignClients +@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) +public class CloudAuthApplication { + public static void main (String[] args) { + SpringApplication.run(CloudAuthApplication.class, args); + } +} diff --git a/cloud-auth/src/main/java/com/muyu/auth/controller/TokenController.java b/cloud-auth/src/main/java/com/muyu/auth/controller/TokenController.java new file mode 100644 index 0000000..d870762 --- /dev/null +++ b/cloud-auth/src/main/java/com/muyu/auth/controller/TokenController.java @@ -0,0 +1,72 @@ +package com.muyu.auth.controller; + +import com.muyu.auth.form.LoginBody; +import com.muyu.auth.form.RegisterBody; +import com.muyu.auth.service.SysLoginService; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.utils.JwtUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.security.auth.AuthUtil; +import com.muyu.common.security.service.TokenService; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.LoginUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.servlet.http.HttpServletRequest; + +/** + * token 控制 + * + * @author muyu + */ +@RestController +public class TokenController { + @Autowired + private TokenService tokenService; + + @Autowired + private SysLoginService sysLoginService; + + @PostMapping("login") + public Result login (@RequestBody LoginBody form) { + // 用户登录 + LoginUser userInfo = sysLoginService.login(form.getUsername(), form.getPassword()); + // 获取登录token + return Result.success(tokenService.createToken(userInfo)); + } + + @DeleteMapping("logout") + public Result logout (HttpServletRequest request) { + String token = SecurityUtils.getToken(request); + if (StringUtils.isNotEmpty(token)) { + String username = JwtUtils.getUserName(token); + // 删除用户缓存记录 + AuthUtil.logoutByToken(token); + // 记录用户退出日志 + sysLoginService.logout(username); + } + return Result.success(); + } + + @PostMapping("refresh") + public Result refresh (HttpServletRequest request) { + LoginUser loginUser = tokenService.getLoginUser(request); + if (StringUtils.isNotNull(loginUser)) { + // 刷新令牌有效期 + tokenService.refreshToken(loginUser); + return Result.success(); + } + return Result.success(); + } + + @PostMapping("register") + public Result register (@RequestBody RegisterBody registerBody) { + // 用户注册 + sysLoginService.register(registerBody.getUsername(), registerBody.getPassword()); + return Result.success(); + } +} diff --git a/cloud-auth/src/main/java/com/muyu/auth/form/LoginBody.java b/cloud-auth/src/main/java/com/muyu/auth/form/LoginBody.java new file mode 100644 index 0000000..354122e --- /dev/null +++ b/cloud-auth/src/main/java/com/muyu/auth/form/LoginBody.java @@ -0,0 +1,35 @@ +package com.muyu.auth.form; + +/** + * 用户登录对象 + * + * @author muyu + */ +public class LoginBody { + /** + * 用户名 + */ + private String username; + + /** + * 用户密码 + */ + private String password; + + + 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; + } +} diff --git a/cloud-auth/src/main/java/com/muyu/auth/form/RegisterBody.java b/cloud-auth/src/main/java/com/muyu/auth/form/RegisterBody.java new file mode 100644 index 0000000..c2a4d5a --- /dev/null +++ b/cloud-auth/src/main/java/com/muyu/auth/form/RegisterBody.java @@ -0,0 +1,10 @@ +package com.muyu.auth.form; + +/** + * 用户注册对象 + * + * @author muyu + */ +public class RegisterBody extends LoginBody { + +} diff --git a/cloud-auth/src/main/java/com/muyu/auth/service/SysLoginService.java b/cloud-auth/src/main/java/com/muyu/auth/service/SysLoginService.java new file mode 100644 index 0000000..ff0af1a --- /dev/null +++ b/cloud-auth/src/main/java/com/muyu/auth/service/SysLoginService.java @@ -0,0 +1,127 @@ +package com.muyu.auth.service; + +import com.muyu.common.core.constant.CacheConstants; +import com.muyu.common.core.constant.Constants; +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.core.constant.UserConstants; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.enums.UserStatus; +import com.muyu.common.core.exception.ServiceException; +import com.muyu.common.core.text.Convert; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.utils.ip.IpUtils; +import com.muyu.common.redis.service.RedisService; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.remote.RemoteUserService; +import com.muyu.common.system.domain.SysUser; +import com.muyu.common.system.domain.LoginUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 登录校验方法 + * + * @author muyu + */ +@Component +public class SysLoginService { + @Autowired + private RemoteUserService remoteUserService; + + @Autowired + private SysPasswordService passwordService; + + @Autowired + private SysRecordLogService recordLogService; + + @Autowired + private RedisService redisService; + + /** + * 登录 + */ + public LoginUser login (String username, String password) { + // 用户名或密码为空 错误 + if (StringUtils.isAnyBlank(username, password)) { + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户/密码必须填写"); + throw new ServiceException("用户/密码必须填写"); + } + // 密码如果不在指定范围内 错误 + if (password.length() < UserConstants.PASSWORD_MIN_LENGTH + || password.length() > UserConstants.PASSWORD_MAX_LENGTH) { + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户密码不在指定范围"); + throw new ServiceException("用户密码不在指定范围"); + } + // 用户名不在指定范围内 错误 + if (username.length() < UserConstants.USERNAME_MIN_LENGTH + || username.length() > UserConstants.USERNAME_MAX_LENGTH) { + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户名不在指定范围"); + throw new ServiceException("用户名不在指定范围"); + } + // IP黑名单校验 + String blackStr = Convert.toStr(redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST)); + if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) { + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "很遗憾,访问IP已被列入系统黑名单"); + throw new ServiceException("很遗憾,访问IP已被列入系统黑名单"); + } + // 查询用户信息 + Result userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER); + + if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData())) { + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "登录用户不存在"); + throw new ServiceException("登录用户:" + username + " 不存在"); + } + + if (Result.FAIL == userResult.getCode()) { + throw new ServiceException(userResult.getMsg()); + } + + LoginUser userInfo = userResult.getData(); + SysUser user = userResult.getData().getSysUser(); + if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) { + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "对不起,您的账号已被删除"); + throw new ServiceException("对不起,您的账号:" + username + " 已被删除"); + } + if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户已停用,请联系管理员"); + throw new ServiceException("对不起,您的账号:" + username + " 已停用"); + } + passwordService.validate(user, password); + recordLogService.recordLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功"); + return userInfo; + } + + public void logout (String loginName) { + recordLogService.recordLogininfor(loginName, Constants.LOGOUT, "退出成功"); + } + + /** + * 注册 + */ + public void register (String username, String password) { + // 用户名或密码为空 错误 + if (StringUtils.isAnyBlank(username, password)) { + throw new ServiceException("用户/密码必须填写"); + } + if (username.length() < UserConstants.USERNAME_MIN_LENGTH + || username.length() > UserConstants.USERNAME_MAX_LENGTH) { + throw new ServiceException("账户长度必须在2到20个字符之间"); + } + if (password.length() < UserConstants.PASSWORD_MIN_LENGTH + || password.length() > UserConstants.PASSWORD_MAX_LENGTH) { + throw new ServiceException("密码长度必须在5到20个字符之间"); + } + + // 注册用户信息 + SysUser sysUser = new SysUser(); + sysUser.setUserName(username); + sysUser.setNickName(username); + sysUser.setPassword(SecurityUtils.encryptPassword(password)); + Result registerResult = remoteUserService.registerUserInfo(sysUser, SecurityConstants.INNER); + + if (Result.FAIL == registerResult.getCode()) { + throw new ServiceException(registerResult.getMsg()); + } + recordLogService.recordLogininfor(username, Constants.REGISTER, "注册成功"); + } +} diff --git a/cloud-auth/src/main/java/com/muyu/auth/service/SysPasswordService.java b/cloud-auth/src/main/java/com/muyu/auth/service/SysPasswordService.java new file mode 100644 index 0000000..e743d92 --- /dev/null +++ b/cloud-auth/src/main/java/com/muyu/auth/service/SysPasswordService.java @@ -0,0 +1,76 @@ +package com.muyu.auth.service; + +import com.muyu.common.core.constant.CacheConstants; +import com.muyu.common.core.constant.Constants; +import com.muyu.common.core.exception.ServiceException; +import com.muyu.common.redis.service.RedisService; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.SysUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +/** + * 登录密码方法 + * + * @author muyu + */ +@Component +public class SysPasswordService { + @Autowired + private RedisService redisService; + + private final int maxRetryCount = CacheConstants.PASSWORD_MAX_RETRY_COUNT; + + private final Long lockTime = CacheConstants.PASSWORD_LOCK_TIME; + + @Autowired + private SysRecordLogService recordLogService; + + /** + * 登录账户密码错误次数缓存键名 + * + * @param username 用户名 + * + * @return 缓存键key + */ + private String getCacheKey (String username) { + return CacheConstants.PWD_ERR_CNT_KEY + username; + } + + public void validate (SysUser user, String password) { + String username = user.getUserName(); + + Integer retryCount = redisService.getCacheObject(getCacheKey(username)); + + if (retryCount == null) { + retryCount = 0; + } + + if (retryCount >= Integer.valueOf(maxRetryCount).intValue()) { + String errMsg = String.format("密码输入错误%s次,帐户锁定%s分钟", maxRetryCount, lockTime); + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, errMsg); + throw new ServiceException(errMsg); + } + + if (!matches(user, password)) { + retryCount = retryCount + 1; + recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, String.format("密码输入错误%s次", retryCount)); + redisService.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES); + throw new ServiceException("用户不存在/密码错误"); + } else { + clearLoginRecordCache(username); + } + } + + public boolean matches (SysUser user, String rawPassword) { + return SecurityUtils.matchesPassword(rawPassword, user.getPassword()); + } + + public void clearLoginRecordCache (String loginName) { + if (redisService.hasKey(getCacheKey(loginName))) { + redisService.deleteObject(getCacheKey(loginName)); + } + } +} diff --git a/cloud-auth/src/main/java/com/muyu/auth/service/SysRecordLogService.java b/cloud-auth/src/main/java/com/muyu/auth/service/SysRecordLogService.java new file mode 100644 index 0000000..2d4de80 --- /dev/null +++ b/cloud-auth/src/main/java/com/muyu/auth/service/SysRecordLogService.java @@ -0,0 +1,44 @@ +package com.muyu.auth.service; + +import com.muyu.common.core.constant.Constants; +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.utils.ip.IpUtils; +import com.muyu.common.system.remote.RemoteLogService; +import com.muyu.common.system.domain.SysLogininfor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 记录日志方法 + * + * @author muyu + */ +@Component +public class SysRecordLogService { + @Autowired + private RemoteLogService remoteLogService; + + /** + * 记录登录信息 + * + * @param username 用户名 + * @param status 状态 + * @param message 消息内容 + * + * @return + */ + public void recordLogininfor (String username, String status, String message) { + SysLogininfor logininfor = new SysLogininfor(); + logininfor.setUserName(username); + logininfor.setIpaddr(IpUtils.getIpAddr()); + logininfor.setMsg(message); + // 日志状态 + if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) { + logininfor.setStatus(Constants.LOGIN_SUCCESS_STATUS); + } else if (Constants.LOGIN_FAIL.equals(status)) { + logininfor.setStatus(Constants.LOGIN_FAIL_STATUS); + } + remoteLogService.saveLogininfor(logininfor, SecurityConstants.INNER); + } +} diff --git a/cloud-auth/src/main/resources/banner.txt b/cloud-auth/src/main/resources/banner.txt new file mode 100644 index 0000000..0dd5eee --- /dev/null +++ b/cloud-auth/src/main/resources/banner.txt @@ -0,0 +1,2 @@ +Spring Boot Version: ${spring-boot.version} +Spring Application Name: ${spring.application.name} diff --git a/cloud-auth/src/main/resources/logback/dev.xml b/cloud-auth/src/main/resources/logback/dev.xml new file mode 100644 index 0000000..880c02d --- /dev/null +++ b/cloud-auth/src/main/resources/logback/dev.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + + + diff --git a/cloud-auth/src/main/resources/logback/prod.xml b/cloud-auth/src/main/resources/logback/prod.xml new file mode 100644 index 0000000..d05b780 --- /dev/null +++ b/cloud-auth/src/main/resources/logback/prod.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + ${log.sky.pattern} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cloud-auth/src/main/resources/logback/test.xml b/cloud-auth/src/main/resources/logback/test.xml new file mode 100644 index 0000000..d05b780 --- /dev/null +++ b/cloud-auth/src/main/resources/logback/test.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + ${log.sky.pattern} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cloud-common/cloud-common-api-doc/pom.xml b/cloud-common/cloud-common-api-doc/pom.xml new file mode 100644 index 0000000..261e760 --- /dev/null +++ b/cloud-common/cloud-common-api-doc/pom.xml @@ -0,0 +1,33 @@ + + + + com.muyu + cloud-common + 3.6.3 + + 4.0.0 + + cloud-common-api-doc + + + cloud-common-api-doc系统接口 + + + + + + + org.springframework.boot + spring-boot-starter-web + + + + + com.github.xiaoymin + knife4j-openapi3-jakarta-spring-boot-starter + + + + diff --git a/cloud-common/cloud-common-api-doc/src/main/java/com/muyu/common/api/doc/config/SpringDocConfig.java b/cloud-common/cloud-common-api-doc/src/main/java/com/muyu/common/api/doc/config/SpringDocConfig.java new file mode 100644 index 0000000..08d7bff --- /dev/null +++ b/cloud-common/cloud-common-api-doc/src/main/java/com/muyu/common/api/doc/config/SpringDocConfig.java @@ -0,0 +1,34 @@ +package com.muyu.common.api.doc.config; + +import io.swagger.v3.oas.models.ExternalDocumentation; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SpringDocConfig { + + @Value("${spring.application.name}") + private String applicationName; + + private Info info(){ + return new Info() + .title(String.format("%S-微服务接口文档", applicationName)) + .description("微服务接口文档,根据此接口文档可以进行前后端功能对接/联调") + .version("v1.0.0"); + } + private ExternalDocumentation externalDocumentation() { + return new ExternalDocumentation() + .description("服务总站") + .url("https://gitea.qinmian.online"); + } + + @Bean + public OpenAPI springShopOpenAPI() { + return new OpenAPI() + .info(info()) + .externalDocs(externalDocumentation()); + } +} diff --git a/cloud-common/cloud-common-api-doc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/cloud-common/cloud-common-api-doc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..4b1b17b --- /dev/null +++ b/cloud-common/cloud-common-api-doc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.muyu.common.api.doc.config.SpringDocConfig \ No newline at end of file diff --git a/cloud-common/cloud-common-core/pom.xml b/cloud-common/cloud-common-core/pom.xml new file mode 100644 index 0000000..57cf128 --- /dev/null +++ b/cloud-common/cloud-common-core/pom.xml @@ -0,0 +1,169 @@ + + + + com.muyu + cloud-common + 3.6.3 + + 4.0.0 + + cloud-common-core + + + cloud-common-core核心模块 + + + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + + + org.springframework + spring-context-support + + + + + org.springframework + spring-web + + + + + com.alibaba + transmittable-thread-local + + + + + com.github.pagehelper + pagehelper-spring-boot-starter + + + + + com.baomidou + mybatis-plus-spring-boot3-starter + 3.5.6 + + + com.github.jsqlparser + jsqlparser + + + org.mybatis + mybatis + + + + + + org.mybatis + mybatis + 3.5.16 + + + + + org.springframework.boot + spring-boot-starter-validation + + + + + com.fasterxml.jackson.core + jackson-databind + + + + + com.alibaba.fastjson2 + fastjson2 + + + + + io.jsonwebtoken + jjwt + + + + + org.apache.commons + commons-lang3 + + + + + commons-io + commons-io + + + + + org.apache.poi + poi-ooxml + + + + + + jakarta.servlet + jakarta.servlet-api + + + + + org.springframework.boot + spring-boot-starter-aop + + + + + + + + + + javax.annotation + javax.annotation-api + + + + + org.projectlombok + lombok + + + + + org.apache.skywalking + apm-toolkit-trace + + + + + org.apache.skywalking + apm-toolkit-logback-1.x + + + + cn.hutool + hutool-all + + + + diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/annotation/Excel.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/annotation/Excel.java new file mode 100644 index 0000000..ef99dd5 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/annotation/Excel.java @@ -0,0 +1,176 @@ +package com.muyu.common.core.annotation; + +import com.muyu.common.core.utils.poi.ExcelHandlerAdapter; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.IndexedColors; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.math.BigDecimal; + +/** + * 自定义导出Excel数据注解 + * + * @author muyu + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Excel { + /** + * 导出时在excel中排序 + */ + int sort() default Integer.MAX_VALUE; + + /** + * 导出到Excel中的名字. + */ + String name() default ""; + + /** + * 日期格式, 如: yyyy-MM-dd + */ + String dateFormat() default ""; + + /** + * 读取内容转表达式 (如: 0=男,1=女,2=未知) + */ + String readConverterExp() default ""; + + /** + * 分隔符,读取字符串组内容 + */ + String separator() default ","; + + /** + * BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化) + */ + int scale() default -1; + + /** + * BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN + */ + int roundingMode() default BigDecimal.ROUND_HALF_EVEN; + + /** + * 导出时在excel中每个列的高度 + */ + double height() default 14; + + /** + * 导出时在excel中每个列的宽度 + */ + double width() default 16; + + /** + * 文字后缀,如% 90 变成90% + */ + String suffix() default ""; + + /** + * 当值为空时,字段的默认值 + */ + String defaultValue() default ""; + + /** + * 提示信息 + */ + String prompt() default ""; + + /** + * 设置只能选择不能输入的列内容. + */ + String[] combo() default {}; + + /** + * 是否需要纵向合并单元格,应对需求:含有list集合单元格) + */ + boolean needMerge() default false; + + /** + * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写. + */ + boolean isExport() default true; + + /** + * 另一个类中的属性名称,支持多级获取,以小数点隔开 + */ + String targetAttr() default ""; + + /** + * 是否自动统计数据,在最后追加一行统计数据总和 + */ + boolean isStatistics() default false; + + /** + * 导出类型(0数字 1字符串) + */ + ColumnType cellType() default ColumnType.STRING; + + /** + * 导出列头背景颜色 + */ + IndexedColors headerBackgroundColor() default IndexedColors.GREY_50_PERCENT; + + /** + * 导出列头字体颜色 + */ + IndexedColors headerColor() default IndexedColors.WHITE; + + /** + * 导出单元格背景颜色 + */ + IndexedColors backgroundColor() default IndexedColors.WHITE; + + /** + * 导出单元格字体颜色 + */ + IndexedColors color() default IndexedColors.BLACK; + + /** + * 导出字段对齐方式 + */ + HorizontalAlignment align() default HorizontalAlignment.CENTER; + + /** + * 自定义数据处理器 + */ + Class handler() default ExcelHandlerAdapter.class; + + /** + * 自定义数据处理器参数 + */ + String[] args() default {}; + + /** + * 字段类型(0:导出导入;1:仅导出;2:仅导入) + */ + Type type () default Type.ALL; + + enum Type { + ALL(0), EXPORT(1), IMPORT(2); + private final int value; + + Type (int value) { + this.value = value; + } + + public int value () { + return this.value; + } + } + + enum ColumnType { + NUMERIC(0), STRING(1), IMAGE(2); + private final int value; + + ColumnType (int value) { + this.value = value; + } + + public int value () { + return this.value; + } + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/annotation/Excels.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/annotation/Excels.java new file mode 100644 index 0000000..f8fc165 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/annotation/Excels.java @@ -0,0 +1,17 @@ +package com.muyu.common.core.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Excel注解集 + * + * @author muyu + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Excels { + Excel[] value (); +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/CacheConstants.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/CacheConstants.java new file mode 100644 index 0000000..532c9f2 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/CacheConstants.java @@ -0,0 +1,58 @@ +package com.muyu.common.core.constant; + +/** + * 缓存常量信息 + * + * @author muyu + */ +public class CacheConstants { + /** + * 缓存有效期,默认720(分钟) + */ + public final static long EXPIRATION = 720; + + /** + * 缓存刷新时间,默认120(分钟) + */ + public final static long REFRESH_TIME = 120; + + /** + * 密码最大错误次数 + */ + public final static int PASSWORD_MAX_RETRY_COUNT = 5; + + /** + * 密码锁定时间,默认10(分钟) + */ + public final static long PASSWORD_LOCK_TIME = 10; + + /** + * 权限缓存前缀 + */ + public final static 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 PWD_ERR_CNT_KEY = "pwd_err_cnt:"; + + /** + * 登录IP黑名单 cache key + */ + public static final String SYS_LOGIN_BLACKIPLIST = SYS_CONFIG_KEY + "sys.login.blackIPList"; +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/Constants.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/Constants.java new file mode 100644 index 0000000..a3540fc --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/Constants.java @@ -0,0 +1,134 @@ +package com.muyu.common.core.constant; + +/** + * 通用常量信息 + * + * @author muyu + */ +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 = "0"; + + /** + * 登录失败状态 + */ + public static final String LOGIN_FAIL_STATUS = "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 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"; + + /** + * 自动识别json对象白名单配置(仅允许解析的包名,范围越小越安全) + */ + public static final String[] JSON_WHITELIST_STR = {"org.springframework", "com.muyu"}; + + /** + * 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加) + */ + public static final String[] JOB_WHITELIST_STR = {"com.muyu"}; + + /** + * 定时任务违规的字符 + */ + public static final String[] JOB_ERROR_STR = {"java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml", + "org.springframework", "org.apache", "com.muyu.common.core.utils.file"}; +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/GenConstants.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/GenConstants.java new file mode 100644 index 0000000..fde9967 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/GenConstants.java @@ -0,0 +1,186 @@ +package com.muyu.common.core.constant; + +/** + * 代码生成通用常量 + * + * @author muyu + */ +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", + "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"}; + + /** + * 文本框 + */ + 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/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/HttpStatus.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/HttpStatus.java new file mode 100644 index 0000000..36e0783 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/HttpStatus.java @@ -0,0 +1,93 @@ +package com.muyu.common.core.constant; + +/** + * 返回状态码 + * + * @author muyu + */ +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/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/ScheduleConstants.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/ScheduleConstants.java new file mode 100644 index 0000000..368317a --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/ScheduleConstants.java @@ -0,0 +1,56 @@ +package com.muyu.common.core.constant; + +/** + * 任务调度通用常量 + * + * @author muyu + */ +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 final String value; + + Status(String value) { + this.value = value; + } + + public String getValue () { + return value; + } + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/SecurityConstants.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/SecurityConstants.java new file mode 100644 index 0000000..0e9b22d --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/SecurityConstants.java @@ -0,0 +1,53 @@ +package com.muyu.common.core.constant; + +/** + * 权限相关通用常量 + * + * @author muyu + */ +public class SecurityConstants { + /** + * 用户ID字段 + */ + public static final String DETAILS_USER_ID = "user_id"; + + /** + * 用户名字段 + */ + public static final String DETAILS_USERNAME = "username"; + + /** + * 授权信息字段 + */ + public static final String AUTHORIZATION_HEADER = "authorization"; + + /** + * 请求来源 + */ + public static final String FROM_SOURCE = "from-source"; + + /** + * 内部请求 + */ + public static final String INNER = "inner"; + + /** + * 用户标识 + */ + public static final String USER_KEY = "user_key"; + + /** + * 登录用户 + */ + public static final String LOGIN_USER = "login_user"; + + /** + * 角色权限 + */ + public static final String ROLE_PERMISSION = "role_permission"; + + /** + * SAAS请求头的key + */ + public static final String SAAS_KEY = "ent-code"; +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/ServiceNameConstants.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/ServiceNameConstants.java new file mode 100644 index 0000000..304711c --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/ServiceNameConstants.java @@ -0,0 +1,33 @@ +package com.muyu.common.core.constant; + +/** + * 服务名称 + * + * @author muyu + */ +public class ServiceNameConstants { + /** + * 认证服务的serviceid + */ + public static final String AUTH_SERVICE = "cloud-auth"; + + /** + * 系统模块的serviceid + */ + public static final String SYSTEM_SERVICE = "cloud-system"; + + /** + * 文件服务的serviceid + */ + public static final String FILE_SERVICE = "cloud-file"; + + /** + * 智能车联服务 + */ + public static final String SMART_SERVICE = "cloud-smart-car"; + + + public static final String SAAS_SERVICE = "cloud-saas"; + + +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/TokenConstants.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/TokenConstants.java new file mode 100644 index 0000000..38abd57 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/TokenConstants.java @@ -0,0 +1,24 @@ +package com.muyu.common.core.constant; + +/** + * Token的Key常量 + * + * @author muyu + */ +public class TokenConstants { + /** + * 令牌自定义标识 + */ + public static final String AUTHENTICATION = "Authorization"; + + /** + * 令牌前缀 + */ + public static final String PREFIX = "Bearer "; + + /** + * 令牌秘钥 + */ + public final static String SECRET = "abcdefghijklmnsalieopadfaqawefwerstuvwxyz"; + +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/UserConstants.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/UserConstants.java new file mode 100644 index 0000000..0df401c --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/constant/UserConstants.java @@ -0,0 +1,113 @@ +package com.muyu.common.core.constant; + +/** + * 用户常量信息 + * + * @author muyu + */ +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/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/context/SecurityContextHolder.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/context/SecurityContextHolder.java new file mode 100644 index 0000000..80ea42b --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/context/SecurityContextHolder.java @@ -0,0 +1,83 @@ +package com.muyu.common.core.context; + +import com.alibaba.ttl.TransmittableThreadLocal; +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.core.text.Convert; +import com.muyu.common.core.utils.StringUtils; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 获取当前线程变量中的 用户id、用户名称、Token等信息 + * 注意: 必须在网关通过请求头的方法传入,同时在HeaderInterceptor拦截器设置值。 否则这里无法获取 + * + * @author muyu + */ +public class SecurityContextHolder { + private static final TransmittableThreadLocal> THREAD_LOCAL = new TransmittableThreadLocal<>(); + + public static void set (String key, Object value) { + Map map = getLocalMap(); + map.put(key, value == null ? StringUtils.EMPTY : value); + } + + public static String get (String key) { + Map map = getLocalMap(); + return Convert.toStr(map.getOrDefault(key, StringUtils.EMPTY)); + } + + public static T get (String key, Class clazz) { + Map map = getLocalMap(); + return StringUtils.cast(map.getOrDefault(key, null)); + } + + public static Map getLocalMap () { + Map map = THREAD_LOCAL.get(); + if (map == null) { + map = new ConcurrentHashMap(); + THREAD_LOCAL.set(map); + } + return map; + } + + public static void setLocalMap (Map threadLocalMap) { + THREAD_LOCAL.set(threadLocalMap); + } + + public static Long getUserId () { + return Convert.toLong(get(SecurityConstants.DETAILS_USER_ID), 0L); + } + + public static void setUserId (String account) { + set(SecurityConstants.DETAILS_USER_ID, account); + } + + public static String getUserName () { + return get(SecurityConstants.DETAILS_USERNAME); + } + + public static void setUserName (String username) { + set(SecurityConstants.DETAILS_USERNAME, username); + } + + public static String getUserKey () { + return get(SecurityConstants.USER_KEY); + } + + public static void setUserKey (String userKey) { + set(SecurityConstants.USER_KEY, userKey); + } + + public static String getPermission () { + return get(SecurityConstants.ROLE_PERMISSION); + } + + public static void setPermission (String permissions) { + set(SecurityConstants.ROLE_PERMISSION, permissions); + } + + public static void remove () { + THREAD_LOCAL.remove(); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/domain/Result.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/domain/Result.java new file mode 100644 index 0000000..70faa40 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/domain/Result.java @@ -0,0 +1,112 @@ +package com.muyu.common.core.domain; + +import com.muyu.common.core.constant.Constants; +import com.muyu.common.core.constant.HttpStatus; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 响应信息主体 + * + * @author muyu + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Result implements Serializable { + /** + * 成功 + */ + public static final int SUCCESS = Constants.SUCCESS; + /** + * 失败 + */ + public static final int FAIL = Constants.FAIL; + /** + * 警告 + */ + public static final int WARN = HttpStatus.WARN; + + private static final long serialVersionUID = 1L; + private int code; + + private String msg; + + private T data; + + public static Result success () { + return restResult(null, SUCCESS, null); + } + + public static Result success (T data) { + return restResult(data, SUCCESS, null); + } + + public static Result success (T data, String msg) { + return restResult(data, SUCCESS, msg); + } + + public static Result error () { + return restResult(null, FAIL, null); + } + + public static Result error (String msg) { + return restResult(null, FAIL, msg); + } + + public static Result error (T data) { + return restResult(data, FAIL, null); + } + + public static Result error (T data, String msg) { + return restResult(data, FAIL, msg); + } + + public static Result error (int code, String msg) { + return restResult(null, code, msg); + } + + + + public static Result warn () { + return restResult(null, WARN, null); + } + + public static Result warn (String msg) { + return restResult(null, WARN, msg); + } + + public static Result warn (T data) { + return restResult(data, WARN, null); + } + + public static Result warn (T data, String msg) { + return restResult(data, WARN, msg); + } + + public static Result warn (int code, String msg) { + return restResult(null, code, msg); + } + + private static Result restResult (T data, int code, String msg) { + return Result.builder() + .code(code) + .data(data) + .msg(msg) + .build(); + } + + public static Boolean isError (Result ret) { + return !isSuccess(ret); + } + + public static Boolean isSuccess (Result ret) { + return Result.SUCCESS == ret.getCode(); + } + +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/enums/UserStatus.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/enums/UserStatus.java new file mode 100644 index 0000000..32ff39a --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/enums/UserStatus.java @@ -0,0 +1,26 @@ +package com.muyu.common.core.enums; + +/** + * 用户状态 + * + * @author muyu + */ +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/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/CaptchaException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/CaptchaException.java new file mode 100644 index 0000000..eb32d0b --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/CaptchaException.java @@ -0,0 +1,14 @@ +package com.muyu.common.core.exception; + +/** + * 验证码错误异常类 + * + * @author muyu + */ +public class CaptchaException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public CaptchaException (String msg) { + super(msg); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/CheckedException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/CheckedException.java new file mode 100644 index 0000000..4f12893 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/CheckedException.java @@ -0,0 +1,26 @@ +package com.muyu.common.core.exception; + +/** + * 检查异常 + * + * @author muyu + */ +public class CheckedException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public CheckedException (String message) { + super(message); + } + + public CheckedException (Throwable cause) { + super(cause); + } + + public CheckedException (String message, Throwable cause) { + super(message, cause); + } + + public CheckedException (String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/DemoModeException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/DemoModeException.java new file mode 100644 index 0000000..82249cf --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/DemoModeException.java @@ -0,0 +1,13 @@ +package com.muyu.common.core.exception; + +/** + * 演示模式异常 + * + * @author muyu + */ +public class DemoModeException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public DemoModeException () { + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/GlobalException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/GlobalException.java new file mode 100644 index 0000000..b14e03c --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/GlobalException.java @@ -0,0 +1,51 @@ +package com.muyu.common.core.exception; + +/** + * 全局异常 + * + * @author muyu + */ +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; + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/InnerAuthException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/InnerAuthException.java new file mode 100644 index 0000000..f211c7f --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/InnerAuthException.java @@ -0,0 +1,14 @@ +package com.muyu.common.core.exception; + +/** + * 内部认证异常 + * + * @author muyu + */ +public class InnerAuthException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public InnerAuthException (String message) { + super(message); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/PreAuthorizeException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/PreAuthorizeException.java new file mode 100644 index 0000000..6cb8636 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/PreAuthorizeException.java @@ -0,0 +1,13 @@ +package com.muyu.common.core.exception; + +/** + * 权限异常 + * + * @author muyu + */ +public class PreAuthorizeException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public PreAuthorizeException () { + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/ServiceException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/ServiceException.java new file mode 100644 index 0000000..9c86969 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/ServiceException.java @@ -0,0 +1,65 @@ +package com.muyu.common.core.exception; + +/** + * 业务异常 + * + * @author muyu + */ +public class ServiceException extends RuntimeException { + private static final long serialVersionUID = 1L; + + /** + * 错误码 + */ + private Integer code; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + *

+ * 和 {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; + } + + public ServiceException setDetailMessage (String detailMessage) { + this.detailMessage = detailMessage; + return this; + } + + @Override + public String getMessage () { + return message; + } + + public ServiceException setMessage (String message) { + this.message = message; + return this; + } + + public Integer getCode () { + return code; + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/UtilException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/UtilException.java new file mode 100644 index 0000000..8de4bbf --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/UtilException.java @@ -0,0 +1,22 @@ +package com.muyu.common.core.exception; + +/** + * 工具类异常 + * + * @author muyu + */ +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/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/auth/NotLoginException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/auth/NotLoginException.java new file mode 100644 index 0000000..40293bf --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/auth/NotLoginException.java @@ -0,0 +1,14 @@ +package com.muyu.common.core.exception.auth; + +/** + * 未能通过的登录认证异常 + * + * @author muyu + */ +public class NotLoginException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public NotLoginException (String message) { + super(message); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/auth/NotPermissionException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/auth/NotPermissionException.java new file mode 100644 index 0000000..e464840 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/auth/NotPermissionException.java @@ -0,0 +1,20 @@ +package com.muyu.common.core.exception.auth; + +import org.apache.commons.lang3.StringUtils; + +/** + * 未能通过的权限认证异常 + * + * @author muyu + */ +public class NotPermissionException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public NotPermissionException (String permission) { + super(permission); + } + + public NotPermissionException (String[] permissions) { + super(StringUtils.join(permissions, ",")); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/auth/NotRoleException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/auth/NotRoleException.java new file mode 100644 index 0000000..53a1522 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/auth/NotRoleException.java @@ -0,0 +1,20 @@ +package com.muyu.common.core.exception.auth; + +import org.apache.commons.lang3.StringUtils; + +/** + * 未能通过的角色认证异常 + * + * @author muyu + */ +public class NotRoleException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public NotRoleException (String role) { + super(role); + } + + public NotRoleException (String[] roles) { + super(StringUtils.join(roles, ",")); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/base/BaseException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/base/BaseException.java new file mode 100644 index 0000000..ecf55f7 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/base/BaseException.java @@ -0,0 +1,69 @@ +package com.muyu.common.core.exception.base; + +/** + * 基础异常 + * + * @author muyu + */ +public class BaseException extends RuntimeException { + private static final long serialVersionUID = 1L; + + /** + * 所属模块 + */ + private final String module; + + /** + * 错误码 + */ + private final String code; + + /** + * 错误码对应的参数 + */ + private final Object[] args; + + /** + * 错误消息 + */ + private final 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); + } + + public String getModule () { + return module; + } + + public String getCode () { + return code; + } + + public Object[] getArgs () { + return args; + } + + public String getDefaultMessage () { + return defaultMessage; + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/FileException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/FileException.java new file mode 100644 index 0000000..ae2e184 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/FileException.java @@ -0,0 +1,17 @@ +package com.muyu.common.core.exception.file; + +import com.muyu.common.core.exception.base.BaseException; + +/** + * 文件信息异常类 + * + * @author muyu + */ +public class FileException extends BaseException { + private static final long serialVersionUID = 1L; + + public FileException (String code, Object[] args, String msg) { + super("file", code, args, msg); + } + +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/FileNameLengthLimitExceededException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/FileNameLengthLimitExceededException.java new file mode 100644 index 0000000..3a85df3 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/FileNameLengthLimitExceededException.java @@ -0,0 +1,14 @@ +package com.muyu.common.core.exception.file; + +/** + * 文件名称超长限制异常类 + * + * @author muyu + */ +public class FileNameLengthLimitExceededException extends FileException { + private static final long serialVersionUID = 1L; + + public FileNameLengthLimitExceededException (int defaultFileNameLength) { + super("upload.filename.exceed.length", new Object[]{defaultFileNameLength}, "the filename is too long"); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/FileSizeLimitExceededException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/FileSizeLimitExceededException.java new file mode 100644 index 0000000..7570be5 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/FileSizeLimitExceededException.java @@ -0,0 +1,14 @@ +package com.muyu.common.core.exception.file; + +/** + * 文件名大小限制异常类 + * + * @author muyu + */ +public class FileSizeLimitExceededException extends FileException { + private static final long serialVersionUID = 1L; + + public FileSizeLimitExceededException (long defaultMaxSize) { + super("upload.exceed.maxSize", new Object[]{defaultMaxSize}, "the filesize is too large"); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/FileUploadException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/FileUploadException.java new file mode 100644 index 0000000..94341ab --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/FileUploadException.java @@ -0,0 +1,52 @@ +package com.muyu.common.core.exception.file; + +import java.io.PrintStream; +import java.io.PrintWriter; + +/** + * 文件上传异常类 + * + * @author muyu + */ +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/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/InvalidExtensionException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/InvalidExtensionException.java new file mode 100644 index 0000000..125327c --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/file/InvalidExtensionException.java @@ -0,0 +1,67 @@ +package com.muyu.common.core.exception.file; + +import java.util.Arrays; + +/** + * 文件上传 误异常类 + * + * @author muyu + */ +public class InvalidExtensionException extends FileUploadException { + private static final long serialVersionUID = 1L; + + private final String[] allowedExtension; + private final String extension; + private final String filename; + + public InvalidExtensionException (String[] allowedExtension, String extension, String filename) { + super("filename : [" + filename + "], extension : [" + extension + "], allowed 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/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/job/TaskException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/job/TaskException.java new file mode 100644 index 0000000..e7e3661 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/job/TaskException.java @@ -0,0 +1,29 @@ +package com.muyu.common.core.exception.job; + +/** + * 计划策略异常 + * + * @author muyu + */ +public class TaskException extends Exception { + private static final long serialVersionUID = 1L; + + private final 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 + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/user/CaptchaExpireException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/user/CaptchaExpireException.java new file mode 100644 index 0000000..a95a57b --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/user/CaptchaExpireException.java @@ -0,0 +1,14 @@ +package com.muyu.common.core.exception.user; + +/** + * 验证码失效异常类 + * + * @author muyu + */ +public class CaptchaExpireException extends UserException { + private static final long serialVersionUID = 1L; + + public CaptchaExpireException () { + super("user.jcaptcha.expire", null); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/user/UserException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/user/UserException.java new file mode 100644 index 0000000..f113749 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/user/UserException.java @@ -0,0 +1,16 @@ +package com.muyu.common.core.exception.user; + +import com.muyu.common.core.exception.base.BaseException; + +/** + * 用户信息异常类 + * + * @author muyu + */ +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/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/user/UserPasswordNotMatchException.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/user/UserPasswordNotMatchException.java new file mode 100644 index 0000000..7615cda --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/exception/user/UserPasswordNotMatchException.java @@ -0,0 +1,14 @@ +package com.muyu.common.core.exception.user; + +/** + * 用户密码不正确或不符合规范异常类 + * + * @author muyu + */ +public class UserPasswordNotMatchException extends UserException { + private static final long serialVersionUID = 1L; + + public UserPasswordNotMatchException () { + super("user.password.not.match", null); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/feign/FeginConfig.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/feign/FeginConfig.java new file mode 100644 index 0000000..77a12fe --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/feign/FeginConfig.java @@ -0,0 +1,19 @@ +package com.muyu.common.core.feign; + +import feign.Contract; +import org.springframework.cloud.openfeign.support.SpringMvcContract; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class FeginConfig { +// @Bean +// public Contract feignConfiguration() { +// return new feign.Contract.Default(); +// } + + @Bean + public Contract feignConfiguration() { + return new SpringMvcContract(); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/text/CharsetKit.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/text/CharsetKit.java new file mode 100644 index 0000000..b1a5140 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/text/CharsetKit.java @@ -0,0 +1,94 @@ +package com.muyu.common.core.text; + +import com.muyu.common.core.utils.StringUtils; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +/** + * 字符集工具类 + * + * @author muyu + */ +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 = StandardCharsets.ISO_8859_1; + /** + * UTF-8 + */ + public static final Charset CHARSET_UTF_8 = StandardCharsets.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/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/text/Convert.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/text/Convert.java new file mode 100644 index 0000000..fb57cb9 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/text/Convert.java @@ -0,0 +1,903 @@ +package com.muyu.common.core.text; + +import com.muyu.common.core.utils.StringUtils; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.RoundingMode; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.text.NumberFormat; +import java.util.Set; + +/** + * 类型转换器 + * + * @author muyu + */ +public class Convert { + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @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(); + } + + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static String toStr (Object value) { + return toStr(value, null); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @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); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static Character toChar (Object value) { + return toChar(value, null); + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @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
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static Byte toByte (Object value) { + return toByte(value, null); + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @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
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static Short toShort (Object value) { + return toShort(value, null); + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @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
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static Number toNumber (Object value) { + return toNumber(value, null); + } + + /** + * 转换为int
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @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
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static Integer toInt (Object value) { + return toInt(value, null); + } + + /** + * 转换为Integer数组
+ * + * @param str 被转换的值 + * + * @return 结果 + */ + public static Integer[] toIntArray (String str) { + return toIntArray(",", str); + } + + /** + * 转换为Long数组
+ * + * @param str 被转换的值 + * + * @return 结果 + */ + public static Long[] toLongArray (String str) { + return toLongArray(",", str); + } + + /** + * 转换为Integer数组
+ * + * @param split 分隔符 + * @param str 被转换的值 + * + * @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数组
+ * + * @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数组
+ * + * @param str 被转换的值 + * + * @return 结果 + */ + public static String[] toStrArray (String str) { + return toStrArray(",", str); + } + + /** + * 转换为String数组
+ * + * @param split 分隔符 + * @param str 被转换的值 + * + * @return 结果 + */ + public static String[] toStrArray (String split, String str) { + return str.split(split); + } + + /** + * 转换为long
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @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
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static Long toLong (Object value) { + return toLong(value, null); + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @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
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static Double toDouble (Object value) { + return toDouble(value, null); + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @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
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static Float toFloat (Object value) { + return toFloat(value, null); + } + + /** + * 转换为boolean
+ * String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @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
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static Boolean toBool (Object value) { + return toBool(value, null); + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * + * @param clazz Enum的Class + * @param value 值 + * @param defaultValue 默认值 + * + * @return Enum + */ + public static > E toEnum (Class 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对象
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * + * @param clazz Enum的Class + * @param value 值 + * + * @return Enum + */ + public static > E toEnum (Class clazz, Object value) { + return toEnum(clazz, value, null); + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @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
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static BigInteger toBigInteger (Object value) { + return toBigInteger(value, null); + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @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
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * + * @return 结果 + */ + public static BigDecimal toBigDecimal (Object value) { + return toBigDecimal(value, null); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * + * @return 字符串 + */ + public static String utf8Str (Object obj) { + return str(obj, CharsetKit.CHARSET_UTF_8); + } + + /** + * 将对象转为字符串
+ * 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)); + } + + /** + * 将对象转为字符串
+ * 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[] || obj instanceof Byte[]) { + if (obj instanceof byte[]) { + return str((byte[]) obj, charset); + } else { + Byte[] bytes = (Byte[]) obj; + int length = bytes.length; + byte[] dest = new byte[length]; + for (int i = 0 ; i < length ; i++) { + dest[i] = bytes[i]; + } + return str(dest, 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 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 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); + } + } + return new String(c); + } + + /** + * 数字金额大写转换 先写个完整的然后将如零拾替换成零 + * + * @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++) { + // 优化double计算精度丢失问题 + BigDecimal nNum = new BigDecimal(n); + BigDecimal decimal = new BigDecimal(10); + BigDecimal scale = nNum.multiply(decimal).setScale(2, RoundingMode.HALF_EVEN); + double d = scale.doubleValue(); + s += (digit[(int) (Math.floor(d * 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/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/text/StrFormatter.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/text/StrFormatter.java new file mode 100644 index 0000000..0c07cf5 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/text/StrFormatter.java @@ -0,0 +1,77 @@ +package com.muyu.common.core.text; + +import com.muyu.common.core.utils.StringUtils; + +/** + * 字符串格式化 + * + * @author muyu + */ +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 = '}'; + + /** + * 格式化字符串
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @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/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/DateUtils.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/DateUtils.java new file mode 100644 index 0000000..d76255e --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/DateUtils.java @@ -0,0 +1,158 @@ +package com.muyu.common.core.utils; + +import org.apache.commons.lang3.time.DateFormatUtils; + +import java.lang.management.ManagementFactory; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.*; +import java.util.Date; + +/** + * 时间工具类 + * + * @author muyu + */ +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 final 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); + } + + /** + * 计算时间差 + * + * @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()); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/ExceptionUtil.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/ExceptionUtil.java new file mode 100644 index 0000000..e6abdf9 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/ExceptionUtil.java @@ -0,0 +1,35 @@ +package com.muyu.common.core.utils; + +import org.apache.commons.lang3.exception.ExceptionUtils; + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * 错误信息处理类。 + * + * @author muyu + */ +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/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/JwtUtils.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/JwtUtils.java new file mode 100644 index 0000000..c94e051 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/JwtUtils.java @@ -0,0 +1,176 @@ +package com.muyu.common.core.utils; + +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.core.constant.TokenConstants; +import com.muyu.common.core.text.Convert; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import io.jsonwebtoken.security.SecureDigestAlgorithm; + +import javax.crypto.SecretKey; +import java.util.Date; +import java.util.Map; + +/** + * Jwt工具类 + * + * @author muyu + */ +public class JwtUtils { + + /** + * 加密算法 + */ + private final static SecureDigestAlgorithm ALGORITHM = Jwts.SIG.HS256; + /** + * 私钥 / 生成签名的时候使用的秘钥secret,一般可以从本地配置文件中读取,切记这个秘钥不能外露,只在服务端使用,在任何场景都不应该流露出去。 + * 一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。 + * 应该大于等于 256位(长度32及以上的字符串),并且是随机的字符串 + */ + private final static String secret = TokenConstants.SECRET; + /** + * 秘钥实例 + */ + public static final SecretKey KEY = Keys.hmacShaKeyFor(secret.getBytes()); + /** + * jwt签发者 + */ + private final static String JWT_ISS = "MUYU"; + /** + * jwt主题 + */ + private final static String SUBJECT = "Peripherals"; + + + /** + * 从数据声明生成令牌 + * + * @param claims 数据声明 + * + * @return 令牌 + */ + public static String createToken (Map claims) { + return Jwts.builder() + // 设置头部信息header + .header().add("typ", "JWT").add("alg", "HS256").and() + // 设置自定义负载信息payload + .claims(claims) + // 签发时间 + .issuedAt(new Date()) + // 主题 + .subject(SUBJECT) + // 签发者 + .issuer(JWT_ISS) + // 签名 + .signWith(KEY, ALGORITHM) + .compact(); + } + + /** + * 从令牌中获取数据声明 + * + * @param token 令牌 + * + * @return 数据声明 + */ + public static Claims parseToken (String token) { + return Jwts.parser() + .verifyWith(KEY) + .build() + .parseSignedClaims(token) + .getPayload(); + } + + /** + * 根据令牌获取用户标识 + * + * @param token 令牌 + * + * @return 用户ID + */ + public static String getUserKey (String token) { + Claims claims = parseToken(token); + return getValue(claims, SecurityConstants.USER_KEY); + } + + /** + * 根据令牌获取用户标识 + * + * @param claims 身份信息 + * + * @return 用户ID + */ + public static String getUserKey (Claims claims) { + return getValue(claims, SecurityConstants.USER_KEY); + } + + /** + * 根据令牌获取用户ID + * + * @param token 令牌 + * + * @return 用户ID + */ + public static String getUserId (String token) { + Claims claims = parseToken(token); + return getValue(claims, SecurityConstants.DETAILS_USER_ID); + } + + /** + * 根据身份信息获取用户ID + * + * @param claims 身份信息 + * + * @return 用户ID + */ + public static String getUserId (Claims claims) { + return getValue(claims, SecurityConstants.DETAILS_USER_ID); + } + + /** + * 根据令牌获取用户名 + * + * @param token 令牌 + * + * @return 用户名 + */ + public static String getUserName (String token) { + Claims claims = parseToken(token); + return getValue(claims, SecurityConstants.DETAILS_USERNAME); + } + + /** + * 根据身份信息获取用户名 + * + * @param claims 身份信息 + * + * @return 用户名 + */ + public static String getUserName (Claims claims) { + return getValue(claims, SecurityConstants.DETAILS_USERNAME); + } + + /** + * 根据身份信息获取键值 + * + * @param claims 身份信息 + * @param key 键 + * + * @return 值 + */ + public static String getValue (Claims claims, String key) { + return Convert.toStr(claims.get(key), ""); + } + + /** + * 根据身份信息获取SAASKey + * + * @param claims 身份信息 + * + * @return saas_key + */ + public static String getSaasKey(Claims claims) { + return getValue(claims, SecurityConstants.SAAS_KEY); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/PageUtils.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/PageUtils.java new file mode 100644 index 0000000..d4b0554 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/PageUtils.java @@ -0,0 +1,32 @@ +package com.muyu.common.core.utils; + +import com.github.pagehelper.PageHelper; +import com.muyu.common.core.utils.sql.SqlUtil; +import com.muyu.common.core.web.page.PageDomain; +import com.muyu.common.core.web.page.TableSupport; + +/** + * 分页工具类 + * + * @author muyu + */ +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 clearPage () { + PageHelper.clearPage(); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/ServletUtils.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/ServletUtils.java new file mode 100644 index 0000000..65e67e7 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/ServletUtils.java @@ -0,0 +1,285 @@ +package com.muyu.common.core.utils; + +import com.alibaba.fastjson2.JSON; +import com.muyu.common.core.constant.Constants; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.text.Convert; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.util.LinkedCaseInsensitiveMap; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import reactor.core.publisher.Mono; + + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.*; + +/** + * 客户端工具类 + * + * @author muyu + */ +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 getParams (ServletRequest request) { + final Map map = request.getParameterMap(); + return Collections.unmodifiableMap(map); + } + + /** + * 获得所有请求参数 + * + * @param request 请求对象{@link ServletRequest} + * + * @return Map + */ + public static Map getParamMap (ServletRequest request) { + Map params = new HashMap<>(); + for (Map.Entry entry : getParams(request).entrySet()) { + params.put(entry.getKey(), StringUtils.join(entry.getValue(), ",")); + } + return params; + } + + /** + * 获取request + */ + public static HttpServletRequest getRequest () { + try { + return Objects.requireNonNull(getRequestAttributes()).getRequest(); + } catch (Exception e) { + return null; + } + } + + /** + * 获取response + */ + public static HttpServletResponse getResponse () { + try { + return Objects.requireNonNull(getRequestAttributes()).getResponse(); + } catch (Exception e) { + return null; + } + } + + /** + * 获取session + */ + public static HttpSession getSession () { + return Objects.requireNonNull(getRequest()).getSession(); + } + + public static ServletRequestAttributes getRequestAttributes () { + try { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + return (ServletRequestAttributes) attributes; + } catch (Exception e) { + return null; + } + } + + public static String getHeader (HttpServletRequest request, String name) { + String value = request.getHeader(name); + if (StringUtils.isEmpty(value)) { + return StringUtils.EMPTY; + } + return urlDecode(value); + } + + public static Map getHeaders (HttpServletRequest request) { + Map map = new LinkedCaseInsensitiveMap<>(); + Enumeration enumeration = request.getHeaderNames(); + if (enumeration != null) { + while (enumeration.hasMoreElements()) { + String key = enumeration.nextElement(); + String value = request.getHeader(key); + map.put(key, value); + } + } + return map; + } + + /** + * 将字符串渲染到客户端 + * + * @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) { + return URLEncoder.encode(str, StandardCharsets.UTF_8); + } + + /** + * 内容解码 + * + * @param str 内容 + * + * @return 解码后的内容 + */ + public static String urlDecode (String str) { + return URLDecoder.decode(str, StandardCharsets.UTF_8); + } + + /** + * 设置webflux模型响应 + * + * @param response ServerHttpResponse + * @param value 响应内容 + * + * @return Mono + */ + public static Mono webFluxResponseWriter (ServerHttpResponse response, Object value) { + return webFluxResponseWriter(response, HttpStatus.OK, value, Result.FAIL); + } + + /** + * 设置webflux模型响应 + * + * @param response ServerHttpResponse + * @param code 响应状态码 + * @param value 响应内容 + * + * @return Mono + */ + public static Mono webFluxResponseWriter (ServerHttpResponse response, Object value, int code) { + return webFluxResponseWriter(response, HttpStatus.OK, value, code); + } + + /** + * 设置webflux模型响应 + * + * @param response ServerHttpResponse + * @param status http状态码 + * @param code 响应状态码 + * @param value 响应内容 + * + * @return Mono + */ + public static Mono webFluxResponseWriter (ServerHttpResponse response, HttpStatus status, Object value, int code) { + return webFluxResponseWriter(response, MediaType.APPLICATION_JSON_VALUE, status, value, code); + } + + /** + * 设置webflux模型响应 + * + * @param response ServerHttpResponse + * @param contentType content-type + * @param status http状态码 + * @param code 响应状态码 + * @param value 响应内容 + * + * @return Mono + */ + public static Mono webFluxResponseWriter (ServerHttpResponse response, String contentType, HttpStatus status, Object value, int code) { + response.setStatusCode(status); + response.getHeaders().add(HttpHeaders.CONTENT_TYPE, contentType); + Result result = Result.error(code, value.toString()); + DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONString(result).getBytes()); + return response.writeWith(Mono.just(dataBuffer)); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/SpringUtils.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/SpringUtils.java new file mode 100644 index 0000000..27f5bac --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/SpringUtils.java @@ -0,0 +1,114 @@ +package com.muyu.common.core.utils; + +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.stereotype.Component; + +/** + * spring工具类 方便在非spring管理环境中获取bean + * + * @author muyu + */ +@Component +public final class SpringUtils implements BeanFactoryPostProcessor { + /** + * Spring应用上下文环境 + */ + private static ConfigurableListableBeanFactory beanFactory; + + /** + * 获取对象 + * + * @param name + * + * @return Object 一个以所给名字注册的bean的实例 + * + * @throws org.springframework.beans.BeansException + */ + @SuppressWarnings("unchecked") + public static T getBean (String name) throws BeansException { + return (T) beanFactory.getBean(name); + } + + /** + * 获取类型为requiredType的对象 + * + * @param clz + * + * @return + * + * @throws org.springframework.beans.BeansException + */ + public static T getBean (Class clz) throws BeansException { + T result = 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 getAopProxy (T invoker) { + return (T) AopContext.currentProxy(); + } + + @Override + public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory) throws BeansException { + SpringUtils.beanFactory = beanFactory; + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/StringUtils.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/StringUtils.java new file mode 100644 index 0000000..ec8f557 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/StringUtils.java @@ -0,0 +1,504 @@ +package com.muyu.common.core.utils; + +import com.muyu.common.core.constant.Constants; +import com.muyu.common.core.text.StrFormatter; +import org.springframework.util.AntPathMatcher; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 字符串工具类 + * + * @author muyu + */ +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 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); + } + + /** + * 判断是否为空,并且不是空白字符 + * + * @param str 要判断的value + * + * @return 结果 + */ + public static boolean hasText (String str) { + return (str != null && !str.isEmpty() && containsText(str)); + } + + private static boolean containsText (CharSequence str) { + int strLen = str.length(); + for (int i = 0 ; i < strLen ; i++) { + if (!Character.isWhitespace(str.charAt(i))) { + return true; + } + } + return false; + } + + /** + * 格式化文本, {} 表示占位符
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @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); + } + + /** + * 判断给定的collection列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value + * + * @param collection 给定的集合 + * @param array 给定的数组 + * + * @return boolean 结果 + */ + public static boolean containsAny (Collection collection, String... array) { + if (isEmpty(collection) || isEmpty(array)) { + return false; + } else { + for (String str : array) { + if (collection.contains(str)) { + 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 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 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(); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/bean/BeanUtils.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/bean/BeanUtils.java new file mode 100644 index 0000000..d44b351 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/bean/BeanUtils.java @@ -0,0 +1,107 @@ +package com.muyu.common.core.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 muyu + */ +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 getSetterMethods (Object obj) { + // setter方法列表 + List setterMethods = new ArrayList(); + + // 获取所有方法 + 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 getGetterMethods (Object obj) { + // getter方法列表 + List getterMethods = new ArrayList(); + // 获取所有方法 + 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方法名中的属性名是否相等。
+ * 如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/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/bean/BeanValidators.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/bean/BeanValidators.java new file mode 100644 index 0000000..b904747 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/bean/BeanValidators.java @@ -0,0 +1,22 @@ +package com.muyu.common.core.utils.bean; + +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import jakarta.validation.Validator; + +import java.util.Set; + +/** + * bean对象属性验证 + * + * @author muyu + */ +public class BeanValidators { + public static void validateWithException (Validator validator, Object object, Class... groups) + throws ConstraintViolationException { + Set> constraintViolations = validator.validate(object, groups); + if (!constraintViolations.isEmpty()) { + throw new ConstraintViolationException(constraintViolations); + } + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/file/FileTypeUtils.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/file/FileTypeUtils.java new file mode 100644 index 0000000..dde7e85 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/file/FileTypeUtils.java @@ -0,0 +1,85 @@ +package com.muyu.common.core.utils.file; + +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.util.Objects; + +/** + * 文件类型工具类 + * + * @author muyu + */ +public class FileTypeUtils { + /** + * 获取文件类型 + *

+ * 例如: muyu.txt, 返回: txt + * + * @param file 文件名 + * + * @return 后缀(不含".") + */ + public static String getFileType (File file) { + if (null == file) { + return StringUtils.EMPTY; + } + return getFileType(file.getName()); + } + + /** + * 获取文件类型 + *

+ * 例如: muyu.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 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; + } + + /** + * 获取文件类型 + * + * @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; + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/file/FileUtils.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/file/FileUtils.java new file mode 100644 index 0000000..51ab790 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/file/FileUtils.java @@ -0,0 +1,222 @@ +package com.muyu.common.core.utils.file; + +import com.muyu.common.core.utils.StringUtils; +import org.apache.commons.lang3.ArrayUtils; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.*; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +/** + * 文件处理工具类 + * + * @author muyu + */ +public class FileUtils { + /** + * 字符常量:斜杠 {@code '/'} + */ + public static final char SLASH = '/'; + + /** + * 字符常量:反斜杠 {@code '\\'} + */ + public static final char BACKSLASH = '\\'; + + public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+"; + + /** + * 输出指定文件的byte数组 + * + * @param filePath 文件路径 + * @param os 输出流 + * + * @return + */ + public static void writeBytes (String filePath, OutputStream os) throws IOException { + FileInputStream fis = null; + try { + File file = new File(filePath); + if (!file.exists()) { + throw new FileNotFoundException(filePath); + } + fis = new FileInputStream(file); + byte[] b = new byte[1024]; + int length; + while ((length = fis.read(b)) > 0) { + os.write(b, 0, length); + } + } catch (IOException e) { + throw e; + } finally { + if (os != null) { + try { + os.close(); + } catch (IOException e1) { + e1.printStackTrace(); + } + } + if (fis != null) { + try { + fis.close(); + } catch (IOException e1) { + e1.printStackTrace(); + } + } + } + } + + /** + * 删除文件 + * + * @param filePath 文件 + * + * @return + */ + public static boolean deleteFile (String filePath) { + boolean flag = false; + File file = new File(filePath); + // 路径为文件且不为空则进行删除 + if (file.isFile() && file.exists()) { + 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; + } + // 判断是否在允许下载的文件规则内 + return ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource)); + } + + /** + * 下载文件名重新编码 + * + * @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, StandardCharsets.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, StandardCharsets.UTF_8); + } else { + // 其它浏览器 + filename = URLEncoder.encode(filename, StandardCharsets.UTF_8); + } + return filename; + } + + /** + * 返回文件名 + * + * @param filePath 文件 + * + * @return 文件名 + */ + public static String getName (String filePath) { + if (null == filePath) { + return null; + } + int len = filePath.length(); + if (0 == len) { + return filePath; + } + if (isFileSeparator(filePath.charAt(len - 1))) { + // 以分隔符结尾的去掉结尾分隔符 + len--; + } + + int begin = 0; + char c; + for (int i = len - 1 ; i > -1 ; i--) { + c = filePath.charAt(i); + if (isFileSeparator(c)) { + // 查找最后一个路径分隔符(/或者\) + begin = i + 1; + break; + } + } + + return filePath.substring(begin, len); + } + + /** + * 是否为Windows或者Linux(Unix)文件分隔符
+ * Windows平台下分隔符为\,Linux(Unix)为/ + * + * @param c 字符 + * + * @return 是否为Windows或者Linux(Unix)文件分隔符 + */ + public static boolean isFileSeparator (char c) { + return SLASH == c || BACKSLASH == c; + } + + /** + * 下载文件名重新编码 + * + * @param response 响应对象 + * @param realFileName 真实文件名 + * + * @return + */ + public static void setAttachmentResponseHeader (HttpServletResponse response, String realFileName) throws UnsupportedEncodingException { + String percentEncodedFileName = percentEncode(realFileName); + + String contentDispositionValue = "attachment; filename=" + + percentEncodedFileName + + ";" + + "filename*=" + + "utf-8''" + + percentEncodedFileName; + + response.setHeader("Content-disposition", contentDispositionValue); + response.setHeader("download-filename", percentEncodedFileName); + } + + /** + * 百分号编码工具方法 + * + * @param s 需要百分号编码的字符串 + * + * @return 百分号编码后的字符串 + */ + public static String percentEncode (String s) throws UnsupportedEncodingException { + String encode = URLEncoder.encode(s, StandardCharsets.UTF_8); + return encode.replaceAll("\\+", "%20"); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/file/ImageUtils.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/file/ImageUtils.java new file mode 100644 index 0000000..7e23345 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/file/ImageUtils.java @@ -0,0 +1,69 @@ +package com.muyu.common.core.utils.file; + +import org.apache.poi.util.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.util.Arrays; + +/** + * 图片处理工具类 + * + * @author muyu + */ +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 { + // 网络地址 + URL urlObj = new URL(url); + URLConnection urlConnection = urlObj.openConnection(); + urlConnection.setConnectTimeout(30 * 1000); + urlConnection.setReadTimeout(60 * 1000); + urlConnection.setDoInput(true); + in = urlConnection.getInputStream(); + return IOUtils.toByteArray(in); + } catch (Exception e) { + log.error("访问文件异常 {}", e); + return null; + } finally { + IOUtils.closeQuietly(in); + } + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/file/MimeTypeUtils.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/file/MimeTypeUtils.java new file mode 100644 index 0000000..9eb1d84 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/file/MimeTypeUtils.java @@ -0,0 +1,56 @@ +package com.muyu.common.core.utils.file; + +/** + * 媒体类型工具类 + * + * @author muyu + */ +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/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/html/EscapeUtil.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/html/EscapeUtil.java new file mode 100644 index 0000000..1fc6f91 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/html/EscapeUtil.java @@ -0,0 +1,145 @@ +package com.muyu.common.core.utils.html; + +import com.muyu.common.core.utils.StringUtils; + +/** + * 转义和反转义工具类 + * + * @author muyu + */ +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, lastPos, pos); + lastPos = pos; + } + } + } + return tmp.toString(); + } + + public static void main (String[] args) { + String html = ""; + String escape = EscapeUtil.escape(html); + // String html = "ipt>alert(\"XSS\")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/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/html/HTMLFilter.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/html/HTMLFilter.java new file mode 100644 index 0000000..68221f3 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/html/HTMLFilter.java @@ -0,0 +1,498 @@ +package com.muyu.common.core.utils.html; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * HTML过滤器,用于去除XSS漏洞隐患。 + * + * @author muyu + */ +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 P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>(); + private static final ConcurrentMap P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>(); + + /** + * set of allowed html elements, along with allowed attributes for each element + **/ + private final Map> vAllowed; + /** + * counts of open tags for each (allowable) html element + **/ + private final Map vTagCounts = new HashMap<>(); + + /** + * html elements which must always be self-closing (e.g. "") + **/ + private final String[] vSelfClosingTags; + /** + * html elements which must always have separate opening and closing tags (e.g. "") + **/ + 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. "" or "") + **/ + 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. "" + * becomes " text "). If set to false, unbalanced angle brackets will be html escaped. + */ + private final boolean alwaysMakeTags; + + /** + * Default constructor. + */ + public HTMLFilter () { + vAllowed = new HashMap<>(); + + final ArrayList a_atts = new ArrayList<>(); + a_atts.add("href"); + a_atts.add("target"); + vAllowed.put("a", a_atts); + + final ArrayList 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 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 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>) 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; + } + + // --------------------------------------------------------------- + // 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; + } + + 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 static boolean inArray (final String s, final String[] array) { + for (String item : array) { + if (item != null && item.equals(s)) { + return true; + } + } + return false; + } + + private void reset () { + vTagCounts.clear(); + } + + /** + * 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("")); + } + 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(""); + } + } + 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[^>]*)?>")); + } + 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 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 ""; + } + } + } + } + + // 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 paramNames = new ArrayList<>(); + final List 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 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)); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/ip/IpUtils.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/ip/IpUtils.java new file mode 100644 index 0000000..490a393 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/ip/IpUtils.java @@ -0,0 +1,331 @@ +package com.muyu.common.core.utils.ip; + +import com.muyu.common.core.utils.ServletUtils; +import com.muyu.common.core.utils.StringUtils; + +import jakarta.servlet.http.HttpServletRequest; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * 获取IP方法 + * + * @author muyu + */ +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_SEG = "(" + REGX_IP + "\\-" + REGX_IP + ")"; + public final static String REGX_IP_WILDCARD = "(((\\*\\.){3}\\*)|(" + REGX_0_255 + "(\\.\\*){3})|(" + REGX_0_255 + "\\." + REGX_0_255 + ")(\\.\\*){2}" + "|((" + REGX_0_255 + "\\.){3}\\*))"; + + /** + * 获取客户端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 (!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; + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/poi/ExcelHandlerAdapter.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/poi/ExcelHandlerAdapter.java new file mode 100644 index 0000000..e2041fc --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/poi/ExcelHandlerAdapter.java @@ -0,0 +1,23 @@ +package com.muyu.common.core.utils.poi; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Workbook; + +/** + * Excel数据格式处理适配器 + * + * @author muyu + */ +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/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/poi/ExcelUtil.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/poi/ExcelUtil.java new file mode 100644 index 0000000..ce4608a --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/poi/ExcelUtil.java @@ -0,0 +1,1230 @@ +package com.muyu.common.core.utils.poi; + +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.core.annotation.Excel.ColumnType; +import com.muyu.common.core.annotation.Excel.Type; +import com.muyu.common.core.annotation.Excels; +import com.muyu.common.core.exception.UtilException; +import com.muyu.common.core.text.Convert; +import com.muyu.common.core.utils.DateUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.utils.file.FileTypeUtils; +import com.muyu.common.core.utils.file.ImageUtils; +import com.muyu.common.core.utils.reflect.ReflectUtils; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.RegExUtils; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.CellRangeAddressList; +import org.apache.poi.util.IOUtils; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFDataValidation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +/** + * Excel相关处理 + * + * @author muyu + */ +public class ExcelUtil { + public static final String FORMULA_REGEX_STR = "=|-|\\+|@"; + public static final String[] FORMULA_STR = {"=", "-", "+", "@"}; + /** + * Excel sheet最大行数,默认65536 + */ + public static final int sheetSize = 65536; + private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class); + /** + * 数字格式 + */ + private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00"); + /** + * 实体对象 + */ + public Class clazz; + /** + * 需要排除列属性 + */ + public String[] excludeFields; + /** + * 工作表名称 + */ + private String sheetName; + /** + * 导出类型(EXPORT:导出数据;IMPORT:导入模板) + */ + private Type type; + /** + * 工作薄对象 + */ + private Workbook wb; + /** + * 工作表对象 + */ + private Sheet sheet; + /** + * 样式列表 + */ + private Map styles; + /** + * 导入导出数据列表 + */ + private List list; + /** + * 注解列表 + */ + private List fields; + /** + * 当前行号 + */ + private int rownum; + /** + * 标题 + */ + private String title; + /** + * 最大高度 + */ + private short maxHeight; + /** + * 合并后最后行数 + */ + private int subMergedLastRowNum = 0; + /** + * 合并后开始行数 + */ + private int subMergedFirstRowNum = 1; + /** + * 对象的子列表方法 + */ + private Method subMethod; + /** + * 对象的子列表属性 + */ + private List subFields; + /** + * 统计列表 + */ + private final Map statistics = new HashMap(); + + public ExcelUtil (Class clazz) { + this.clazz = clazz; + } + + /** + * 获取画布 + */ + public static Drawing getDrawingPatriarch (Sheet sheet) { + if (sheet.getDrawingPatriarch() == null) { + sheet.createDrawingPatriarch(); + } + return sheet.getDrawingPatriarch(); + } + + /** + * 解析导出值 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); + } + + /** + * 隐藏Excel中列属性 + * + * @param fields 列属性名 示例[单个"name"/多个"id","name"] + * + * @throws Exception + */ + public void hideColumn (String... fields) { + this.excludeFields = fields; + } + + public void init (List list, String sheetName, String title, Type type) { + if (list == null) { + list = new ArrayList(); + } + 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 importExcel (InputStream is) { + List 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 importExcel (InputStream is, int titleNum) throws Exception { + return importExcel(StringUtils.EMPTY, is, titleNum); + } + + /** + * 对excel表单指定表格索引名转换成list + * + * @param sheetName 表格索引名 + * @param titleNum 标题占用行数 + * @param is 输入流 + * + * @return 转换后集合 + */ + public List importExcel (String sheetName, InputStream is, int titleNum) throws Exception { + this.type = Type.IMPORT; + this.wb = WorkbookFactory.create(is); + List list = new ArrayList(); + // 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet + Sheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0); + if (sheet == null) { + throw new IOException("文件sheet不存在"); + } + + // 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1 + int rows = sheet.getLastRowNum(); + if (rows > 0) { + // 定义一个map用于存放excel列的序号和field. + Map cellMap = new HashMap(); + // 获取表头 + 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 fields = this.getFields(); + Map fieldsMap = new HashMap(); + 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 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 (!attr.handler().equals(ExcelHandlerAdapter.class)) { + val = dataFormatHandlerAdapter(val, attr, null); + } + ReflectUtils.invokeSetter(entity, propertyName, val); + } + } + list.add(entity); + } + } + return list; + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param response 返回数据 + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * + * @return 结果 + */ + public void exportExcel (HttpServletResponse response, List 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 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 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); + } + } + + /** + * 创建写入数据到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 = 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 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 createStyles (Workbook wb) { + // 写入各条记录,每条记录对应excel表中的一行 + Map styles = new HashMap(); + 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 annotationHeaderStyles (Workbook wb, Map styles) { + Map headerStyles = new HashMap(); + 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 annotationDataStyles (Workbook wb) { + Map styles = new HashMap(); + 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 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(); + 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 (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); + } + + /** + * 数据处理器 + * + * @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", 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 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(); + } + } + + /** + * 获取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 getFields () { + List fields = new ArrayList(); + List 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; + } + + /** + * 格式化不同类型的日期对象 + * + * @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); + } catch (Exception e) { + return new ArrayList(); + } + 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()); + } catch (Exception e) { + log.error("获取对象异常{}", e.getMessage()); + } + return method; + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/reflect/ReflectUtils.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/reflect/ReflectUtils.java new file mode 100644 index 0000000..82897c7 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/reflect/ReflectUtils.java @@ -0,0 +1,324 @@ +package com.muyu.common.core.utils.reflect; + +import com.muyu.common.core.text.Convert; +import com.muyu.common.core.utils.DateUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.apache.poi.ss.usermodel.DateUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.*; +import java.util.Date; + +/** + * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. + * + * @author muyu + */ +@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 final Logger logger = LoggerFactory.getLogger(ReflectUtils.class); + + /** + * 调用Getter方法. + * 支持多级,如:对象名.对象名.方法 + */ + @SuppressWarnings("unchecked") + public static 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 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 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 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 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 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 Class 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/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/sign/Base64.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/sign/Base64.java new file mode 100644 index 0000000..f6e4305 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/sign/Base64.java @@ -0,0 +1,256 @@ +package com.muyu.common.core.utils.sign; + +/** + * Base64工具类 + * + * @author muyu + */ +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] = '+'; + lookUpBase64Alphabet[63] = '/'; + } + + 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/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/sql/SqlUtil.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/sql/SqlUtil.java new file mode 100644 index 0000000..3f418e7 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/sql/SqlUtil.java @@ -0,0 +1,59 @@ +package com.muyu.common.core.utils.sql; + +import com.muyu.common.core.exception.UtilException; +import com.muyu.common.core.utils.StringUtils; + +/** + * sql操作工具类 + * + * @author muyu + */ +public class SqlUtil { + /** + * 限制orderBy最大长度 + */ + private static final int ORDER_BY_MAX_LENGTH = 500; + /** + * 定义常用的 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_\\ \\,\\.]+"; + + /** + * 检查字符,防止注入绕过 + */ + 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/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/uuid/IdUtils.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/uuid/IdUtils.java new file mode 100644 index 0000000..375b034 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/uuid/IdUtils.java @@ -0,0 +1,44 @@ +package com.muyu.common.core.utils.uuid; + +/** + * ID生成器工具类 + * + * @author muyu + */ +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/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/uuid/Seq.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/uuid/Seq.java new file mode 100644 index 0000000..2b4e382 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/uuid/Seq.java @@ -0,0 +1,78 @@ +package com.muyu.common.core.utils.uuid; + +import com.muyu.common.core.utils.DateUtils; +import com.muyu.common.core.utils.StringUtils; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author muyu 序列生成类 + */ +public class Seq { + // 通用序列类型 + public static final String commSeqType = "COMMON"; + + // 上传序列类型 + public static final String uploadSeqType = "UPLOAD"; + // 机器标识 + private static final String machineCode = "A"; + // 通用接口序列数 + private static final AtomicInteger commSeq = new AtomicInteger(1); + // 上传接口序列数 + private static final AtomicInteger uploadSeq = new AtomicInteger(1); + + /** + * 获取通用序列号 + * + * @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/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/uuid/UUID.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/uuid/UUID.java new file mode 100644 index 0000000..eb18d5b --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/utils/uuid/UUID.java @@ -0,0 +1,450 @@ +package com.muyu.common.core.utils.uuid; + +import com.muyu.common.core.exception.UtilException; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; + +/** + * 提供通用唯一识别码(universally unique identifier)(UUID)实现 + * + * @author muyu + */ +public final class UUID implements java.io.Serializable, Comparable { + private static final long serialVersionUID = -1185015143654744140L; + /** + * 此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); + } + + /** + * 返回指定数字对应的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); + } + + /** + * 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG) + * + * @return {@link SecureRandom} + */ + public static SecureRandom getSecureRandom () { + try { + return SecureRandom.getInstance("SHA1PRNG"); + } catch (NoSuchAlgorithmException e) { + throw new UtilException(e); + } + } + + /** + * 获取随机数生成器对象
+ * ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。 + * + * @return {@link ThreadLocalRandom} + */ + public static ThreadLocalRandom getRandom () { + return ThreadLocalRandom.current(); + } + + /** + * 返回此 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} 是如何生成的。 + *

+ * 版本号具有以下含意: + *

    + *
  • 1 基于时间的 UUID + *
  • 2 DCE 安全 UUID + *
  • 3 基于名称的 UUID + *
  • 4 随机生成的 UUID + *
+ * + * @return 此 {@code UUID} 的版本号 + */ + public int version () { + // Version is bits masked by 0x000000000000F000 in MS long + return (int) ((mostSigBits >> 12) & 0x0f); + } + + /** + * 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。 + *

+ * 变体号具有以下含意: + *

    + *
  • 0 为 NCS 向后兼容保留 + *
  • 2 IETF RFC 4122(Leach-Salz), 用于此类 + *
  • 6 保留,微软向后兼容 + *
  • 7 保留供以后定义使用 + *
+ * + * @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 相关联的时间戳值。 + * + *

+ * 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。
+ * 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。 + * + *

+ * 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。
+ * 如果此 {@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 相关联的时钟序列值。 + * + *

+ * 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。 + *

+ * {@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 相关的节点值。 + * + *

+ * 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。 + *

+ * 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。
+ * 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的节点值 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public long node () throws UnsupportedOperationException { + checkTimeBase(); + return leastSigBits & 0x0000FFFFFFFFFFFFL; + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + *

+ * UUID 的字符串表示形式由此 BNF 描述: + * + *

+     * {@code
+     * UUID                   = ----
+     * time_low               = 4*
+     * time_mid               = 2*
+     * time_high_and_version  = 2*
+     * variant_and_sequence   = 2*
+     * node                   = 6*
+     * hexOctet               = 
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * 
+ * + * + * + * @return 此{@code UUID} 的字符串表现形式 + * + * @see #toString(boolean) + */ + @Override + public String toString () { + return toString(false); + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + *

+ * UUID 的字符串表示形式由此 BNF 描述: + * + *

+     * {@code
+     * UUID                   = ----
+     * time_low               = 4*
+     * time_mid               = 2*
+     * time_high_and_version  = 2*
+     * variant_and_sequence   = 2*
+     * node                   = 6*
+     * hexOctet               = 
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * 
+ * + * + * + * @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(); + } + + // Comparison Operations + + /** + * 返回此 UUID 的哈希码。 + * + * @return UUID 的哈希码值。 + */ + @Override + public int hashCode () { + long hilo = mostSigBits ^ leastSigBits; + return ((int) (hilo >> 32)) ^ (int) hilo; + } + + // ------------------------------------------------------------------------------------------------------------------- + // Private method start + + /** + * 将此对象与指定对象比较。 + *

+ * 当且仅当参数不为 {@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); + } + + /** + * 将此 UUID 与指定的 UUID 比较。 + * + *

+ * 如果两个 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)))); + } + + /** + * 检查是否为time-based版本UUID + */ + private void checkTimeBase () { + if (version() != 1) { + throw new UnsupportedOperationException("Not a time-based UUID"); + } + } + + /** + * SecureRandom 的单例 + */ + private static class Holder { + static final SecureRandom numberGenerator = getSecureRandom(); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/validation/ValidationConfig.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/validation/ValidationConfig.java new file mode 100644 index 0000000..0540064 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/validation/ValidationConfig.java @@ -0,0 +1,14 @@ +package com.muyu.common.core.validation; + +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ValidationConfig { + @Bean + public Validator validator() { + return Validation.byDefaultProvider().configure().buildValidatorFactory().getValidator(); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/controller/BaseController.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/controller/BaseController.java new file mode 100644 index 0000000..3789a82 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/controller/BaseController.java @@ -0,0 +1,129 @@ +package com.muyu.common.core.web.controller; + +import com.github.pagehelper.PageInfo; +import com.muyu.common.core.utils.DateUtils; +import com.muyu.common.core.utils.PageUtils; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.web.page.TableDataInfo; +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 muyu + */ +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 clearPage () { + PageUtils.clearPage(); + } + + /** + * 响应请求分页数据 + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + protected Result> getDataTable (List list) { + return Result.success( + TableDataInfo.builder() + .total(new PageInfo(list).getTotal()) + .rows(list) + .build() + ); + } + + /** + * 返回成功 + */ + public Result success () { + return Result.success(); + } + + /** + * 返回成功消息 + */ + public Result success (String message) { + return Result.success(message); + } + + /** + * 返回成功消息 + */ + public Result success (Object data) { + return Result.success(data); + } + + /** + * 返回失败消息 + */ + public Result error () { + return Result.error(); + } + + /** + * 返回失败消息 + */ + public Result error (String message) { + return Result.error(message); + } + + /** + * 返回警告消息 + */ + public Result warn (String message) { + return Result.warn(message); + } + + /** + * 响应返回结果 + * + * @param rows 影响行数 + * + * @return 操作结果 + */ + protected Result toAjax (int rows) { + return rows > 0 ? Result.success() : Result.error(); + } + + /** + * 响应返回结果 + * + * @param result 结果 + * + * @return 操作结果 + */ + protected Result toAjax (boolean result) { + return result ? success() : error(); + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/domain/BaseEntity.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/domain/BaseEntity.java new file mode 100644 index 0000000..add8e3e --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/domain/BaseEntity.java @@ -0,0 +1,80 @@ +package com.muyu.common.core.web.domain; + +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 lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * Entity基类 + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class BaseEntity implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 搜索值 + */ + @JsonIgnore + @TableField(exist = false) + 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; + + /** + * 请求参数 + */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @TableField(exist = false) + private Map params; + + public Map getParams () { + if (params == null) { + params = new HashMap<>(); + } + return params; + } + + public void setParams (Map params) { + this.params = params; + } +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/domain/TreeEntity.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/domain/TreeEntity.java new file mode 100644 index 0000000..85ea8df --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/domain/TreeEntity.java @@ -0,0 +1,47 @@ +package com.muyu.common.core.web.domain; + +import lombok.*; +import lombok.experimental.SuperBuilder; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tree基类 + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +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<>(); + +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/page/PageDomain.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/page/PageDomain.java new file mode 100644 index 0000000..b9c5e45 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/page/PageDomain.java @@ -0,0 +1,93 @@ +package com.muyu.common.core.web.page; + +import com.muyu.common.core.utils.StringUtils; + +/** + * 分页数据 + * + * @author muyu + */ +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/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/page/TableDataInfo.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/page/TableDataInfo.java new file mode 100644 index 0000000..d677cce --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/page/TableDataInfo.java @@ -0,0 +1,45 @@ +package com.muyu.common.core.web.page; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.apache.poi.ss.formula.functions.T; + +import java.io.Serializable; +import java.util.List; + +/** + * 表格分页数据对象 + * + * @author muyu + */ + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class TableDataInfo implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 总记录数 + */ + private long total; + + /** + * 列表数据 + */ + private List rows; + + /** + * 消息状态码 + */ + private int code; + + /** + * 消息内容 + */ + private String msg; + +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/page/TableSupport.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/page/TableSupport.java new file mode 100644 index 0000000..ce0328d --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/web/page/TableSupport.java @@ -0,0 +1,53 @@ +package com.muyu.common.core.web.page; + +import com.muyu.common.core.text.Convert; +import com.muyu.common.core.utils.ServletUtils; + +/** + * 表格数据处理 + * + * @author muyu + */ +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/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/xss/Xss.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/xss/Xss.java new file mode 100644 index 0000000..fa31755 --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/xss/Xss.java @@ -0,0 +1,27 @@ +package com.muyu.common.core.xss; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义xss校验注解 + * + * @author muyu + */ +@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[] payload () default {}; +} diff --git a/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/xss/XssValidator.java b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/xss/XssValidator.java new file mode 100644 index 0000000..3a03eab --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/java/com/muyu/common/core/xss/XssValidator.java @@ -0,0 +1,31 @@ +package com.muyu.common.core.xss; + +import com.muyu.common.core.utils.StringUtils; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 自定义xss校验注解实现 + * + * @author muyu + */ +public class XssValidator implements ConstraintValidator { + private static final String HTML_PATTERN = "<(\\S*?)[^>]*>.*?|<.*? />"; + + public static boolean containsHtml (String value) { + Pattern pattern = Pattern.compile(HTML_PATTERN); + Matcher matcher = pattern.matcher(value); + return matcher.matches(); + } + + @Override + public boolean isValid (String value, ConstraintValidatorContext constraintValidatorContext) { + if (StringUtils.isBlank(value)) { + return true; + } + return !containsHtml(value); + } +} diff --git a/cloud-common/cloud-common-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/cloud-common/cloud-common-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..17f73ec --- /dev/null +++ b/cloud-common/cloud-common-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,2 @@ +com.muyu.common.core.utils.SpringUtils +com.muyu.common.core.feign.FeginConfig diff --git a/cloud-common/cloud-common-datascope/pom.xml b/cloud-common/cloud-common-datascope/pom.xml new file mode 100644 index 0000000..a7681bb --- /dev/null +++ b/cloud-common/cloud-common-datascope/pom.xml @@ -0,0 +1,27 @@ + + + + com.muyu + cloud-common + 3.6.3 + + 4.0.0 + + cloud-common-datascope + + + cloud-common-datascope权限范围 + + + + + + + com.muyu + cloud-common-security + + + + diff --git a/cloud-common/cloud-common-datascope/src/main/java/com/muyu/common/datascope/annotation/DataScope.java b/cloud-common/cloud-common-datascope/src/main/java/com/muyu/common/datascope/annotation/DataScope.java new file mode 100644 index 0000000..d42163c --- /dev/null +++ b/cloud-common/cloud-common-datascope/src/main/java/com/muyu/common/datascope/annotation/DataScope.java @@ -0,0 +1,28 @@ +package com.muyu.common.datascope.annotation; + +import java.lang.annotation.*; + +/** + * 数据权限过滤注解 + * + * @author muyu + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DataScope { + /** + * 部门表的别名 + */ + String deptAlias() default ""; + + /** + * 用户表的别名 + */ + String userAlias() default ""; + + /** + * 权限字符(用于多个角色匹配符合要求的权限)默认根据权限注解@RequiresPermissions获取,多个权限用逗号分隔开来 + */ + String permission() default ""; +} diff --git a/cloud-common/cloud-common-datascope/src/main/java/com/muyu/common/datascope/aspect/DataScopeAspect.java b/cloud-common/cloud-common-datascope/src/main/java/com/muyu/common/datascope/aspect/DataScopeAspect.java new file mode 100644 index 0000000..ff9110d --- /dev/null +++ b/cloud-common/cloud-common-datascope/src/main/java/com/muyu/common/datascope/aspect/DataScopeAspect.java @@ -0,0 +1,147 @@ +package com.muyu.common.datascope.aspect; + +import com.muyu.common.core.context.SecurityContextHolder; +import com.muyu.common.core.text.Convert; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.web.domain.BaseEntity; +import com.muyu.common.datascope.annotation.DataScope; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.SysRole; +import com.muyu.common.system.domain.SysUser; +import com.muyu.common.system.domain.LoginUser; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * 数据过滤处理 + * + * @author muyu + */ +@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"; + + /** + * 数据范围过滤 + * + * @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 conditions = new ArrayList(); + + 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.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")"); + } + } + } + + @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.getSysUser(); + // 如果是超级管理员,则不过滤数据 + if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) { + String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), SecurityContextHolder.getPermission()); + dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), + controllerDataScope.userAlias(), permission); + } + } + } + + /** + * 拼接权限sql前先清空params.dataScope参数防止注入 + */ + private void clearDataScope (final JoinPoint joinPoint) { + Object params = joinPoint.getArgs()[0]; + if (StringUtils.isNotNull(params) && params instanceof BaseEntity baseEntity) { + baseEntity.getParams().put(DATA_SCOPE, ""); + } + } +} diff --git a/cloud-common/cloud-common-datascope/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/cloud-common/cloud-common-datascope/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..8f4967b --- /dev/null +++ b/cloud-common/cloud-common-datascope/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.muyu.common.datascope.aspect.DataScopeAspect diff --git a/cloud-common/cloud-common-datasource/pom.xml b/cloud-common/cloud-common-datasource/pom.xml new file mode 100644 index 0000000..d1639d5 --- /dev/null +++ b/cloud-common/cloud-common-datasource/pom.xml @@ -0,0 +1,35 @@ + + + + com.muyu + cloud-common + 3.6.3 + + 4.0.0 + + cloud-common-datasource + + + cloud-common-datasource多数据源 + + + + + + + com.alibaba + druid-spring-boot-3-starter + ${druid.version} + + + + + com.baomidou + dynamic-datasource-spring-boot3-starter + ${dynamic-ds.version} + + + + diff --git a/cloud-common/cloud-common-datasource/src/main/java/com/muyu/common/datasource/annotation/Master.java b/cloud-common/cloud-common-datasource/src/main/java/com/muyu/common/datasource/annotation/Master.java new file mode 100644 index 0000000..d9aae00 --- /dev/null +++ b/cloud-common/cloud-common-datasource/src/main/java/com/muyu/common/datasource/annotation/Master.java @@ -0,0 +1,18 @@ +package com.muyu.common.datasource.annotation; + +import com.baomidou.dynamic.datasource.annotation.DS; + +import java.lang.annotation.*; + +/** + * 主库数据源 + * + * @author muyu + */ +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@DS("master") +public @interface Master { + +} diff --git a/cloud-common/cloud-common-datasource/src/main/java/com/muyu/common/datasource/annotation/Slave.java b/cloud-common/cloud-common-datasource/src/main/java/com/muyu/common/datasource/annotation/Slave.java new file mode 100644 index 0000000..9663cd4 --- /dev/null +++ b/cloud-common/cloud-common-datasource/src/main/java/com/muyu/common/datasource/annotation/Slave.java @@ -0,0 +1,18 @@ +package com.muyu.common.datasource.annotation; + +import com.baomidou.dynamic.datasource.annotation.DS; + +import java.lang.annotation.*; + +/** + * 从库数据源 + * + * @author muyu + */ +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@DS("slave") +public @interface Slave { + +} diff --git a/cloud-common/cloud-common-log/pom.xml b/cloud-common/cloud-common-log/pom.xml new file mode 100644 index 0000000..27ab5a5 --- /dev/null +++ b/cloud-common/cloud-common-log/pom.xml @@ -0,0 +1,27 @@ + + + + com.muyu + cloud-common + 3.6.3 + + 4.0.0 + + cloud-common-log + + + cloud-common-log日志记录 + + + + + + + com.muyu + cloud-common-security + + + + diff --git a/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/annotation/Log.java b/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/annotation/Log.java new file mode 100644 index 0000000..1a240a8 --- /dev/null +++ b/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/annotation/Log.java @@ -0,0 +1,46 @@ +package com.muyu.common.log.annotation; + +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.log.enums.OperatorType; + +import java.lang.annotation.*; + +/** + * 自定义操作日志记录注解 + * + * @author muyu + */ +@Target({ElementType.PARAMETER, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Log { + /** + * 模块 + */ + String title() default ""; + + /** + * 功能 + */ + BusinessType businessType() default BusinessType.OTHER; + + /** + * 操作人类别 + */ + OperatorType operatorType() default OperatorType.MANAGE; + + /** + * 是否保存请求的参数 + */ + boolean isSaveRequestData() default true; + + /** + * 是否保存响应的参数 + */ + boolean isSaveResponseData() default true; + + /** + * 排除指定的请求参数 + */ + String[] excludeParamNames() default {}; +} diff --git a/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/aspect/LogAspect.java b/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/aspect/LogAspect.java new file mode 100644 index 0000000..1d6e3c3 --- /dev/null +++ b/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/aspect/LogAspect.java @@ -0,0 +1,220 @@ +package com.muyu.common.log.aspect; + +import com.alibaba.fastjson2.JSON; +import com.muyu.common.core.utils.ServletUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.utils.ip.IpUtils; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessStatus; +import com.muyu.common.log.filter.PropertyPreExcludeFilter; +import com.muyu.common.log.service.AsyncLogService; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.SysOperLog; +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.http.HttpMethod; +import org.springframework.stereotype.Component; +import org.springframework.validation.BindingResult; +import org.springframework.web.multipart.MultipartFile; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.util.Collection; +import java.util.Map; + +/** + * 操作日志记录处理 + * + * @author muyu + */ +@Aspect +@Component +public class LogAspect { + /** + * 排除敏感属性字段 + */ + public static final String[] EXCLUDE_PROPERTIES = {"password", "oldPassword", "newPassword", "confirmPassword"}; + private static final Logger log = LoggerFactory.getLogger(LogAspect.class); + /** + * 计算操作消耗时间 + */ + private static final ThreadLocal TIME_THREADLOCAL = new NamedThreadLocal("Cost Time"); + + @Autowired + private AsyncLogService asyncLogService; + + /** + * 处理请求前执行 + */ + @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 { + // *========数据库日志=========*// + 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)); + String username = SecurityUtils.getUsername(); + if (StringUtils.isNotBlank(username)) { + operLog.setOperName(username); + } + + 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()); + // 保存数据库 + asyncLogService.saveSysLog(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 { + String requestMethod = operLog.getRequestMethod(); + Map paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest()); + 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) { + for (Object o : paramsArray) { + if (StringUtils.isNotNull(o) && !isFilterObject(o)) { + try { + String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter(excludeParamNames)); + params += jsonObj + " "; + } 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/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/enums/BusinessStatus.java b/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/enums/BusinessStatus.java new file mode 100644 index 0000000..45e9713 --- /dev/null +++ b/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/enums/BusinessStatus.java @@ -0,0 +1,18 @@ +package com.muyu.common.log.enums; + +/** + * 操作状态 + * + * @author muyu + */ +public enum BusinessStatus { + /** + * 成功 + */ + SUCCESS, + + /** + * 失败 + */ + FAIL, +} diff --git a/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/enums/BusinessType.java b/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/enums/BusinessType.java new file mode 100644 index 0000000..2e928c7 --- /dev/null +++ b/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/enums/BusinessType.java @@ -0,0 +1,58 @@ +package com.muyu.common.log.enums; + +/** + * 业务操作类型 + * + * @author muyu + */ +public enum BusinessType { + /** + * 其它 + */ + OTHER, + + /** + * 新增 + */ + INSERT, + + /** + * 修改 + */ + UPDATE, + + /** + * 删除 + */ + DELETE, + + /** + * 授权 + */ + GRANT, + + /** + * 导出 + */ + EXPORT, + + /** + * 导入 + */ + IMPORT, + + /** + * 强退 + */ + FORCE, + + /** + * 生成代码 + */ + GENCODE, + + /** + * 清空数据 + */ + CLEAN, +} diff --git a/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/enums/OperatorType.java b/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/enums/OperatorType.java new file mode 100644 index 0000000..645777f --- /dev/null +++ b/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/enums/OperatorType.java @@ -0,0 +1,23 @@ +package com.muyu.common.log.enums; + +/** + * 操作人类别 + * + * @author muyu + */ +public enum OperatorType { + /** + * 其它 + */ + OTHER, + + /** + * 后台用户 + */ + MANAGE, + + /** + * 手机端用户 + */ + MOBILE +} diff --git a/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/filter/PropertyPreExcludeFilter.java b/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/filter/PropertyPreExcludeFilter.java new file mode 100644 index 0000000..2245256 --- /dev/null +++ b/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/filter/PropertyPreExcludeFilter.java @@ -0,0 +1,20 @@ +package com.muyu.common.log.filter; + +import com.alibaba.fastjson2.filter.SimplePropertyPreFilter; + +/** + * 排除JSON敏感属性 + * + * @author muyu + */ +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/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/service/AsyncLogService.java b/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/service/AsyncLogService.java new file mode 100644 index 0000000..46e1cdf --- /dev/null +++ b/cloud-common/cloud-common-log/src/main/java/com/muyu/common/log/service/AsyncLogService.java @@ -0,0 +1,27 @@ +package com.muyu.common.log.service; + +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.system.remote.RemoteLogService; +import com.muyu.common.system.domain.SysOperLog; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +/** + * 异步调用日志服务 + * + * @author muyu + */ +@Service +public class AsyncLogService { + @Autowired + private RemoteLogService remoteLogService; + + /** + * 保存系统日志记录 + */ + @Async + public void saveSysLog (SysOperLog sysOperLog) throws Exception { + remoteLogService.saveLog(sysOperLog, SecurityConstants.INNER); + } +} diff --git a/cloud-common/cloud-common-log/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/cloud-common/cloud-common-log/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..e46e773 --- /dev/null +++ b/cloud-common/cloud-common-log/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,2 @@ +com.muyu.common.log.service.AsyncLogService +com.muyu.common.log.aspect.LogAspect diff --git a/cloud-common/cloud-common-rabbit/pom.xml b/cloud-common/cloud-common-rabbit/pom.xml new file mode 100644 index 0000000..fa6d383 --- /dev/null +++ b/cloud-common/cloud-common-rabbit/pom.xml @@ -0,0 +1,35 @@ + + + 4.0.0 + + com.muyu + cloud-common + 3.6.3 + + + cloud-common-rabbit + + + 17 + 17 + UTF-8 + + + + + + + org.springframework.boot + spring-boot-starter-amqp + + + + + com.muyu + cloud-common-core + + + + \ No newline at end of file diff --git a/cloud-common/cloud-common-rabbit/src/main/java/com/muyu/common/rabbit/RabbitListenerConfigurer.java b/cloud-common/cloud-common-rabbit/src/main/java/com/muyu/common/rabbit/RabbitListenerConfigurer.java new file mode 100644 index 0000000..51cb359 --- /dev/null +++ b/cloud-common/cloud-common-rabbit/src/main/java/com/muyu/common/rabbit/RabbitListenerConfigurer.java @@ -0,0 +1,41 @@ +package com.muyu.common.rabbit; + +import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistrar; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.converter.MappingJackson2MessageConverter; +import org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory; + +@Configuration +public class RabbitListenerConfigurer implements org.springframework.amqp.rabbit.annotation.RabbitListenerConfigurer { + + static { + System.setProperty("spring.amqp.deserialization.trust.all", "true"); + } + + //以下配置RabbitMQ消息服务 + @Autowired + public ConnectionFactory connectionFactory; + + + /** + * 处理器方法工厂 + * @return + */ + @Bean + public DefaultMessageHandlerMethodFactory handlerMethodFactory() { + DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory(); + // 这里的转换器设置实现了 通过 @Payload 注解 自动反序列化message body + factory.setMessageConverter(new MappingJackson2MessageConverter()); + return factory; + } + + @Override + public void configureRabbitListeners(RabbitListenerEndpointRegistrar rabbitListenerEndpointRegistrar) { + rabbitListenerEndpointRegistrar.setMessageHandlerMethodFactory(handlerMethodFactory()); + } + +} + diff --git a/cloud-common/cloud-common-rabbit/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/cloud-common/cloud-common-rabbit/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..189ea2c --- /dev/null +++ b/cloud-common/cloud-common-rabbit/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.muyu.common.rabbit.RabbitListenerConfigurer \ No newline at end of file diff --git a/cloud-common/cloud-common-redis/pom.xml b/cloud-common/cloud-common-redis/pom.xml new file mode 100644 index 0000000..4a2d5ea --- /dev/null +++ b/cloud-common/cloud-common-redis/pom.xml @@ -0,0 +1,33 @@ + + + + com.muyu + cloud-common + 3.6.3 + + 4.0.0 + + cloud-common-redis + + + cloud-common-redis缓存服务 + + + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + com.muyu + cloud-common-core + + + + diff --git a/cloud-common/cloud-common-redis/src/main/java/com/muyu/common/redis/configure/FastJson2JsonRedisSerializer.java b/cloud-common/cloud-common-redis/src/main/java/com/muyu/common/redis/configure/FastJson2JsonRedisSerializer.java new file mode 100644 index 0000000..6ec0967 --- /dev/null +++ b/cloud-common/cloud-common-redis/src/main/java/com/muyu/common/redis/configure/FastJson2JsonRedisSerializer.java @@ -0,0 +1,49 @@ +package com.muyu.common.redis.configure; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONReader; +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.filter.Filter; +import com.muyu.common.core.constant.Constants; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.SerializationException; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +/** + * Redis使用FastJson序列化 + * + * @author muyu + */ +public class FastJson2JsonRedisSerializer implements RedisSerializer { + + public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; + + static final Filter AUTO_TYPE_FILTER = JSONReader.autoTypeFilter(Constants.JSON_WHITELIST_STR); + + private final Class clazz; + + public FastJson2JsonRedisSerializer (Class clazz) { + super(); + this.clazz = clazz; + } + + @Override + public byte[] serialize (T t) throws SerializationException { + if (t == null) { + return new byte[0]; + } + return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(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/cloud-common/cloud-common-redis/src/main/java/com/muyu/common/redis/configure/RedisConfig.java b/cloud-common/cloud-common-redis/src/main/java/com/muyu/common/redis/configure/RedisConfig.java new file mode 100644 index 0000000..ba8760e --- /dev/null +++ b/cloud-common/cloud-common-redis/src/main/java/com/muyu/common/redis/configure/RedisConfig.java @@ -0,0 +1,41 @@ +package com.muyu.common.redis.configure; + +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; +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.serializer.StringRedisSerializer; + +/** + * redis配置 + * + * @author muyu + */ +@Configuration +@EnableCaching +@AutoConfigureBefore(RedisAutoConfiguration.class) +public class RedisConfig extends CachingConfigurerSupport { + @Bean + @SuppressWarnings(value = {"unchecked", "rawtypes"}) + public RedisTemplate redisTemplate (RedisConnectionFactory connectionFactory) { + RedisTemplate 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; + } +} diff --git a/cloud-common/cloud-common-redis/src/main/java/com/muyu/common/redis/service/RedisService.java b/cloud-common/cloud-common-redis/src/main/java/com/muyu/common/redis/service/RedisService.java new file mode 100644 index 0000000..db90c1e --- /dev/null +++ b/cloud-common/cloud-common-redis/src/main/java/com/muyu/common/redis/service/RedisService.java @@ -0,0 +1,258 @@ +package com.muyu.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 muyu + **/ +@SuppressWarnings(value = {"unchecked", "rawtypes"}) +@Component +public class RedisService { + @Autowired + public RedisTemplate redisTemplate; + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + */ + public void setCacheObject (final String key, final T value) { + redisTemplate.opsForValue().set(key, value); + } + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + * @param timeout 时间 + * @param timeUnit 时间颗粒度 + */ + public 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 getCacheObject (final String key) { + ValueOperations 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 long setCacheList (final String key, final List dataList) { + Long count = redisTemplate.opsForList().rightPushAll(key, dataList); + return count == null ? 0 : count; + } + + /** + * 获得缓存的list对象 + * + * @param key 缓存的键值 + * + * @return 缓存键值对应的数据 + */ + public List getCacheList (final String key) { + return redisTemplate.opsForList().range(key, 0, -1); + } + + /** + * 缓存Set + * + * @param key 缓存键值 + * @param dataSet 缓存的数据 + * + * @return 缓存数据的对象 + */ + public BoundSetOperations setCacheSet (final String key, final Set dataSet) { + BoundSetOperations setOperation = redisTemplate.boundSetOps(key); + Iterator it = dataSet.iterator(); + while (it.hasNext()) { + setOperation.add(it.next()); + } + return setOperation; + } + + /** + * 获得缓存的set + * + * @param key + * + * @return + */ + public Set getCacheSet (final String key) { + return redisTemplate.opsForSet().members(key); + } + + /** + * 缓存Map + * + * @param key + * @param dataMap + */ + public void setCacheMap (final String key, final Map dataMap) { + if (dataMap != null) { + redisTemplate.opsForHash().putAll(key, dataMap); + } + } + + /** + * 获得缓存的Map + * + * @param key + * + * @return + */ + public Map getCacheMap (final String key) { + return redisTemplate.opsForHash().entries(key); + } + + /** + * 往Hash中存入数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @param value 值 + */ + public 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 getCacheMapValue (final String key, final String hKey) { + HashOperations opsForHash = redisTemplate.opsForHash(); + return opsForHash.get(key, hKey); + } + + /** + * 获取多个Hash中的数据 + * + * @param key Redis键 + * @param hKeys Hash键集合 + * + * @return Hash对象集合 + */ + public List getMultiCacheMapValue (final String key, final Collection 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 keys (final String pattern) { + return redisTemplate.keys(pattern); + } +} diff --git a/cloud-common/cloud-common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/cloud-common/cloud-common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..27b030e --- /dev/null +++ b/cloud-common/cloud-common-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,2 @@ +com.muyu.common.redis.configure.RedisConfig +com.muyu.common.redis.service.RedisService diff --git a/cloud-common/cloud-common-saas/pom.xml b/cloud-common/cloud-common-saas/pom.xml new file mode 100644 index 0000000..41b57fd --- /dev/null +++ b/cloud-common/cloud-common-saas/pom.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + + com.muyu + cloud-common + 3.6.3 + + + cloud-common-saas + + + 17 + 17 + UTF-8 + + + + + + com.muyu + cloud-common-datasource + + + + + com.muyu + cloud-common-security + + + + + + + + diff --git a/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/many/datasource/ManyDataSource.java b/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/many/datasource/ManyDataSource.java new file mode 100644 index 0000000..8ea974e --- /dev/null +++ b/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/many/datasource/ManyDataSource.java @@ -0,0 +1,110 @@ +package com.muyu.cloud.common.many.datasource; + +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.fastjson2.JSON; +import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; +import com.muyu.cloud.common.many.datasource.constents.DatasourceContent; +import com.muyu.cloud.common.many.datasource.domain.model.DataSourceInfo; +import com.muyu.cloud.common.many.datasource.factory.DruidDataSourceFactory; +import com.muyu.cloud.common.many.datasource.role.DynamicDataSource; +import com.muyu.cloud.common.saas.domain.model.EntInfo; +import com.muyu.cloud.common.saas.exception.SaaSException; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.utils.SpringUtils; +import com.muyu.common.system.domain.Datasource; +import com.muyu.common.system.domain.SysUser; +import com.muyu.common.system.remote.RemoteSaaSService; +import com.muyu.common.system.remote.RemoteUserService; +import lombok.extern.log4j.Log4j2; +import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @Author: DongZeLiang + * @date: 2024/6/3 + * @Description: 多数据源 + * @Version: 1.0 + */ +@Log4j2 +@Component +@AutoConfiguration(before = {MybatisPlusAutoConfiguration.class, MybatisAutoConfiguration.class}) +public class ManyDataSource implements ApplicationRunner{ + private List dataSourceInfoList(){ + RemoteSaaSService remoteSaaSService = SpringUtils.getBean(RemoteSaaSService.class); +Result> tableDataInfoResult = remoteSaaSService.findDatabaseList(); + if (tableDataInfoResult==null){ + throw new SaaSException("saas远调数据源错误"); + } + List data = tableDataInfoResult.getData(); + if (tableDataInfoResult.getCode() ==Result.SUCCESS && data !=null){ + List list = new ArrayList<>(); + for (Datasource row : data) { + list.add( + EntInfo.builder() + .entCode(row.getDatabaseName()) + .ip(DatasourceContent.IP) + .port(DatasourceContent.PORT) + .build() + ); + } + return list; + }else { + log.error("远调数据源错误,远调数据为:{}", JSON.toJSONString(data)); + return null; + } + } + +// private List dataPrimarySourceInfoList(){ +// List list = new ArrayList<>(); +// list.add( +// EntInfo.builder() +// .entCode() +// .ip(DatasourceContent.IP) +// .port(DatasourceContent.PORT) +// .build() +// ); +// return list; +// } + + @Bean + public DynamicDataSource dynamicDataSource(DruidDataSourceFactory druidDataSourceFactory) { + // 企业列表 企业CODE,端口,IP + Map dataSourceMap = new HashMap<>(); + dataSourceInfoList() + .stream() + .map(entInfo -> DataSourceInfo.hostAndPortBuild(entInfo.getEntCode(), entInfo.getIp(), entInfo.getPort())) + .forEach(dataSourceInfo -> { + dataSourceMap.put(dataSourceInfo.getKey(), druidDataSourceFactory.create(dataSourceInfo)); + }); + //设置动态数据源 + DynamicDataSource dynamicDataSource = new DynamicDataSource(); +// dynamicDataSource.setDefaultTargetDataSource(masterDataSource()); + dynamicDataSource.setTargetDataSources(dataSourceMap); + //将数据源信息备份在defineTargetDataSources中 + dynamicDataSource.setDefineTargetDataSources(dataSourceMap); + return dynamicDataSource; + } + + @Override + public void run(ApplicationArguments args) { + DruidDataSourceFactory druidDataSourceFactory = SpringUtils.getBean(DruidDataSourceFactory.class); + DynamicDataSource dynamicDataSource = SpringUtils.getBean(DynamicDataSource.class); + for (EntInfo entInfo : dataSourceInfoList()) { + DataSourceInfo dataSourceInfo = DataSourceInfo.hostAndPortBuild( + entInfo.getEntCode(), entInfo.getIp(), entInfo.getPort() + ); + DruidDataSource druidDataSource = druidDataSourceFactory.create(dataSourceInfo); + dynamicDataSource.put(dataSourceInfo.getKey(), druidDataSource); + log.info("存储数据连接池为:key:{}",dataSourceInfo.getKey()); + } + } +} diff --git a/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/many/datasource/constents/DatasourceContent.java b/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/many/datasource/constents/DatasourceContent.java new file mode 100644 index 0000000..b69b858 --- /dev/null +++ b/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/many/datasource/constents/DatasourceContent.java @@ -0,0 +1,19 @@ +package com.muyu.cloud.common.many.datasource.constents; + +/** + * @author DongZl + * @description: 数据源常量 + * @Date 2023-8-1 上午 11:02 + */ +public class DatasourceContent { + + public final static String DATASOURCE_URL = "jdbc:mysql://{}:{}/{}?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8"; + + public final static String USER_NAME = "root"; + + public final static String PASSWORD = "Lw030106"; + + public final static String IP = "47.101.53.251"; + + public final static Integer PORT = 3306; +} diff --git a/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/many/datasource/domain/model/DataSourceInfo.java b/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/many/datasource/domain/model/DataSourceInfo.java new file mode 100644 index 0000000..73cc26e --- /dev/null +++ b/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/many/datasource/domain/model/DataSourceInfo.java @@ -0,0 +1,50 @@ +package com.muyu.cloud.common.many.datasource.domain.model; + +import com.muyu.cloud.common.many.datasource.constents.DatasourceContent; +import com.muyu.common.core.utils.StringUtils; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author DongZl + * @description: 数据源实体类 + * @Date 2023-8-1 上午 11:15 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DataSourceInfo { + + /** + * 键 + */ + private String key; + + /** + * 地址 + */ + private String url; + + /** + * 用户名 + */ + private String userName; + + /** + * 密码 + */ + private String password; + + + public static DataSourceInfo hostAndPortBuild(String key, String host, Integer port) { + return DataSourceInfo.builder() + .key(key) + .url(StringUtils.format(DatasourceContent.DATASOURCE_URL, host, port, key)) + .password(DatasourceContent.PASSWORD) + .userName(DatasourceContent.USER_NAME) + .build(); + } +} diff --git a/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/many/datasource/factory/DruidDataSourceFactory.java b/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/many/datasource/factory/DruidDataSourceFactory.java new file mode 100644 index 0000000..7ceb782 --- /dev/null +++ b/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/many/datasource/factory/DruidDataSourceFactory.java @@ -0,0 +1,40 @@ +package com.muyu.cloud.common.many.datasource.factory; + +import com.alibaba.druid.pool.DruidDataSource; +import com.muyu.cloud.common.many.datasource.domain.model.DataSourceInfo; +import lombok.extern.log4j.Log4j2; +import org.springframework.stereotype.Component; + +import java.sql.SQLException; + +/** + * @Author: DongZeLiang + * @date: 2024/6/3 + * @Description: Druid工厂 + * @Version: 1.0 + */ +@Log4j2 +@Component +public class DruidDataSourceFactory { + + /** + * @Description: 根据传递的数据源信息测试数据库连接 + * @Author Dongzl + */ + public DruidDataSource create(DataSourceInfo dataSourceInfo) { + DruidDataSource druidDataSource = new DruidDataSource(); + druidDataSource.setUrl(dataSourceInfo.getUrl()); + druidDataSource.setUsername(dataSourceInfo.getUserName()); + druidDataSource.setPassword(dataSourceInfo.getPassword()); + druidDataSource.setBreakAfterAcquireFailure(true); + druidDataSource.setConnectionErrorRetryAttempts(0); + try { + druidDataSource.getConnection(2000); + log.info("{} -> 数据源连接成功", dataSourceInfo.getKey()); + return druidDataSource; + } catch (SQLException throwables) { + log.error("数据源 {} 连接失败,用户名:{},密码 {}",dataSourceInfo.getUrl(),dataSourceInfo.getUserName(),dataSourceInfo.getPassword()); + return null; + } + } +} diff --git a/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/many/datasource/holder/DynamicDataSourceHolder.java b/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/many/datasource/holder/DynamicDataSourceHolder.java new file mode 100644 index 0000000..e53229a --- /dev/null +++ b/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/many/datasource/holder/DynamicDataSourceHolder.java @@ -0,0 +1,42 @@ +package com.muyu.cloud.common.many.datasource.holder; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.Assert; + +/** + * 数据源切换处理 + * + * @author Dongzl + */ +@Slf4j +public class DynamicDataSourceHolder { + /** + * 保存动态数据源名称 + */ + private static final ThreadLocal DYNAMIC_DATASOURCE_KEY = new ThreadLocal<>(); + + /** + * 设置/切换数据源,决定当前线程使用哪个数据源 + */ + public static void setDynamicDataSourceKey(String key){ + log.info("数据源切换为:{}",key); + DYNAMIC_DATASOURCE_KEY.set(key); + } + + /** + * 获取动态数据源名称,默认使用mater数据源 + */ + public static String getDynamicDataSourceKey(){ + String key = DYNAMIC_DATASOURCE_KEY.get(); + Assert.notNull(key, "请携带数据标识"); + return key; + } + + /** + * 移除当前数据源 + */ + public static void removeDynamicDataSourceKey(){ + log.info("移除数据源:{}",DYNAMIC_DATASOURCE_KEY.get()); + DYNAMIC_DATASOURCE_KEY.remove(); + } +} diff --git a/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/many/datasource/role/DynamicDataSource.java b/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/many/datasource/role/DynamicDataSource.java new file mode 100644 index 0000000..6ac17cb --- /dev/null +++ b/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/many/datasource/role/DynamicDataSource.java @@ -0,0 +1,55 @@ +package com.muyu.cloud.common.many.datasource.role; + +import com.alibaba.druid.pool.DruidDataSource; +import com.muyu.cloud.common.many.datasource.holder.DynamicDataSourceHolder; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + +import java.util.Map; + +/** + * 动态数据源 + * 调用AddDefineDataSource组件的addDefineDynamicDataSource()方法,获取原来targetdatasources的map,并将新的数据源信息添加到map中,并替换targetdatasources中的map + * 切换数据源时可以使用@DataSource(value = "数据源名称"),或者DynamicDataSourceContextHolder.setContextKey("数据源名称") + * @author Dongzl + */ +@EqualsAndHashCode(callSuper = true) +@Data +@AllArgsConstructor +@NoArgsConstructor +public class DynamicDataSource extends AbstractRoutingDataSource { + /** + * 备份所有数据源信息 备份的是个 指针 !!! + */ + private Map defineTargetDataSources; + + /** + * 判定键是否出站了 + * @param key 键 + * @return 存在结果 true存在 false不存在 + */ + public boolean hashKey(String key){ + return defineTargetDataSources.containsKey(key); + } + + /** + * 添加数据库 + * @param key 键 + * @param value 数据源 + */ + public void put(String key, DruidDataSource value) { + defineTargetDataSources.put(key, value); + this.afterPropertiesSet(); + } + + /** + * 决定当前线程使用哪个数据源 + */ + @Override + protected Object determineCurrentLookupKey() { + return DynamicDataSourceHolder.getDynamicDataSourceKey(); + } +} diff --git a/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/saas/contents/SaaSConstant.java b/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/saas/contents/SaaSConstant.java new file mode 100644 index 0000000..241ce32 --- /dev/null +++ b/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/saas/contents/SaaSConstant.java @@ -0,0 +1,12 @@ +package com.muyu.cloud.common.saas.contents; + +/** + * @Author: DongZeLiang + * @date: 2024/6/3 + * @Description: SAAS常量 + * @Version: 1.0 + */ +public class SaaSConstant { + + public final static String SAAS_KEY = "ent-code"; +} diff --git a/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/saas/domain/Datasource.java b/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/saas/domain/Datasource.java new file mode 100644 index 0000000..cfda448 --- /dev/null +++ b/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/saas/domain/Datasource.java @@ -0,0 +1,26 @@ +package com.muyu.cloud.common.saas.domain; + + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@TableName(value = "datasource", autoResultMap = true) +public class Datasource { + + @TableId(value = "id",type = IdType.AUTO) + private Integer id; + + public String firmName; + + public String databaseName; + + + +} diff --git a/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/saas/domain/model/EntInfo.java b/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/saas/domain/model/EntInfo.java new file mode 100644 index 0000000..5c1985a --- /dev/null +++ b/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/saas/domain/model/EntInfo.java @@ -0,0 +1,25 @@ +package com.muyu.cloud.common.saas.domain.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @Author: DongZeLiang + * @date: 2024/6/3 + * @Description: 企业信息 + * @Version: 1.0 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class EntInfo { + + private String entCode; + + private String ip; + + private Integer port; +} diff --git a/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/saas/exception/SaaSException.java b/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/saas/exception/SaaSException.java new file mode 100644 index 0000000..b2b4cc7 --- /dev/null +++ b/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/saas/exception/SaaSException.java @@ -0,0 +1,28 @@ +package com.muyu.cloud.common.saas.exception; + + +import com.muyu.common.core.exception.ServiceException; + +/** + * @Author: DongZeLiang + * @date: 2024/6/3 + * @Description: SaaS异常类 + * @Version: 1.0 + */ +public class SaaSException extends ServiceException { + + public SaaSException (String message, Integer code) { + super(message, code); + } + + public SaaSException (String message) { + super(message); + } + + /** + * 空构造方法,避免反序列化问题 + */ + public SaaSException () { + super(); + } +} diff --git a/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/saas/interceptor/SaaSInterceptor.java b/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/saas/interceptor/SaaSInterceptor.java new file mode 100644 index 0000000..0efef00 --- /dev/null +++ b/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/saas/interceptor/SaaSInterceptor.java @@ -0,0 +1,53 @@ +package com.muyu.cloud.common.saas.interceptor; + +import com.alibaba.fastjson2.JSONObject; +import com.muyu.cloud.common.saas.contents.SaaSConstant; +import com.muyu.cloud.common.many.datasource.holder.DynamicDataSourceHolder; +import com.muyu.cloud.common.saas.exception.SaaSException; +import com.muyu.cloud.common.many.datasource.role.DynamicDataSource; +import com.muyu.common.core.utils.ServletUtils; +import com.muyu.common.core.utils.SpringUtils; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.AsyncHandlerInterceptor; + + +/** + * @Author: DongZeLiang + * @date: 2024/6/3 + * @Description: SAAS拦截器 + * @Version: 1.0 + */ +public class SaaSInterceptor implements AsyncHandlerInterceptor { + + /** + * 之前 + */ + @Override + public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + if (!(handler instanceof HandlerMethod)) { + return true; + } + + String SaaSKey = ServletUtils.getHeader(request, SaaSConstant.SAAS_KEY); + if (SaaSKey == null) { + throw new SaaSException("SaaS非法访问"); + }else { + DynamicDataSource dynamicDataSource = SpringUtils.getBean(DynamicDataSource.class); + if (!dynamicDataSource.hashKey(SaaSKey)){ + throw new SaaSException("SaaS非法访问"); + } + } + DynamicDataSourceHolder.setDynamicDataSourceKey(SaaSKey); + return true; + } + + /** + * 之后 + */ + @Override + public void afterConcurrentHandlingStarted (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + DynamicDataSourceHolder.removeDynamicDataSourceKey(); + } +} diff --git a/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/saas/interceptor/WebMvcSaaSConfig.java b/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/saas/interceptor/WebMvcSaaSConfig.java new file mode 100644 index 0000000..4c79417 --- /dev/null +++ b/cloud-common/cloud-common-saas/src/main/java/com/muyu/cloud/common/saas/interceptor/WebMvcSaaSConfig.java @@ -0,0 +1,31 @@ +package com.muyu.cloud.common.saas.interceptor; + +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * 拦截器配置 + * + * @author muyu + */ +public class WebMvcSaaSConfig implements WebMvcConfigurer { + /** + * 不需要拦截的地址 + */ + public static final String[] excludeUrls = {"/login", "/logout", "/refresh"}; + + @Override + public void addInterceptors (InterceptorRegistry registry) { + registry.addInterceptor(getHeaderInterceptor()) + .addPathPatterns("/**") + .excludePathPatterns(excludeUrls) + .order(-10); + } + + /** + * 自定义请求头拦截器 + */ + public SaaSInterceptor getHeaderInterceptor () { + return new SaaSInterceptor(); + } +} diff --git a/cloud-common/cloud-common-saas/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/cloud-common/cloud-common-saas/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..70148cf --- /dev/null +++ b/cloud-common/cloud-common-saas/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,3 @@ +com.muyu.cloud.common.saas.interceptor.WebMvcSaaSConfig +com.muyu.cloud.common.many.datasource.ManyDataSource +com.muyu.cloud.common.many.datasource.factory.DruidDataSourceFactory diff --git a/cloud-common/cloud-common-seata/pom.xml b/cloud-common/cloud-common-seata/pom.xml new file mode 100644 index 0000000..3725683 --- /dev/null +++ b/cloud-common/cloud-common-seata/pom.xml @@ -0,0 +1,27 @@ + + + + com.muyu + cloud-common + 3.6.3 + + 4.0.0 + + cloud-common-seata + + + cloud-common-seata分布式事务 + + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-seata + + + + diff --git a/cloud-common/cloud-common-security/pom.xml b/cloud-common/cloud-common-security/pom.xml new file mode 100644 index 0000000..f1a480a --- /dev/null +++ b/cloud-common/cloud-common-security/pom.xml @@ -0,0 +1,39 @@ + + + + com.muyu + cloud-common + 3.6.3 + + 4.0.0 + + cloud-common-security + + + cloud-common-security安全模块 + + + + + + + org.springframework + spring-webmvc + + + + + com.muyu + cloud-common-redis + + + + + com.muyu + cloud-common-system + + + + + diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/EnableCustomConfig.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/EnableCustomConfig.java new file mode 100644 index 0000000..ca7a07a --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/EnableCustomConfig.java @@ -0,0 +1,26 @@ +package com.muyu.common.security.annotation; + +import com.muyu.common.security.config.ApplicationConfig; +import com.muyu.common.security.feign.FeignAutoConfiguration; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.EnableAspectJAutoProxy; +import org.springframework.context.annotation.Import; +import org.springframework.scheduling.annotation.EnableAsync; + +import java.lang.annotation.*; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +// 表示通过aop框架暴露该代理对象,AopContext能够访问 +@EnableAspectJAutoProxy(exposeProxy = true) +// 指定要扫描的Mapper类的包的路径 +@MapperScan("com.muyu.**.mapper") +// 开启线程异步执行 +@EnableAsync +// 自动加载类 +@Import({ApplicationConfig.class, FeignAutoConfiguration.class}) +public @interface EnableCustomConfig { + +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/EnableMyFeignClients.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/EnableMyFeignClients.java new file mode 100644 index 0000000..7a59fa4 --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/EnableMyFeignClients.java @@ -0,0 +1,27 @@ +package com.muyu.common.security.annotation; + +import org.springframework.cloud.openfeign.EnableFeignClients; + +import java.lang.annotation.*; + +/** + * 自定义feign注解 + * 添加basePackages路径 + * + * @author muyu + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@EnableFeignClients +public @interface EnableMyFeignClients { + String[] value () default {}; + + String[] basePackages () default {"com.muyu"}; + + Class[] basePackageClasses () default {}; + + Class[] defaultConfiguration () default {}; + + Class[] clients () default {}; +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/InnerAuth.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/InnerAuth.java new file mode 100644 index 0000000..092a573 --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/InnerAuth.java @@ -0,0 +1,18 @@ +package com.muyu.common.security.annotation; + +import java.lang.annotation.*; + +/** + * 内部认证注解 + * + * @author muyu + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface InnerAuth { + /** + * 是否校验用户信息 + */ + boolean isUser () default false; +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/Logical.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/Logical.java new file mode 100644 index 0000000..0be306a --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/Logical.java @@ -0,0 +1,18 @@ +package com.muyu.common.security.annotation; + +/** + * 权限注解的验证模式 + * + * @author muyu + */ +public enum Logical { + /** + * 必须具有所有的元素 + */ + AND, + + /** + * 只需具有其中一个元素 + */ + OR +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/RequiresLogin.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/RequiresLogin.java new file mode 100644 index 0000000..4eff911 --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/RequiresLogin.java @@ -0,0 +1,16 @@ +package com.muyu.common.security.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 登录认证:只有登录之后才能进入该方法 + * + * @author muyu + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface RequiresLogin { +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/RequiresPermissions.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/RequiresPermissions.java new file mode 100644 index 0000000..8d95bb4 --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/RequiresPermissions.java @@ -0,0 +1,25 @@ +package com.muyu.common.security.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 权限认证:必须具有指定权限才能进入该方法 + * + * @author muyu + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface RequiresPermissions { + /** + * 需要校验的权限码 + */ + String[] value () default {}; + + /** + * 验证模式:AND | OR,默认AND + */ + Logical logical () default Logical.AND; +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/RequiresRoles.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/RequiresRoles.java new file mode 100644 index 0000000..78911cc --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/annotation/RequiresRoles.java @@ -0,0 +1,25 @@ +package com.muyu.common.security.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 角色认证:必须具有指定角色标识才能进入该方法 + * + * @author muyu + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface RequiresRoles { + /** + * 需要校验的角色标识 + */ + String[] value () default {}; + + /** + * 验证逻辑:AND | OR,默认AND + */ + Logical logical () default Logical.AND; +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/aspect/InnerAuthAspect.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/aspect/InnerAuthAspect.java new file mode 100644 index 0000000..1707742 --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/aspect/InnerAuthAspect.java @@ -0,0 +1,46 @@ +package com.muyu.common.security.aspect; + +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.core.exception.InnerAuthException; +import com.muyu.common.core.utils.ServletUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.security.annotation.InnerAuth; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.core.Ordered; +import org.springframework.stereotype.Component; + +/** + * 内部服务调用验证处理 + * + * @author muyu + */ +@Aspect +@Component +public class InnerAuthAspect implements Ordered { + @Around("@annotation(innerAuth)") + public Object innerAround (ProceedingJoinPoint point, InnerAuth innerAuth) throws Throwable { + String source = ServletUtils.getRequest().getHeader(SecurityConstants.FROM_SOURCE); + // 内部请求验证 + if (!StringUtils.equals(SecurityConstants.INNER, source)) { + throw new InnerAuthException("没有内部访问权限,不允许访问"); + } + + String userid = ServletUtils.getRequest().getHeader(SecurityConstants.DETAILS_USER_ID); + String username = ServletUtils.getRequest().getHeader(SecurityConstants.DETAILS_USERNAME); + // 用户信息验证 + if (innerAuth.isUser() && (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username))) { + throw new InnerAuthException("没有设置用户信息,不允许访问 "); + } + return point.proceed(); + } + + /** + * 确保在权限认证aop执行前执行 + */ + @Override + public int getOrder () { + return Ordered.HIGHEST_PRECEDENCE + 1; + } +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/aspect/PreAuthorizeAspect.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/aspect/PreAuthorizeAspect.java new file mode 100644 index 0000000..4cdd933 --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/aspect/PreAuthorizeAspect.java @@ -0,0 +1,89 @@ +package com.muyu.common.security.aspect; + +import com.muyu.common.security.annotation.RequiresLogin; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.security.annotation.RequiresRoles; +import com.muyu.common.security.auth.AuthUtil; +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.springframework.stereotype.Component; + +import java.lang.reflect.Method; + +/** + * 基于 Spring Aop 的注解鉴权 + * + * @author kong + */ +@Aspect +@Component +public class PreAuthorizeAspect { + /** + * 定义AOP签名 (切入所有使用鉴权注解的方法) + */ + public static final String POINTCUT_SIGN = " @annotation(com.muyu.common.security.annotation.RequiresLogin) || " + + "@annotation(com.muyu.common.security.annotation.RequiresPermissions) || " + + "@annotation(com.muyu.common.security.annotation.RequiresRoles)"; + + /** + * 构建 + */ + public PreAuthorizeAspect () { + } + + /** + * 声明AOP签名 + */ + @Pointcut(POINTCUT_SIGN) + public void pointcut () { + } + + /** + * 环绕切入 + * + * @param joinPoint 切面对象 + * + * @return 底层方法执行后的返回值 + * + * @throws Throwable 底层方法抛出的异常 + */ + @Around("pointcut()") + public Object around (ProceedingJoinPoint joinPoint) throws Throwable { + // 注解鉴权 + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + checkMethodAnnotation(signature.getMethod()); + try { + // 执行原有逻辑 + Object obj = joinPoint.proceed(); + return obj; + } catch (Throwable e) { + throw e; + } + } + + /** + * 对一个Method对象进行注解检查 + */ + public void checkMethodAnnotation (Method method) { + // 校验 @RequiresLogin 注解 + RequiresLogin requiresLogin = method.getAnnotation(RequiresLogin.class); + if (requiresLogin != null) { + AuthUtil.checkLogin(); + } + + // 校验 @RequiresRoles 注解 + RequiresRoles requiresRoles = method.getAnnotation(RequiresRoles.class); + if (requiresRoles != null) { + AuthUtil.checkRole(requiresRoles); + } + + // 校验 @RequiresPermissions 注解 + RequiresPermissions requiresPermissions = method.getAnnotation(RequiresPermissions.class); + if (requiresPermissions != null) { + AuthUtil.checkPermi(requiresPermissions); + } + } +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/auth/AuthLogic.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/auth/AuthLogic.java new file mode 100644 index 0000000..beb3426 --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/auth/AuthLogic.java @@ -0,0 +1,327 @@ +package com.muyu.common.security.auth; + +import com.muyu.common.core.context.SecurityContextHolder; +import com.muyu.common.core.exception.auth.NotLoginException; +import com.muyu.common.core.exception.auth.NotPermissionException; +import com.muyu.common.core.exception.auth.NotRoleException; +import com.muyu.common.core.utils.SpringUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.security.annotation.Logical; +import com.muyu.common.security.annotation.RequiresLogin; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.security.annotation.RequiresRoles; +import com.muyu.common.security.service.TokenService; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.LoginUser; +import org.springframework.util.PatternMatchUtils; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +/** + * Token 权限验证,逻辑实现类 + * + * @author muyu + */ +public class AuthLogic { + /** + * 所有权限标识 + */ + private static final String ALL_PERMISSION = "*:*:*"; + + /** + * 管理员角色权限标识 + */ + private static final String SUPER_ADMIN = "admin"; + + public TokenService tokenService = SpringUtils.getBean(TokenService.class); + + /** + * 会话注销 + */ + public void logout () { + String token = SecurityUtils.getToken(); + if (token == null) { + return; + } + logoutByToken(token); + } + + /** + * 会话注销,根据指定Token + */ + public void logoutByToken (String token) { + tokenService.delLoginUser(token); + } + + /** + * 检验用户是否已经登录,如未登录,则抛出异常 + */ + public void checkLogin () { + getLoginUser(); + } + + /** + * 获取当前用户缓存信息, 如果未登录,则抛出异常 + * + * @return 用户缓存信息 + */ + public LoginUser getLoginUser () { + String token = SecurityUtils.getToken(); + if (token == null) { + throw new NotLoginException("未提供token"); + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (loginUser == null) { + throw new NotLoginException("无效的token"); + } + return loginUser; + } + + /** + * 获取当前用户缓存信息, 如果未登录,则抛出异常 + * + * @param token 前端传递的认证信息 + * + * @return 用户缓存信息 + */ + public LoginUser getLoginUser (String token) { + return tokenService.getLoginUser(token); + } + + /** + * 验证当前用户有效期, 如果相差不足120分钟,自动刷新缓存 + * + * @param loginUser 当前用户信息 + */ + public void verifyLoginUserExpire (LoginUser loginUser) { + tokenService.verifyToken(loginUser); + } + + /** + * 验证用户是否具备某权限 + * + * @param permission 权限字符串 + * + * @return 用户是否具备某权限 + */ + public boolean hasPermi (String permission) { + return hasPermi(getPermiList(), permission); + } + + /** + * 验证用户是否具备某权限, 如果验证未通过,则抛出异常: NotPermissionException + * + * @param permission 权限字符串 + * + * @return 用户是否具备某权限 + */ + public void checkPermi (String permission) { + if (!hasPermi(getPermiList(), permission)) { + throw new NotPermissionException(permission); + } + } + + /** + * 根据注解(@RequiresPermissions)鉴权, 如果验证未通过,则抛出异常: NotPermissionException + * + * @param requiresPermissions 注解对象 + */ + public void checkPermi (RequiresPermissions requiresPermissions) { + SecurityContextHolder.setPermission(StringUtils.join(requiresPermissions.value(), ",")); + if (requiresPermissions.logical() == Logical.AND) { + checkPermiAnd(requiresPermissions.value()); + } else { + checkPermiOr(requiresPermissions.value()); + } + } + + /** + * 验证用户是否含有指定权限,必须全部拥有 + * + * @param permissions 权限列表 + */ + public void checkPermiAnd (String... permissions) { + Set permissionList = getPermiList(); + for (String permission : permissions) { + if (!hasPermi(permissionList, permission)) { + throw new NotPermissionException(permission); + } + } + } + + /** + * 验证用户是否含有指定权限,只需包含其中一个 + * + * @param permissions 权限码数组 + */ + public void checkPermiOr (String... permissions) { + Set permissionList = getPermiList(); + for (String permission : permissions) { + if (hasPermi(permissionList, permission)) { + return; + } + } + if (permissions.length > 0) { + throw new NotPermissionException(permissions); + } + } + + /** + * 判断用户是否拥有某个角色 + * + * @param role 角色标识 + * + * @return 用户是否具备某角色 + */ + public boolean hasRole (String role) { + return hasRole(getRoleList(), role); + } + + /** + * 判断用户是否拥有某个角色, 如果验证未通过,则抛出异常: NotRoleException + * + * @param role 角色标识 + */ + public void checkRole (String role) { + if (!hasRole(role)) { + throw new NotRoleException(role); + } + } + + /** + * 根据注解(@RequiresRoles)鉴权 + * + * @param requiresRoles 注解对象 + */ + public void checkRole (RequiresRoles requiresRoles) { + if (requiresRoles.logical() == Logical.AND) { + checkRoleAnd(requiresRoles.value()); + } else { + checkRoleOr(requiresRoles.value()); + } + } + + /** + * 验证用户是否含有指定角色,必须全部拥有 + * + * @param roles 角色标识数组 + */ + public void checkRoleAnd (String... roles) { + Set roleList = getRoleList(); + for (String role : roles) { + if (!hasRole(roleList, role)) { + throw new NotRoleException(role); + } + } + } + + /** + * 验证用户是否含有指定角色,只需包含其中一个 + * + * @param roles 角色标识数组 + */ + public void checkRoleOr (String... roles) { + Set roleList = getRoleList(); + for (String role : roles) { + if (hasRole(roleList, role)) { + return; + } + } + if (roles.length > 0) { + throw new NotRoleException(roles); + } + } + + /** + * 根据注解(@RequiresLogin)鉴权 + * + * @param at 注解对象 + */ + public void checkByAnnotation (RequiresLogin at) { + this.checkLogin(); + } + + /** + * 根据注解(@RequiresRoles)鉴权 + * + * @param at 注解对象 + */ + public void checkByAnnotation (RequiresRoles at) { + String[] roleArray = at.value(); + if (at.logical() == Logical.AND) { + this.checkRoleAnd(roleArray); + } else { + this.checkRoleOr(roleArray); + } + } + + /** + * 根据注解(@RequiresPermissions)鉴权 + * + * @param at 注解对象 + */ + public void checkByAnnotation (RequiresPermissions at) { + String[] permissionArray = at.value(); + if (at.logical() == Logical.AND) { + this.checkPermiAnd(permissionArray); + } else { + this.checkPermiOr(permissionArray); + } + } + + /** + * 获取当前账号的角色列表 + * + * @return 角色列表 + */ + public Set getRoleList () { + try { + LoginUser loginUser = getLoginUser(); + return loginUser.getRoles(); + } catch (Exception e) { + return new HashSet<>(); + } + } + + /** + * 获取当前账号的权限列表 + * + * @return 权限列表 + */ + public Set getPermiList () { + try { + LoginUser loginUser = getLoginUser(); + return loginUser.getPermissions(); + } catch (Exception e) { + return new HashSet<>(); + } + } + + /** + * 判断是否包含权限 + * + * @param authorities 权限列表 + * @param permission 权限字符串 + * + * @return 用户是否具备某权限 + */ + public boolean hasPermi (Collection authorities, String permission) { + return authorities.stream().filter(StringUtils::hasText) + .anyMatch(x -> ALL_PERMISSION.equals(x) || PatternMatchUtils.simpleMatch(x, permission)); + } + + /** + * 判断是否包含角色 + * + * @param roles 角色列表 + * @param role 角色 + * + * @return 用户是否具备某角色权限 + */ + public boolean hasRole (Collection roles, String role) { + return roles.stream().filter(StringUtils::hasText) + .anyMatch(x -> SUPER_ADMIN.equals(x) || PatternMatchUtils.simpleMatch(x, role)); + } +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/auth/AuthUtil.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/auth/AuthUtil.java new file mode 100644 index 0000000..131d150 --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/auth/AuthUtil.java @@ -0,0 +1,154 @@ +package com.muyu.common.security.auth; + +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.security.annotation.RequiresRoles; +import com.muyu.common.system.domain.LoginUser; + +/** + * Token 权限验证工具类 + * + * @author muyu + */ +public class AuthUtil { + /** + * 底层的 AuthLogic 对象 + */ + public static AuthLogic authLogic = new AuthLogic(); + + /** + * 会话注销 + */ + public static void logout () { + authLogic.logout(); + } + + /** + * 会话注销,根据指定Token + * + * @param token 指定token + */ + public static void logoutByToken (String token) { + authLogic.logoutByToken(token); + } + + /** + * 检验当前会话是否已经登录,如未登录,则抛出异常 + */ + public static void checkLogin () { + authLogic.checkLogin(); + } + + /** + * 获取当前登录用户信息 + * + * @param token 指定token + * + * @return 用户信息 + */ + public static LoginUser getLoginUser (String token) { + return authLogic.getLoginUser(token); + } + + /** + * 验证当前用户有效期 + * + * @param loginUser 用户信息 + */ + public static void verifyLoginUserExpire (LoginUser loginUser) { + authLogic.verifyLoginUserExpire(loginUser); + } + + /** + * 当前账号是否含有指定角色标识, 返回true或false + * + * @param role 角色标识 + * + * @return 是否含有指定角色标识 + */ + public static boolean hasRole (String role) { + return authLogic.hasRole(role); + } + + /** + * 当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException + * + * @param role 角色标识 + */ + public static void checkRole (String role) { + authLogic.checkRole(role); + } + + /** + * 根据注解传入参数鉴权, 如果验证未通过,则抛出异常: NotRoleException + * + * @param requiresRoles 角色权限注解 + */ + public static void checkRole (RequiresRoles requiresRoles) { + authLogic.checkRole(requiresRoles); + } + + /** + * 当前账号是否含有指定角色标识 [指定多个,必须全部验证通过] + * + * @param roles 角色标识数组 + */ + public static void checkRoleAnd (String... roles) { + authLogic.checkRoleAnd(roles); + } + + /** + * 当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可] + * + * @param roles 角色标识数组 + */ + public static void checkRoleOr (String... roles) { + authLogic.checkRoleOr(roles); + } + + /** + * 当前账号是否含有指定权限, 返回true或false + * + * @param permission 权限码 + * + * @return 是否含有指定权限 + */ + public static boolean hasPermi (String permission) { + return authLogic.hasPermi(permission); + } + + /** + * 当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException + * + * @param permission 权限码 + */ + public static void checkPermi (String permission) { + authLogic.checkPermi(permission); + } + + /** + * 根据注解传入参数鉴权, 如果验证未通过,则抛出异常: NotPermissionException + * + * @param requiresPermissions 权限注解 + */ + public static void checkPermi (RequiresPermissions requiresPermissions) { + authLogic.checkPermi(requiresPermissions); + } + + /** + * 当前账号是否含有指定权限 [指定多个,必须全部验证通过] + * + * @param permissions 权限码数组 + */ + public static void checkPermiAnd (String... permissions) { + authLogic.checkPermiAnd(permissions); + } + + /** + * 当前账号是否含有指定权限 [指定多个,只要其一验证通过即可] + * + * @param permissions 权限码数组 + */ + public static void checkPermiOr (String... permissions) { + authLogic.checkPermiOr(permissions); + } +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/config/ApplicationConfig.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/config/ApplicationConfig.java new file mode 100644 index 0000000..b78abbf --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/config/ApplicationConfig.java @@ -0,0 +1,21 @@ +package com.muyu.common.security.config; + +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.context.annotation.Bean; + +import java.util.TimeZone; + +/** + * 系统配置 + * + * @author muyu + */ +public class ApplicationConfig { + /** + * 时区配置 + */ + @Bean + public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization () { + return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault()); + } +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/config/WebMvcConfig.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/config/WebMvcConfig.java new file mode 100644 index 0000000..8acde35 --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/config/WebMvcConfig.java @@ -0,0 +1,32 @@ +package com.muyu.common.security.config; + +import com.muyu.common.security.interceptor.HeaderInterceptor; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * 拦截器配置 + * + * @author muyu + */ +public class WebMvcConfig implements WebMvcConfigurer { + /** + * 不需要拦截地址 + */ + public static final String[] excludeUrls = {"/login", "/logout", "/refresh"}; + + @Override + public void addInterceptors (InterceptorRegistry registry) { + registry.addInterceptor(getHeaderInterceptor()) + .addPathPatterns("/**") + .excludePathPatterns(excludeUrls) + .order(-10); + } + + /** + * 自定义请求头拦截器 + */ + public HeaderInterceptor getHeaderInterceptor () { + return new HeaderInterceptor(); + } +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/feign/FeignAutoConfiguration.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/feign/FeignAutoConfiguration.java new file mode 100644 index 0000000..4bfda6d --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/feign/FeignAutoConfiguration.java @@ -0,0 +1,18 @@ +package com.muyu.common.security.feign; + +import feign.RequestInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Feign 配置注册 + * + * @author muyu + **/ +@Configuration +public class FeignAutoConfiguration { + @Bean + public RequestInterceptor requestInterceptor () { + return new FeignRequestInterceptor(); + } +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/feign/FeignRequestInterceptor.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/feign/FeignRequestInterceptor.java new file mode 100644 index 0000000..8ae21da --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/feign/FeignRequestInterceptor.java @@ -0,0 +1,48 @@ +package com.muyu.common.security.feign; + +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.core.utils.ServletUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.utils.ip.IpUtils; +import feign.RequestInterceptor; +import feign.RequestTemplate; +import org.springframework.stereotype.Component; + +import jakarta.servlet.http.HttpServletRequest; +import java.util.Map; + +/** + * feign 请求拦截器 + * + * @author muyu + */ +@Component +public class FeignRequestInterceptor implements RequestInterceptor { + @Override + public void apply (RequestTemplate requestTemplate) { + HttpServletRequest httpServletRequest = ServletUtils.getRequest(); + if (StringUtils.isNotNull(httpServletRequest)) { + Map headers = ServletUtils.getHeaders(httpServletRequest); + // 传递用户信息请求头,防止丢失 + String userId = headers.get(SecurityConstants.DETAILS_USER_ID); + if (StringUtils.isNotEmpty(userId)) { + requestTemplate.header(SecurityConstants.DETAILS_USER_ID, userId); + } + String userKey = headers.get(SecurityConstants.USER_KEY); + if (StringUtils.isNotEmpty(userKey)) { + requestTemplate.header(SecurityConstants.USER_KEY, userKey); + } + String userName = headers.get(SecurityConstants.DETAILS_USERNAME); + if (StringUtils.isNotEmpty(userName)) { + requestTemplate.header(SecurityConstants.DETAILS_USERNAME, userName); + } + String authentication = headers.get(SecurityConstants.AUTHORIZATION_HEADER); + if (StringUtils.isNotEmpty(authentication)) { + requestTemplate.header(SecurityConstants.AUTHORIZATION_HEADER, authentication); + } + + // 配置客户端IP + requestTemplate.header("X-Forwarded-For", IpUtils.getIpAddr()); + } + } +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/handler/GlobalExceptionHandler.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/handler/GlobalExceptionHandler.java new file mode 100644 index 0000000..170211c --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/handler/GlobalExceptionHandler.java @@ -0,0 +1,147 @@ +package com.muyu.common.security.handler; + +import com.muyu.common.core.constant.HttpStatus; +import com.muyu.common.core.exception.DemoModeException; +import com.muyu.common.core.exception.InnerAuthException; +import com.muyu.common.core.exception.ServiceException; +import com.muyu.common.core.exception.auth.NotPermissionException; +import com.muyu.common.core.exception.auth.NotRoleException; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.domain.Result; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +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 jakarta.servlet.http.HttpServletRequest; + +/** + * 全局异常处理器 + * + * @author muyu + */ +@RestControllerAdvice +public class GlobalExceptionHandler { + private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + /** + * 权限码异常 + */ + @ExceptionHandler(NotPermissionException.class) + public Result handleNotPermissionException (NotPermissionException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',权限码校验失败'{}'", requestURI, e.getMessage()); + return Result.error(HttpStatus.FORBIDDEN, "没有访问权限,请联系管理员授权"); + } + + /** + * 角色权限异常 + */ + @ExceptionHandler(NotRoleException.class) + public Result handleNotRoleException (NotRoleException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',角色权限校验失败'{}'", requestURI, e.getMessage()); + return Result.error(HttpStatus.FORBIDDEN, "没有访问权限,请联系管理员授权"); + } + + /** + * 请求方式不支持 + */ + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + public Result handleHttpRequestMethodNotSupported (HttpRequestMethodNotSupportedException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod()); + return Result.error(e.getMessage()); + } + + /** + * 业务异常 + */ + @ExceptionHandler(ServiceException.class) + public Result handleServiceException (ServiceException e, HttpServletRequest request) { + log.error(e.getMessage(), e); + Integer code = e.getCode(); + return StringUtils.isNotNull(code) ? Result.error(code, e.getMessage()) : Result.error(e.getMessage()); + } + + /** + * 请求路径中缺少必需的路径变量 + */ + @ExceptionHandler(MissingPathVariableException.class) + public Result handleMissingPathVariableException (MissingPathVariableException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI, e); + return Result.error(String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName())); + } + + /** + * 请求参数类型不匹配 + */ + @ExceptionHandler(MethodArgumentTypeMismatchException.class) + public Result handleMethodArgumentTypeMismatchException (MethodArgumentTypeMismatchException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI, e); + return Result.error(String.format("请求参数类型不匹配,参数[%s]要求类型为:'%s',但输入值为:'%s'", e.getName(), e.getRequiredType().getName(), e.getValue())); + } + + /** + * 拦截未知的运行时异常 + */ + @ExceptionHandler(RuntimeException.class) + public Result handleRuntimeException (RuntimeException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生未知异常.", requestURI, e); + return Result.error(e.getMessage()); + } + + /** + * 系统异常 + */ + @ExceptionHandler(Exception.class) + public Result handleException (Exception e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生系统异常.", requestURI, e); + return Result.error(e.getMessage()); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(BindException.class) + public Result handleBindException (BindException e) { + log.error(e.getMessage(), e); + String message = e.getAllErrors().get(0).getDefaultMessage(); + return Result.error(message); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public Object handleMethodArgumentNotValidException (MethodArgumentNotValidException e) { + log.error(e.getMessage(), e); + String message = e.getBindingResult().getFieldError().getDefaultMessage(); + return Result.error(message); + } + + /** + * 内部认证异常 + */ + @ExceptionHandler(InnerAuthException.class) + public Result handleInnerAuthException (InnerAuthException e) { + return Result.error(e.getMessage()); + } + + /** + * 演示模式异常 + */ + @ExceptionHandler(DemoModeException.class) + public Result handleDemoModeException (DemoModeException e) { + return Result.error("演示模式,不允许操作"); + } +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/interceptor/HeaderInterceptor.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/interceptor/HeaderInterceptor.java new file mode 100644 index 0000000..0b1938d --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/interceptor/HeaderInterceptor.java @@ -0,0 +1,50 @@ +package com.muyu.common.security.interceptor; + +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.core.context.SecurityContextHolder; +import com.muyu.common.core.utils.ServletUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.security.auth.AuthUtil; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.LoginUser; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.AsyncHandlerInterceptor; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +/** + * 自定义请求头拦截器,将Header数据封装到线程变量中方便获取 + * 注意:此拦截器会同时验证当前用户有效期自动刷新有效期 + * + * @author muyu + */ +public class HeaderInterceptor implements AsyncHandlerInterceptor { + + @Override + public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + if (!(handler instanceof HandlerMethod)) { + return true; + } + + SecurityContextHolder.setUserId(ServletUtils.getHeader(request, SecurityConstants.DETAILS_USER_ID)); + SecurityContextHolder.setUserName(ServletUtils.getHeader(request, SecurityConstants.DETAILS_USERNAME)); + SecurityContextHolder.setUserKey(ServletUtils.getHeader(request, SecurityConstants.USER_KEY)); + + String token = SecurityUtils.getToken(); + if (StringUtils.isNotEmpty(token)) { + LoginUser loginUser = AuthUtil.getLoginUser(token); + if (StringUtils.isNotNull(loginUser)) { + AuthUtil.verifyLoginUserExpire(loginUser); + SecurityContextHolder.set(SecurityConstants.LOGIN_USER, loginUser); + } + } + return true; + } + + @Override + public void afterCompletion (HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) + throws Exception { + SecurityContextHolder.remove(); + } +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/service/TokenService.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/service/TokenService.java new file mode 100644 index 0000000..cb5c6d0 --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/service/TokenService.java @@ -0,0 +1,153 @@ +package com.muyu.common.security.service; + +import com.muyu.common.core.constant.CacheConstants; +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.core.utils.JwtUtils; +import com.muyu.common.core.utils.ServletUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.utils.ip.IpUtils; +import com.muyu.common.core.utils.uuid.IdUtils; +import com.muyu.common.redis.service.RedisService; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.LoginUser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import jakarta.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * token验证处理 + * + * @author muyu + */ +@Component +public class TokenService { + protected static final long MILLIS_SECOND = 1000; + protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND; + private final static Long MILLIS_MINUTE_TEN = CacheConstants.REFRESH_TIME * MILLIS_MINUTE; + private static final Logger log = LoggerFactory.getLogger(TokenService.class); + private final static long expireTime = CacheConstants.EXPIRATION; + + private final static String ACCESS_TOKEN = CacheConstants.LOGIN_TOKEN_KEY; + @Autowired + private RedisService redisService; + + /** + * 创建令牌 + */ + public Map createToken (LoginUser loginUser) { + String token = IdUtils.fastUUID(); + Long userId = loginUser.getSysUser().getUserId(); + String userName = loginUser.getSysUser().getUserName(); + loginUser.setToken(token); + loginUser.setUserid(userId); + loginUser.setUsername(userName); + loginUser.setIpaddr(IpUtils.getIpAddr()); + refreshToken(loginUser); + + // Jwt存储信息 + Map claimsMap = new HashMap(); + claimsMap.put(SecurityConstants.USER_KEY, token); + claimsMap.put(SecurityConstants.DETAILS_USER_ID, userId); + claimsMap.put(SecurityConstants.DETAILS_USERNAME, userName); + claimsMap.put(SecurityConstants.SAAS_KEY,loginUser.getSysUser().getDatabaseName()); + // 接口返回信息 + Map rspMap = new HashMap(); + rspMap.put("access_token", JwtUtils.createToken(claimsMap)); + rspMap.put("expires_in", expireTime); + return rspMap; + } + + /** + * 获取用户身份信息 + * + * @return 用户信息 + */ + public LoginUser getLoginUser () { + return getLoginUser(ServletUtils.getRequest()); + } + + /** + * 设置用户身份信息 + */ + public void setLoginUser (LoginUser loginUser) { + if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken())) { + refreshToken(loginUser); + } + } + + /** + * 获取用户身份信息 + * + * @return 用户信息 + */ + public LoginUser getLoginUser (HttpServletRequest request) { + // 获取请求携带的令牌 + String token = SecurityUtils.getToken(request); + return getLoginUser(token); + } + + /** + * 获取用户身份信息 + * + * @return 用户信息 + */ + public LoginUser getLoginUser (String token) { + LoginUser user = null; + try { + if (StringUtils.isNotEmpty(token)) { + String userkey = JwtUtils.getUserKey(token); + user = redisService.getCacheObject(getTokenKey(userkey)); + return user; + } + } catch (Exception e) { + log.error("获取用户信息异常'{}'", e.getMessage()); + } + return user; + } + + /** + * 删除用户缓存信息 + */ + public void delLoginUser (String token) { + if (StringUtils.isNotEmpty(token)) { + String userkey = JwtUtils.getUserKey(token); + redisService.deleteObject(getTokenKey(userkey)); + } + } + + /** + * 验证令牌有效期,相差不足120分钟,自动刷新缓存 + * + * @param loginUser + */ + public void verifyToken (LoginUser loginUser) { + long expireTime = loginUser.getExpireTime(); + long currentTime = System.currentTimeMillis(); + if (expireTime - currentTime <= MILLIS_MINUTE_TEN) { + refreshToken(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()); + redisService.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES); + } + + private String getTokenKey (String token) { + return ACCESS_TOKEN + token; + } +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/utils/DictUtils.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/utils/DictUtils.java new file mode 100644 index 0000000..04ee068 --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/utils/DictUtils.java @@ -0,0 +1,71 @@ +package com.muyu.common.security.utils; + +import com.alibaba.fastjson2.JSONArray; +import com.muyu.common.core.constant.CacheConstants; +import com.muyu.common.core.utils.SpringUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.redis.service.RedisService; +import com.muyu.common.system.domain.SysDictData; + +import java.util.Collection; +import java.util.List; + +/** + * 字典工具类 + * + * @author muyu + */ +public class DictUtils { + /** + * 设置字典缓存 + * + * @param key 参数键 + * @param dictDatas 字典数据列表 + */ + public static void setDictCache (String key, List dictDatas) { + SpringUtils.getBean(RedisService.class).setCacheObject(getCacheKey(key), dictDatas); + } + + /** + * 获取字典缓存 + * + * @param key 参数键 + * + * @return dictDatas 字典数据列表 + */ + public static List getDictCache (String key) { + JSONArray arrayCache = SpringUtils.getBean(RedisService.class).getCacheObject(getCacheKey(key)); + if (StringUtils.isNotNull(arrayCache)) { + return arrayCache.toList(SysDictData.class); + } + return null; + } + + /** + * 删除指定字典缓存 + * + * @param key 字典键 + */ + public static void removeDictCache (String key) { + SpringUtils.getBean(RedisService.class).deleteObject(getCacheKey(key)); + } + + /** + * 清空字典缓存 + */ + public static void clearDictCache () { + Collection keys = SpringUtils.getBean(RedisService.class).keys(CacheConstants.SYS_DICT_KEY + "*"); + SpringUtils.getBean(RedisService.class).deleteObject(keys); + } + + /** + * 设置cache key + * + * @param configKey 参数键 + * + * @return 缓存键key + */ + public static String getCacheKey (String configKey) { + return CacheConstants.SYS_DICT_KEY + configKey; + } +} diff --git a/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/utils/SecurityUtils.java b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/utils/SecurityUtils.java new file mode 100644 index 0000000..6e3f92b --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/java/com/muyu/common/security/utils/SecurityUtils.java @@ -0,0 +1,109 @@ +package com.muyu.common.security.utils; + +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.core.constant.TokenConstants; +import com.muyu.common.core.context.SecurityContextHolder; +import com.muyu.common.core.utils.ServletUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.system.domain.LoginUser; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import jakarta.servlet.http.HttpServletRequest; + +/** + * 权限获取工具类 + * + * @author muyu + */ +public class SecurityUtils { + /** + * 获取用户ID + */ + public static Long getUserId () { + return SecurityContextHolder.getUserId(); + } + + /** + * 获取用户名称 + */ + public static String getUsername () { + return SecurityContextHolder.getUserName(); + } + + /** + * 获取用户key + */ + public static String getUserKey () { + return SecurityContextHolder.getUserKey(); + } + + /** + * 获取登录用户信息 + */ + public static LoginUser getLoginUser () { + return SecurityContextHolder.get(SecurityConstants.LOGIN_USER, LoginUser.class); + } + + /** + * 获取请求token + */ + public static String getToken () { + return getToken(ServletUtils.getRequest()); + } + + /** + * 根据request获取请求token + */ + public static String getToken (HttpServletRequest request) { + // 从header获取token标识 + String token = request.getHeader(TokenConstants.AUTHENTICATION); + return replaceTokenPrefix(token); + } + + /** + * 裁剪token前缀 + */ + public static String replaceTokenPrefix (String token) { + // 如果前端设置了令牌前缀,则裁剪掉前缀 + if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX)) { + token = token.replaceFirst(TokenConstants.PREFIX, ""); + } + return token; + } + + /** + * 是否为管理员 + * + * @param userId 用户ID + * + * @return 结果 + */ + public static boolean isAdmin (Long userId) { + return userId != null && 1L == userId; + } + + /** + * 生成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); + } +} diff --git a/cloud-common/cloud-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/cloud-common/cloud-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..f4e76b1 --- /dev/null +++ b/cloud-common/cloud-common-security/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,5 @@ +com.muyu.common.security.config.WebMvcConfig +com.muyu.common.security.service.TokenService +com.muyu.common.security.aspect.PreAuthorizeAspect +com.muyu.common.security.aspect.InnerAuthAspect +com.muyu.common.security.handler.GlobalExceptionHandler diff --git a/cloud-common/cloud-common-system/pom.xml b/cloud-common/cloud-common-system/pom.xml new file mode 100644 index 0000000..22cd455 --- /dev/null +++ b/cloud-common/cloud-common-system/pom.xml @@ -0,0 +1,32 @@ + + + 4.0.0 + + com.muyu + cloud-common + 3.6.3 + + + cloud-common-system + + cloud-common-system系统级远程调用 + + + 17 + 17 + UTF-8 + + + + + + com.muyu + cloud-common-core + + + + + + diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/Datasource.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/Datasource.java new file mode 100644 index 0000000..9b54051 --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/Datasource.java @@ -0,0 +1,26 @@ +package com.muyu.common.system.domain; + + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@TableName(value = "datasource", autoResultMap = true) +public class Datasource { + + @TableId(value = "id",type = IdType.AUTO) + private Integer id; + + public String firmName; + + public String databaseName; + + + +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/LoginUser.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/LoginUser.java new file mode 100644 index 0000000..a18af37 --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/LoginUser.java @@ -0,0 +1,66 @@ +package com.muyu.common.system.domain; + + +import lombok.*; + +import java.io.Serializable; +import java.util.Set; + +/** + * 用户信息 + * + * @author muyu + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class LoginUser implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 用户唯一标识 + */ + private String token; + + /** + * 用户名id + */ + private Long userid; + + /** + * 用户名 + */ + private String username; + + /** + * 登录时间 + */ + private Long loginTime; + + /** + * 过期时间 + */ + private Long expireTime; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 权限列表 + */ + private Set permissions; + + /** + * 角色列表 + */ + private Set roles; + + /** + * 用户信息 + */ + private SysUser sysUser; + +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysDept.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysDept.java new file mode 100644 index 0000000..65ada9e --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysDept.java @@ -0,0 +1,108 @@ +package com.muyu.common.system.domain; + +import com.muyu.common.core.web.domain.BaseEntity; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import java.util.ArrayList; +import java.util.List; + +/** + * 部门表 sys_dept + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +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 children = new ArrayList(); + + @NotBlank(message = "部门名称不能为空") + @Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符") + public String getDeptName () { + return deptName; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getOrderNum () { + return orderNum; + } + + @Size(min = 0, max = 11, message = "联系电话长度不能超过11个字符") + public String getPhone () { + return phone; + } + + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") + public String getEmail () { + return email; + } +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysDictData.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysDictData.java new file mode 100644 index 0000000..5beee3f --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysDictData.java @@ -0,0 +1,104 @@ +package com.muyu.common.system.domain; + +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.core.annotation.Excel.ColumnType; +import com.muyu.common.core.constant.UserConstants; +import com.muyu.common.core.web.domain.BaseEntity; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +/** + * 字典数据表 sys_dict_data + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +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; + + @NotBlank(message = "字典标签不能为空") + @Size(min = 0, max = 100, message = "字典标签长度不能超过100个字符") + public String getDictLabel () { + return dictLabel; + } + + @NotBlank(message = "字典键值不能为空") + @Size(min = 0, max = 100, message = "字典键值长度不能超过100个字符") + public String getDictValue () { + return dictValue; + } + + @NotBlank(message = "字典类型不能为空") + @Size(min = 0, max = 100, message = "字典类型长度不能超过100个字符") + public String getDictType () { + return dictType; + } + + @Size(min = 0, max = 100, message = "样式属性长度不能超过100个字符") + public String getCssClass () { + return cssClass; + } + + public boolean getDefault () { + return UserConstants.YES.equals(this.isDefault); + } +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysDictType.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysDictType.java new file mode 100644 index 0000000..d623333 --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysDictType.java @@ -0,0 +1,62 @@ +package com.muyu.common.system.domain; + +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.core.annotation.Excel.ColumnType; +import com.muyu.common.core.web.domain.BaseEntity; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; + +/** + * 字典类型表 sys_dict_type + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +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; + + @NotBlank(message = "字典名称不能为空") + @Size(min = 0, max = 100, message = "字典类型名称长度不能超过100个字符") + public String getDictName () { + return dictName; + } + + @NotBlank(message = "字典类型不能为空") + @Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符") + @Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)") + public String getDictType () { + return dictType; + } +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysFile.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysFile.java new file mode 100644 index 0000000..ad48a1e --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysFile.java @@ -0,0 +1,27 @@ +package com.muyu.common.system.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 文件信息 + * + * @author muyu + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SysFile { + /** + * 文件名称 + */ + private String name; + + /** + * 文件地址 + */ + private String url; +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysFirmUser.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysFirmUser.java new file mode 100644 index 0000000..0bba83a --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysFirmUser.java @@ -0,0 +1,25 @@ +package com.muyu.common.system.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +/** + * @Author WangXin + * @Data 2024/9/18 + * @Description 企业用户 + * @Version 1.0.0 + */ +@EqualsAndHashCode(callSuper = true) +@Data +@SuperBuilder +@AllArgsConstructor +@NoArgsConstructor +public class SysFirmUser extends SysUser { + /** + * 用户数据库 + */ + private String databaseName; +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysLogininfor.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysLogininfor.java new file mode 100644 index 0000000..c66f131 --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysLogininfor.java @@ -0,0 +1,64 @@ +package com.muyu.common.system.domain; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.core.annotation.Excel.ColumnType; +import com.muyu.common.core.web.domain.BaseEntity; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.Date; + +/** + * 系统访问记录表 sys_logininfor + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class SysLogininfor extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * ID + */ + @Excel(name = "序号", cellType = ColumnType.NUMERIC) + private Long infoId; + + /** + * 用户账号 + */ + @Excel(name = "用户账号") + private String userName; + + /** + * 状态 0成功 1失败 + */ + @Excel(name = "状态", readConverterExp = "0=成功,1=失败") + private String status; + + /** + * 地址 + */ + @Excel(name = "地址") + private String ipaddr; + + /** + * 描述 + */ + @Excel(name = "描述") + private String msg; + + /** + * 访问时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "访问时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date accessTime; +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysOperLog.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysOperLog.java new file mode 100644 index 0000000..b207611 --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysOperLog.java @@ -0,0 +1,129 @@ +package com.muyu.common.system.domain; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.core.annotation.Excel.ColumnType; +import com.muyu.common.core.web.domain.BaseEntity; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.Date; + +/** + * 操作日志记录表 oper_log + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class SysOperLog extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 日志主键 + */ + @Excel(name = "操作序号", cellType = ColumnType.NUMERIC) + private Long operId; + + /** + * 操作模块 + */ + @Excel(name = "操作模块") + private String title; + + /** + * 业务类型(0其它 1新增 2修改 3删除) + */ + @Excel(name = "业务类型", readConverterExp = "0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据") + private Integer businessType; + + /** + * 业务类型数组 + */ + private Integer[] businessTypes; + + /** + * 请求方法 + */ + @Excel(name = "请求方法") + private String method; + + /** + * 请求方式 + */ + @Excel(name = "请求方式") + private String requestMethod; + + /** + * 操作类别(0其它 1后台用户 2手机端用户) + */ + @Excel(name = "操作类别", readConverterExp = "0=其它,1=后台用户,2=手机端用户") + 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 operParam; + + /** + * 返回参数 + */ + @Excel(name = "返回参数") + private String jsonResult; + + /** + * 操作状态(0正常 1异常) + */ + @Excel(name = "状态", readConverterExp = "0=正常,1=异常") + private Integer status; + + /** + * 错误消息 + */ + @Excel(name = "错误消息") + private String errorMsg; + + /** + * 操作时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "操作时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date operTime; + + /** + * 消耗时间 + */ + @Excel(name = "消耗时间", suffix = "毫秒") + private Long costTime; +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysRole.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysRole.java new file mode 100644 index 0000000..1aab56a --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysRole.java @@ -0,0 +1,127 @@ +package com.muyu.common.system.domain; + +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.core.annotation.Excel.ColumnType; +import com.muyu.common.core.web.domain.BaseEntity; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import java.util.Set; + +/** + * 角色表 sys_role + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class SysRole extends BaseEntity { + 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 String status; + + /** + * 删除标志(0代表存在 2代表删除) + */ + private String delFlag; + + /** + * 用户是否存在此角色标识 默认不存在 + */ + private boolean flag = false; + + /** + * 菜单组 + */ + private Long[] menuIds; + + /** + * 部门组(数据权限) + */ + private Long[] deptIds; + + /** + * 角色菜单权限 + */ + private Set permissions; + + + public SysRole (Long roleId) { + this.roleId = roleId; + } + + public static boolean isAdmin (Long roleId) { + return roleId != null && 1L == roleId; + } + + public boolean isAdmin () { + return isAdmin(this.roleId); + } + + @NotBlank(message = "角色名称不能为空") + @Size(min = 0, max = 30, message = "角色名称长度不能超过30个字符") + public String getRoleName () { + return roleName; + } + + @NotBlank(message = "权限字符不能为空") + @Size(min = 0, max = 100, message = "权限字符长度不能超过100个字符") + public String getRoleKey () { + return roleKey; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getRoleSort () { + return roleSort; + } +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysUser.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysUser.java new file mode 100644 index 0000000..1c8fc9c --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/domain/SysUser.java @@ -0,0 +1,178 @@ +package com.muyu.common.system.domain; + +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.core.annotation.Excel.ColumnType; +import com.muyu.common.core.annotation.Excel.Type; +import com.muyu.common.core.annotation.Excels; +import com.muyu.common.core.web.domain.BaseEntity; +import com.muyu.common.core.xss.Xss; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.Date; +import java.util.List; + +/** + * 用户对象 sys_user + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class SysUser extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + @Excel(name = "用户序号", cellType = ColumnType.NUMERIC, prompt = "用户编号") + private Long userId; + + /** + * 部门ID + */ + @Excel(name = "部门编号", type = Type.IMPORT) + private Long deptId; + + /** + * 用户账号 + */ + @Excel(name = "登录名称") + private String userName; + + /** + * 用户昵称 + */ + @Excel(name = "用户名称") + private String nickName; + + /** + * 用户邮箱 + */ + @Excel(name = "用户邮箱") + private String email; + + /** + * 手机号码 + */ + @Excel(name = "手机号码") + private String phonenumber; + + /** + * 用户性别 + */ + @Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知") + private String sex; + + /** + * 用户头像 + */ + private String avatar; + + /** + * 密码 + */ + private String password; + + /** + * 帐号状态(0正常 1停用) + */ + @Excel(name = "帐号状态", readConverterExp = "0=正常,1=停用") + private String status; + + /** + * 删除标志(0代表存在 2代表删除) + */ + private String delFlag; + + /** + * 最后登录IP + */ + @Excel(name = "最后登录IP", type = Type.EXPORT) + private String loginIp; + + /** + * 最后登录时间 + */ + @Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT) + private Date loginDate; + + /** + * 部门对象 + */ + @Excels({ + @Excel(name = "部门名称", targetAttr = "deptName", type = Type.EXPORT), + @Excel(name = "部门负责人", targetAttr = "leader", type = Type.EXPORT) + }) + private SysDept dept; + + /** + * 角色对象 + */ + private List roles; + + /** + * 角色组 + */ + private Long[] roleIds; + + /** + * 岗位组 + */ + private Long[] postIds; + + /** + * 角色ID + */ + private Long roleId; + + /** + * 数据库名称 + */ + private String databaseName; + + public SysUser (Long userId) { + this.userId = userId; + } + + public static boolean isAdmin (Long userId) { + return userId != null && 1L == userId; + } + + public boolean isAdmin () { + return isAdmin(this.userId); + } + + @Xss(message = "用户昵称不能包含脚本字符") + @Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符") + public String getNickName () { + return nickName; + } + + @Xss(message = "用户账号不能包含脚本字符") + @NotBlank(message = "用户账号不能为空") + @Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符") + public String getUserName () { + return userName; + } + + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") + public String getEmail () { + return email; + } + + @Size(min = 0, max = 11, message = "手机号码长度不能超过11个字符") + public String getPhonenumber () { + return phonenumber; + } +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/RemoteFileService.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/RemoteFileService.java new file mode 100644 index 0000000..00d5d88 --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/RemoteFileService.java @@ -0,0 +1,29 @@ +package com.muyu.common.system.remote; + +import com.muyu.common.core.constant.ServiceNameConstants; +import com.muyu.common.core.domain.Result; +import com.muyu.common.system.domain.SysFile; +import com.muyu.common.system.remote.factory.RemoteFileFallbackFactory; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.multipart.MultipartFile; + +/** + * 文件服务 + * + * @author muyu + */ +@FeignClient(contextId = "remoteFileService", value = ServiceNameConstants.FILE_SERVICE, fallbackFactory = RemoteFileFallbackFactory.class) +public interface RemoteFileService { + /** + * 上传文件 + * + * @param file 文件信息 + * + * @return 结果 + */ + @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + Result upload(@RequestPart(value = "file") MultipartFile file); +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/RemoteLogService.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/RemoteLogService.java new file mode 100644 index 0000000..1b67135 --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/RemoteLogService.java @@ -0,0 +1,42 @@ +package com.muyu.common.system.remote; + +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.core.constant.ServiceNameConstants; +import com.muyu.common.core.domain.Result; +import com.muyu.common.system.domain.SysLogininfor; +import com.muyu.common.system.domain.SysOperLog; +import com.muyu.common.system.remote.factory.RemoteLogFallbackFactory; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; + +/** + * 日志服务 + * + * @author muyu + */ +@FeignClient(contextId = "remoteLogService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteLogFallbackFactory.class) +public interface RemoteLogService { + /** + * 保存系统日志 + * + * @param sysOperLog 日志实体 + * @param source 请求来源 + * + * @return 结果 + */ + @PostMapping("/operlog") + Result saveLog(@RequestBody SysOperLog sysOperLog, @RequestHeader(SecurityConstants.FROM_SOURCE) String source) throws Exception; + + /** + * 保存访问记录 + * + * @param sysLogininfor 访问实体 + * @param source 请求来源 + * + * @return 结果 + */ + @PostMapping("/logininfor") + Result saveLogininfor(@RequestBody SysLogininfor sysLogininfor, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/RemoteSaaSService.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/RemoteSaaSService.java new file mode 100644 index 0000000..9e6691e --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/RemoteSaaSService.java @@ -0,0 +1,29 @@ +package com.muyu.common.system.remote; + +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.core.constant.ServiceNameConstants; +import com.muyu.common.core.domain.Result; +import com.muyu.common.system.domain.Datasource; +import com.muyu.common.system.domain.LoginUser; +import com.muyu.common.system.domain.SysUser; +import com.muyu.common.system.remote.factory.RemoteSaaSFallbackFactory; +import com.muyu.common.system.remote.factory.RemoteUserFallbackFactory; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 用户服务 + * + * @author muyu + */ +@FeignClient(contextId = "SaasService", value = ServiceNameConstants.SAAS_SERVICE, fallbackFactory = RemoteSaaSFallbackFactory.class) +public interface RemoteSaaSService { + + @GetMapping("/saas/findDatabaseList") + Result> findDatabaseList(); + + + +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/RemoteUserService.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/RemoteUserService.java new file mode 100644 index 0000000..4bcc4ff --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/RemoteUserService.java @@ -0,0 +1,47 @@ +package com.muyu.common.system.remote; + +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.core.constant.ServiceNameConstants; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.web.page.TableDataInfo; +import com.muyu.common.system.domain.SysFirmUser; +import com.muyu.common.system.domain.SysUser; +import com.muyu.common.system.remote.factory.RemoteUserFallbackFactory; +import com.muyu.common.system.domain.LoginUser; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 用户服务 + * + * @author muyu + */ +@FeignClient(contextId = "remoteUserService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteUserFallbackFactory.class) +public interface RemoteUserService { + /** + * 通过用户名查询用户信息 + * + * @param username 用户名 + * @param source 请求来源 + * + * @return 结果 + */ + @GetMapping("/user/info/{username}") + Result getUserInfo(@PathVariable("username") String username, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); + + /** + * 注册用户信息 + * + * @param sysUser 用户信息 + * @param source 请求来源 + * + * @return 结果 + */ + @PostMapping("/user/register") + Result registerUserInfo(@RequestBody SysUser sysUser, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); + + @GetMapping("/user/companyList") + Result> companyList(); +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/factory/RemoteFileFallbackFactory.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/factory/RemoteFileFallbackFactory.java new file mode 100644 index 0000000..395f74f --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/factory/RemoteFileFallbackFactory.java @@ -0,0 +1,31 @@ +package com.muyu.common.system.remote.factory; + +import com.muyu.common.core.domain.Result; +import com.muyu.common.system.remote.RemoteFileService; +import com.muyu.common.system.domain.SysFile; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.openfeign.FallbackFactory; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +/** + * 文件服务降级处理 + * + * @author muyu + */ +@Component +public class RemoteFileFallbackFactory implements FallbackFactory { + private static final Logger log = LoggerFactory.getLogger(RemoteFileFallbackFactory.class); + + @Override + public RemoteFileService create (Throwable throwable) { + log.error("文件服务调用失败:{}", throwable.getMessage()); + return new RemoteFileService() { + @Override + public Result upload (MultipartFile file) { + return Result.error("上传文件失败:" + throwable.getMessage()); + } + }; + } +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/factory/RemoteLogFallbackFactory.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/factory/RemoteLogFallbackFactory.java new file mode 100644 index 0000000..1fc0491 --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/factory/RemoteLogFallbackFactory.java @@ -0,0 +1,37 @@ +package com.muyu.common.system.remote.factory; + +import com.muyu.common.core.domain.Result; +import com.muyu.common.system.remote.RemoteLogService; +import com.muyu.common.system.domain.SysLogininfor; +import com.muyu.common.system.domain.SysOperLog; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.openfeign.FallbackFactory; +import org.springframework.stereotype.Component; + +/** + * 日志服务降级处理 + * + * @author muyu + */ +@Component +public class RemoteLogFallbackFactory implements FallbackFactory { + private static final Logger log = LoggerFactory.getLogger(RemoteLogFallbackFactory.class); + + @Override + public RemoteLogService create (Throwable throwable) { + log.error("日志服务调用失败:{}", throwable.getMessage(), throwable); + return new RemoteLogService() { + @Override + public Result saveLog (SysOperLog sysOperLog, String source) { + return Result.error("保存操作日志失败:" + throwable.getMessage()); + } + + @Override + public Result saveLogininfor (SysLogininfor sysLogininfor, String source) { + return Result.error("保存登录日志失败:" + throwable.getMessage()); + } + }; + + } +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/factory/RemoteSaaSFallbackFactory.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/factory/RemoteSaaSFallbackFactory.java new file mode 100644 index 0000000..886a151 --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/factory/RemoteSaaSFallbackFactory.java @@ -0,0 +1,33 @@ +package com.muyu.common.system.remote.factory; + +import com.muyu.common.core.domain.Result; +import com.muyu.common.system.domain.Datasource; +import com.muyu.common.system.domain.LoginUser; +import com.muyu.common.system.domain.SysUser; +import com.muyu.common.system.remote.RemoteSaaSService; +import com.muyu.common.system.remote.RemoteUserService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.openfeign.FallbackFactory; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 用户服务降级处理 + * + * @author muyu + */ +@Component +public class RemoteSaaSFallbackFactory implements FallbackFactory { + + @Override + public RemoteSaaSService create(Throwable cause) { + return new RemoteSaaSService() { + @Override + public Result> findDatabaseList() { + return Result.error("查询数据库失败:" + cause.getMessage()); + } + }; + } +} diff --git a/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/factory/RemoteUserFallbackFactory.java b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/factory/RemoteUserFallbackFactory.java new file mode 100644 index 0000000..071490a --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/java/com/muyu/common/system/remote/factory/RemoteUserFallbackFactory.java @@ -0,0 +1,46 @@ +package com.muyu.common.system.remote.factory; + +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.web.page.TableDataInfo; +import com.muyu.common.system.domain.SysFirmUser; +import com.muyu.common.system.remote.RemoteUserService; +import com.muyu.common.system.domain.SysUser; +import com.muyu.common.system.domain.LoginUser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.openfeign.FallbackFactory; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 用户服务降级处理 + * + * @author muyu + */ +@Component +public class RemoteUserFallbackFactory implements FallbackFactory { + private static final Logger log = LoggerFactory.getLogger(RemoteUserFallbackFactory.class); + + @Override + public RemoteUserService create (Throwable throwable) { + log.error("用户服务调用失败:{}", throwable.getMessage()); + return new RemoteUserService() { + @Override + public Result getUserInfo (String username, String source) { + return Result.error("获取用户失败:" + throwable.getMessage()); + } + + @Override + public Result registerUserInfo (SysUser sysUser, String source) { + return Result.error("注册用户失败:" + throwable.getMessage()); + } + + @Override + public Result> companyList() { + return Result.error("获取企业列表失败:" + throwable.getMessage()); + } + + }; + } +} diff --git a/cloud-common/cloud-common-system/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/cloud-common/cloud-common-system/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..6539412 --- /dev/null +++ b/cloud-common/cloud-common-system/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,4 @@ +com.muyu.common.system.remote.factory.RemoteUserFallbackFactory +com.muyu.common.system.remote.factory.RemoteLogFallbackFactory +com.muyu.common.system.remote.factory.RemoteFileFallbackFactory +com.muyu.common.system.remote.factory.RemoteSaaSFallbackFactory diff --git a/cloud-common/cloud-common-xxl/pom.xml b/cloud-common/cloud-common-xxl/pom.xml new file mode 100644 index 0000000..4af9214 --- /dev/null +++ b/cloud-common/cloud-common-xxl/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + com.muyu + cloud-common + 3.6.3 + + + cloud-common-xxl + + XllJob定时任务 + + + 17 + 17 + UTF-8 + + + + + + + com.muyu + cloud-common-core + + + + + com.xuxueli + xxl-job-core + + + + \ No newline at end of file diff --git a/cloud-common/cloud-common-xxl/src/main/java/com/muyu/common/xxl/XXLJobConfig.java b/cloud-common/cloud-common-xxl/src/main/java/com/muyu/common/xxl/XXLJobConfig.java new file mode 100644 index 0000000..0bf4362 --- /dev/null +++ b/cloud-common/cloud-common-xxl/src/main/java/com/muyu/common/xxl/XXLJobConfig.java @@ -0,0 +1,28 @@ +package com.muyu.common.xxl; + +import com.muyu.common.core.utils.StringUtils; +import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; +import lombok.extern.log4j.Log4j2; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; + +@Log4j2 +@Component +public class XXLJobConfig { + @Bean + public XxlJobSpringExecutor xxlJobExecutor(XxlJobProperties xxlJobProperties) { + if (StringUtils.isEmpty(xxlJobProperties.getAdminAddresses())){ + throw new RuntimeException("请在bootstrap.yml当中配置shared-configs项,xxl-job共享配置[application-xxl-config]"); + } + XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor(); + xxlJobSpringExecutor.setAdminAddresses(xxlJobProperties.getAdminAddresses()); + xxlJobSpringExecutor.setAppname(xxlJobProperties.getAppName()); + xxlJobSpringExecutor.setIp(xxlJobProperties.getIp()); + xxlJobSpringExecutor.setPort(xxlJobProperties.getPort()); + xxlJobSpringExecutor.setAccessToken(xxlJobProperties.getAccessToken()); + xxlJobSpringExecutor.setLogPath(xxlJobProperties.getLogPath()); + xxlJobSpringExecutor.setLogRetentionDays(xxlJobProperties.getLogRetentionDays()); + log.info(">>>>>>>>>>> xxl-job config init success."); + return xxlJobSpringExecutor; + } +} diff --git a/cloud-common/cloud-common-xxl/src/main/java/com/muyu/common/xxl/XxlJobProperties.java b/cloud-common/cloud-common-xxl/src/main/java/com/muyu/common/xxl/XxlJobProperties.java new file mode 100644 index 0000000..c1428f7 --- /dev/null +++ b/cloud-common/cloud-common-xxl/src/main/java/com/muyu/common/xxl/XxlJobProperties.java @@ -0,0 +1,63 @@ +package com.muyu.common.xxl; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Data +@Configuration +@ConfigurationProperties(prefix = "xxl-job") +public class XxlJobProperties { + + /** + * 调度中心部署根地址 [选填]: + * 如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调"; + * 为空则关闭自动注册; + */ + private String adminAddresses; + + /** + * 执行器通讯TOKEN [选填]: + * 非空时启用; + */ + private String accessToken; + + /** + * 执行器AppName [选填]: + * 执行器心跳注册分组依据;为空则关闭自动注册 + */ + private String appName; + + /** + * 执行器注册 [选填]: + * 优先使用该配置作为注册地址,为空时使用内嵌服务 ”IP:PORT“ 作为注册地址。 + * 从而更灵活的支持容器类型执行器动态IP和动态映射端口问题。 + */ + private String address; + + /** + * 执行器IP [选填]: + * 默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用; + * 地址信息用于 "执行器注册" 和 "调度中心请求并触发任务"; + */ + private String ip; + + /** + * 执行器端口号 [选填]: + * 小于等于0则自动获取;默认端口为9999, + * 单机部署多个执行器时,注意要配置不同执行器端口; + */ + private int port; + + /** + * 执行器运行日志文件存储磁盘路径 [选填]: + * 需要对该路径拥有读写权限;为空则使用默认路径; + */ + private String logPath; + + /** + * 执行器日志文件保存天数 [选填]: + * 过期日志自动清理, 限制值大于等于3时生效; 否则, 如-1, 关闭自动清理功能; + */ + private int logRetentionDays; +} diff --git a/cloud-common/cloud-common-xxl/src/main/java/com/muyu/common/xxl/demo/XxlJobDemoService.java b/cloud-common/cloud-common-xxl/src/main/java/com/muyu/common/xxl/demo/XxlJobDemoService.java new file mode 100644 index 0000000..51cd2a4 --- /dev/null +++ b/cloud-common/cloud-common-xxl/src/main/java/com/muyu/common/xxl/demo/XxlJobDemoService.java @@ -0,0 +1,38 @@ +package com.muyu.common.xxl.demo; + +import com.xxl.job.core.context.XxlJobHelper; +import com.xxl.job.core.handler.annotation.XxlJob; +import lombok.extern.log4j.Log4j2; +import org.springframework.stereotype.Component; + +@Log4j2 +@Component +public class XxlJobDemoService { + + /** + * 无参测试 + */ + @XxlJob("xxl-job-demo-no-param") + public void xxlJobDemoNoParam(){ + log.info("调度名称:[{}] - 无参", "xxl-job-demo-no-param"); + } + + /** + * 有参测试 + */ + @XxlJob("xxl-job-demo-one-param") + public void xxlJobDemoOneParam(){ + String param = XxlJobHelper.getJobParam(); + log.info("调度名称:[{}] - 参数:[{}]", "xxl-job-demo-one-param", param); + } + /** + * 分片测试 + */ + @XxlJob("xxl-job-slice-demo") + public void xxlJobSliceDemo(){ + int shardTotal = XxlJobHelper.getShardTotal(); + int shardIndex = XxlJobHelper.getShardIndex(); + String param = XxlJobHelper.getJobParam(); + log.info("调度名称:[{}-[{}/{}(分片位置/分片总数)]] - 参数:[{}]", "xxl-job-slice-demo",shardIndex, shardTotal, param); + } +} diff --git a/cloud-common/cloud-common-xxl/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/cloud-common/cloud-common-xxl/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..1404a51 --- /dev/null +++ b/cloud-common/cloud-common-xxl/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,3 @@ +com.muyu.common.xxl.XXLJobConfig +com.muyu.common.xxl.XxlJobProperties +com.muyu.common.xxl.demo.XxlJobDemoService \ No newline at end of file diff --git a/cloud-common/pom.xml b/cloud-common/pom.xml new file mode 100644 index 0000000..a7a40be --- /dev/null +++ b/cloud-common/pom.xml @@ -0,0 +1,33 @@ + + + + com.muyu + cloud-server + 3.6.3 + + 4.0.0 + + + cloud-common-log + cloud-common-core + cloud-common-redis + cloud-common-seata + cloud-common-api-doc + cloud-common-security + cloud-common-datascope + cloud-common-datasource + cloud-common-system + cloud-common-xxl + cloud-common-rabbit + cloud-common-saas + + + cloud-common + pom + + + cloud-common通用模块 + + + diff --git a/cloud-gateway/pom.xml b/cloud-gateway/pom.xml new file mode 100644 index 0000000..8aa98ed --- /dev/null +++ b/cloud-gateway/pom.xml @@ -0,0 +1,103 @@ + + + com.muyu + cloud-server + 3.6.3 + + 4.0.0 + + cloud-gateway + + + cloud-gateway网关模块 + + + + + + + org.springframework.cloud + spring-cloud-starter-gateway + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + + com.alibaba.cloud + spring-cloud-alibaba-sentinel-gateway + + + + + com.alibaba.csp + sentinel-datasource-nacos + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + org.springframework.cloud + spring-cloud-loadbalancer + + + + + pro.fessional + kaptcha + + + + + com.muyu + cloud-common-redis + + + + com.github.xiaoymin + knife4j-gateway-spring-boot-starter + 4.5.0 + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + + diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/CloudGatewayApplication.java b/cloud-gateway/src/main/java/com/muyu/gateway/CloudGatewayApplication.java new file mode 100644 index 0000000..43c7ed9 --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/CloudGatewayApplication.java @@ -0,0 +1,17 @@ +package com.muyu.gateway; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; + +/** + * 网关启动程序 + * + * @author muyu + */ +@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) +public class CloudGatewayApplication { + public static void main (String[] args) { + SpringApplication.run(CloudGatewayApplication.class, args); + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/config/CaptchaConfig.java b/cloud-gateway/src/main/java/com/muyu/gateway/config/CaptchaConfig.java new file mode 100644 index 0000000..557af8a --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/config/CaptchaConfig.java @@ -0,0 +1,82 @@ +package com.muyu.gateway.config; + +import com.google.code.kaptcha.impl.DefaultKaptcha; +import com.google.code.kaptcha.util.Config; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.Properties; + +import static com.google.code.kaptcha.Constants.*; + +/** + * 验证码配置 + * + * @author muyu + */ +@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.muyu.gateway.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/cloud-gateway/src/main/java/com/muyu/gateway/config/GatewayConfig.java b/cloud-gateway/src/main/java/com/muyu/gateway/config/GatewayConfig.java new file mode 100644 index 0000000..bf004bf --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/config/GatewayConfig.java @@ -0,0 +1,21 @@ +package com.muyu.gateway.config; + +import com.muyu.gateway.handler.SentinelFallbackHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; + +/** + * 网关限流配置 + * + * @author muyu + */ +@Configuration +public class GatewayConfig { + @Bean + @Order(Ordered.HIGHEST_PRECEDENCE) + public SentinelFallbackHandler sentinelGatewayExceptionHandler () { + return new SentinelFallbackHandler(); + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/config/KaptchaTextCreator.java b/cloud-gateway/src/main/java/com/muyu/gateway/config/KaptchaTextCreator.java new file mode 100644 index 0000000..7b0636d --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/config/KaptchaTextCreator.java @@ -0,0 +1,61 @@ +package com.muyu.gateway.config; + +import com.google.code.kaptcha.text.impl.DefaultTextCreator; + +import java.util.Random; + +/** + * 验证码文本生成器 + * + * @author muyu + */ +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 (randomoperands == 2) { + 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]); + } + } else { + result = x + y; + suChinese.append(CNUMBERS[x]); + suChinese.append("+"); + suChinese.append(CNUMBERS[y]); + } + suChinese.append("=?@" + result); + return suChinese.toString(); + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/config/RouterFunctionConfiguration.java b/cloud-gateway/src/main/java/com/muyu/gateway/config/RouterFunctionConfiguration.java new file mode 100644 index 0000000..41b8e47 --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/config/RouterFunctionConfiguration.java @@ -0,0 +1,29 @@ +package com.muyu.gateway.config; + +import com.muyu.gateway.handler.ValidateCodeHandler; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.server.RequestPredicates; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; + +/** + * 路由配置信息 + * + * @author muyu + */ +@Configuration +public class RouterFunctionConfiguration { + @Autowired + private ValidateCodeHandler validateCodeHandler; + + @SuppressWarnings("rawtypes") + @Bean + public RouterFunction routerFunction () { + return RouterFunctions.route( + RequestPredicates.GET("/code").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), + validateCodeHandler); + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/config/properties/CaptchaProperties.java b/cloud-gateway/src/main/java/com/muyu/gateway/config/properties/CaptchaProperties.java new file mode 100644 index 0000000..60b1814 --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/config/properties/CaptchaProperties.java @@ -0,0 +1,41 @@ +package com.muyu.gateway.config.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.annotation.Configuration; + +/** + * 验证码配置 + * + * @author muyu + */ +@Configuration +@RefreshScope +@ConfigurationProperties(prefix = "security.captcha") +public class CaptchaProperties { + /** + * 验证码开关 + */ + private Boolean enabled; + + /** + * 验证码类型(math 数组计算 char 字符) + */ + private String type; + + public Boolean getEnabled () { + return enabled; + } + + public void setEnabled (Boolean enabled) { + this.enabled = enabled; + } + + public String getType () { + return type; + } + + public void setType (String type) { + this.type = type; + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/config/properties/IgnoreWhiteProperties.java b/cloud-gateway/src/main/java/com/muyu/gateway/config/properties/IgnoreWhiteProperties.java new file mode 100644 index 0000000..d5ea46c --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/config/properties/IgnoreWhiteProperties.java @@ -0,0 +1,31 @@ +package com.muyu.gateway.config.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.annotation.Configuration; + +import java.util.ArrayList; +import java.util.List; + +/** + * 放行白名单配置 + * + * @author muyu + */ +@Configuration +@RefreshScope +@ConfigurationProperties(prefix = "security.ignore") +public class IgnoreWhiteProperties { + /** + * 放行白名单配置,网关不校验此处的白名单 + */ + private List whites = new ArrayList<>(); + + public List getWhites () { + return whites; + } + + public void setWhites (List whites) { + this.whites = whites; + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/config/properties/XssProperties.java b/cloud-gateway/src/main/java/com/muyu/gateway/config/properties/XssProperties.java new file mode 100644 index 0000000..31dcc6a --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/config/properties/XssProperties.java @@ -0,0 +1,44 @@ +package com.muyu.gateway.config.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.annotation.Configuration; + +import java.util.ArrayList; +import java.util.List; + +/** + * XSS跨站脚本配置 + * + * @author muyu + */ +@Configuration +@RefreshScope +@ConfigurationProperties(prefix = "security.xss") +public class XssProperties { + /** + * Xss开关 + */ + private Boolean enabled; + + /** + * 排除路径 + */ + private List excludeUrls = new ArrayList<>(); + + public Boolean getEnabled () { + return enabled; + } + + public void setEnabled (Boolean enabled) { + this.enabled = enabled; + } + + public List getExcludeUrls () { + return excludeUrls; + } + + public void setExcludeUrls (List excludeUrls) { + this.excludeUrls = excludeUrls; + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/filter/AuthFilter.java b/cloud-gateway/src/main/java/com/muyu/gateway/filter/AuthFilter.java new file mode 100644 index 0000000..bda6f2f --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/filter/AuthFilter.java @@ -0,0 +1,122 @@ +package com.muyu.gateway.filter; + +import com.muyu.common.core.constant.CacheConstants; +import com.muyu.common.core.constant.HttpStatus; +import com.muyu.common.core.constant.SecurityConstants; +import com.muyu.common.core.constant.TokenConstants; +import com.muyu.common.core.utils.JwtUtils; +import com.muyu.common.core.utils.ServletUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.redis.service.RedisService; +import com.muyu.gateway.config.properties.IgnoreWhiteProperties; +import io.jsonwebtoken.Claims; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.Ordered; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * 网关鉴权 + * + * @author muyu + */ +@Component +public class AuthFilter implements GlobalFilter, Ordered { + private static final Logger log = LoggerFactory.getLogger(AuthFilter.class); + + // 排除过滤的 uri 地址,nacos自行添加 + @Autowired + private IgnoreWhiteProperties ignoreWhite; + + @Autowired + private RedisService redisService; + + + @Override + public Mono filter (ServerWebExchange exchange, GatewayFilterChain chain) { + ServerHttpRequest request = exchange.getRequest(); + ServerHttpRequest.Builder mutate = request.mutate(); + + String url = request.getURI().getPath(); + // 跳过不需要验证的路径 + if (StringUtils.matches(url, ignoreWhite.getWhites())) { + return chain.filter(exchange); + } + String token = getToken(request); + if (StringUtils.isEmpty(token)) { + return unauthorizedResponse(exchange, "令牌不能为空"); + } + Claims claims = JwtUtils.parseToken(token); + if (claims == null) { + return unauthorizedResponse(exchange, "令牌已过期或验证不正确!"); + } + String userkey = JwtUtils.getUserKey(claims); + boolean islogin = redisService.hasKey(getTokenKey(userkey)); + if (!islogin) { + return unauthorizedResponse(exchange, "登录状态已过期"); + } + String userid = JwtUtils.getUserId(claims); + String username = JwtUtils.getUserName(claims); + String saasKey = JwtUtils.getSaasKey(claims); + if (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username)) { + return unauthorizedResponse(exchange, "令牌验证失败"); + } + + // 设置用户信息到请求 + addHeader(mutate, SecurityConstants.USER_KEY, userkey); + addHeader(mutate, SecurityConstants.DETAILS_USER_ID, userid); + addHeader(mutate, SecurityConstants.DETAILS_USERNAME, username); + addHeader(mutate,SecurityConstants.SAAS_KEY,saasKey); + // 内部请求来源参数清除 + removeHeader(mutate, SecurityConstants.FROM_SOURCE); + return chain.filter(exchange.mutate().request(mutate.build()).build()); + } + + private void addHeader (ServerHttpRequest.Builder mutate, String name, Object value) { + if (value == null) { + return; + } + String valueStr = value.toString(); + String valueEncode = ServletUtils.urlEncode(valueStr); + mutate.header(name, valueEncode); + } + + private void removeHeader (ServerHttpRequest.Builder mutate, String name) { + mutate.headers(httpHeaders -> httpHeaders.remove(name)).build(); + } + + private Mono unauthorizedResponse (ServerWebExchange exchange, String msg) { + log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath()); + return ServletUtils.webFluxResponseWriter(exchange.getResponse(), msg, HttpStatus.UNAUTHORIZED); + } + + /** + * 获取缓存key + */ + private String getTokenKey (String token) { + return CacheConstants.LOGIN_TOKEN_KEY + token; + } + + /** + * 获取请求token + */ + private String getToken (ServerHttpRequest request) { + String token = request.getHeaders().getFirst(TokenConstants.AUTHENTICATION); + // 如果前端设置了令牌前缀,则裁剪掉前缀 + if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX)) { + token = token.replaceFirst(TokenConstants.PREFIX, StringUtils.EMPTY); + } + return token; + } + + @Override + public int getOrder () { + return -200; + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/filter/BlackListUrlFilter.java b/cloud-gateway/src/main/java/com/muyu/gateway/filter/BlackListUrlFilter.java new file mode 100644 index 0000000..b59634e --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/filter/BlackListUrlFilter.java @@ -0,0 +1,58 @@ +package com.muyu.gateway.filter; + +import com.muyu.common.core.utils.ServletUtils; +import org.springframework.cloud.gateway.filter.GatewayFilter; +import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +/** + * 黑名单过滤器 + * + * @author muyu + */ +@Component +public class BlackListUrlFilter extends AbstractGatewayFilterFactory { + public BlackListUrlFilter () { + super(Config.class); + } + + @Override + public GatewayFilter apply (Config config) { + return (exchange, chain) -> { + + String url = exchange.getRequest().getURI().getPath(); + if (config.matchBlacklist(url)) { + return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "请求地址不允许访问"); + } + + return chain.filter(exchange); + }; + } + + public static class Config { + private List blacklistUrl; + + private final List blacklistUrlPattern = new ArrayList<>(); + + public boolean matchBlacklist (String url) { + return !blacklistUrlPattern.isEmpty() && blacklistUrlPattern.stream().anyMatch(p -> p.matcher(url).find()); + } + + public List getBlacklistUrl () { + return blacklistUrl; + } + + public void setBlacklistUrl (List blacklistUrl) { + this.blacklistUrl = blacklistUrl; + this.blacklistUrlPattern.clear(); + this.blacklistUrl.forEach(url -> { + this.blacklistUrlPattern.add(Pattern.compile(url.replaceAll("\\*\\*", "(.*?)"), Pattern.CASE_INSENSITIVE)); + }); + } + } + +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/filter/CacheRequestFilter.java b/cloud-gateway/src/main/java/com/muyu/gateway/filter/CacheRequestFilter.java new file mode 100644 index 0000000..3a09564 --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/filter/CacheRequestFilter.java @@ -0,0 +1,75 @@ +package com.muyu.gateway.filter; + +import org.springframework.cloud.gateway.filter.GatewayFilter; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.OrderedGatewayFilter; +import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; +import org.springframework.cloud.gateway.support.ServerWebExchangeUtils; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +import java.util.Collections; +import java.util.List; + +/** + * 获取body请求数据(解决流不能重复读取问题) + * + * @author muyu + */ +@Component +public class CacheRequestFilter extends AbstractGatewayFilterFactory { + public CacheRequestFilter () { + super(Config.class); + } + + @Override + public String name () { + return "CacheRequestFilter"; + } + + @Override + public GatewayFilter apply (Config config) { + CacheRequestGatewayFilter cacheRequestGatewayFilter = new CacheRequestGatewayFilter(); + Integer order = config.getOrder(); + if (order == null) { + return cacheRequestGatewayFilter; + } + return new OrderedGatewayFilter(cacheRequestGatewayFilter, order); + } + + @Override + public List shortcutFieldOrder () { + return Collections.singletonList("order"); + } + + public static class CacheRequestGatewayFilter implements GatewayFilter { + @Override + public Mono filter (ServerWebExchange exchange, GatewayFilterChain chain) { + // GET DELETE 不过滤 + HttpMethod method = exchange.getRequest().getMethod(); + if (method == null || method == HttpMethod.GET || method == HttpMethod.DELETE) { + return chain.filter(exchange); + } + return ServerWebExchangeUtils.cacheRequestBodyAndRequest(exchange, (serverHttpRequest) -> { + if (serverHttpRequest == exchange.getRequest()) { + return chain.filter(exchange); + } + return chain.filter(exchange.mutate().request(serverHttpRequest).build()); + }); + } + } + + static class Config { + private Integer order; + + public Integer getOrder () { + return order; + } + + public void setOrder (Integer order) { + this.order = order; + } + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/filter/ValidateCodeFilter.java b/cloud-gateway/src/main/java/com/muyu/gateway/filter/ValidateCodeFilter.java new file mode 100644 index 0000000..c19c944 --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/filter/ValidateCodeFilter.java @@ -0,0 +1,69 @@ +package com.muyu.gateway.filter; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.muyu.common.core.utils.ServletUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.gateway.config.properties.CaptchaProperties; +import com.muyu.gateway.service.ValidateCodeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.gateway.filter.GatewayFilter; +import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Flux; + +import java.nio.CharBuffer; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.atomic.AtomicReference; + +/** + * 验证码过滤器 + * + * @author muyu + */ +@Component +public class ValidateCodeFilter extends AbstractGatewayFilterFactory { + private final static String[] VALIDATE_URL = new String[]{"/auth/login", "/auth/register"}; + private static final String CODE = "code"; + private static final String UUID = "uuid"; + @Autowired + private ValidateCodeService validateCodeService; + @Autowired + private CaptchaProperties captchaProperties; + + @Override + public GatewayFilter apply (Object config) { + return (exchange, chain) -> { + ServerHttpRequest request = exchange.getRequest(); + + // 非登录/注册请求或验证码关闭,不处理 + if (!StringUtils.equalsAnyIgnoreCase(request.getURI().getPath(), VALIDATE_URL) || !captchaProperties.getEnabled()) { + return chain.filter(exchange); + } + + try { + String rspStr = resolveBodyFromRequest(request); + JSONObject obj = JSON.parseObject(rspStr); + validateCodeService.checkCaptcha(obj.getString(CODE), obj.getString(UUID)); + } catch (Exception e) { + return ServletUtils.webFluxResponseWriter(exchange.getResponse(), e.getMessage()); + } + return chain.filter(exchange); + }; + } + + private String resolveBodyFromRequest (ServerHttpRequest serverHttpRequest) { + // 获取请求体 + Flux body = serverHttpRequest.getBody(); + AtomicReference bodyRef = new AtomicReference<>(); + body.subscribe(buffer -> { + CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer()); + DataBufferUtils.release(buffer); + bodyRef.set(charBuffer.toString()); + }); + return bodyRef.get(); + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/filter/XssFilter.java b/cloud-gateway/src/main/java/com/muyu/gateway/filter/XssFilter.java new file mode 100644 index 0000000..4316d84 --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/filter/XssFilter.java @@ -0,0 +1,114 @@ +package com.muyu.gateway.filter; + +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.utils.html.EscapeUtil; +import com.muyu.gateway.config.properties.XssProperties; +import io.netty.buffer.ByteBufAllocator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.Ordered; +import org.springframework.core.io.buffer.*; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpRequestDecorator; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.nio.charset.StandardCharsets; + +/** + * 跨站脚本过滤器 + * + * @author muyu + */ +@Component +@ConditionalOnProperty(value = "security.xss.enabled", havingValue = "true") +public class XssFilter implements GlobalFilter, Ordered { + // 跨站脚本的 xss 配置,nacos自行添加 + @Autowired + private XssProperties xss; + + @Override + public Mono filter (ServerWebExchange exchange, GatewayFilterChain chain) { + ServerHttpRequest request = exchange.getRequest(); + // xss开关未开启 或 通过nacos关闭,不过滤 + if (!xss.getEnabled()) { + return chain.filter(exchange); + } + // GET DELETE 不过滤 + HttpMethod method = request.getMethod(); + if (method == null || method == HttpMethod.GET || method == HttpMethod.DELETE) { + return chain.filter(exchange); + } + // 非json类型,不过滤 + if (!isJsonRequest(exchange)) { + return chain.filter(exchange); + } + // excludeUrls 不过滤 + String url = request.getURI().getPath(); + if (StringUtils.matches(url, xss.getExcludeUrls())) { + return chain.filter(exchange); + } + ServerHttpRequestDecorator httpRequestDecorator = requestDecorator(exchange); + return chain.filter(exchange.mutate().request(httpRequestDecorator).build()); + + } + + private ServerHttpRequestDecorator requestDecorator (ServerWebExchange exchange) { + ServerHttpRequestDecorator serverHttpRequestDecorator = new ServerHttpRequestDecorator(exchange.getRequest()) { + @Override + public Flux getBody () { + Flux body = super.getBody(); + return body.buffer().map(dataBuffers -> { + DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory(); + DataBuffer join = dataBufferFactory.join(dataBuffers); + byte[] content = new byte[join.readableByteCount()]; + join.read(content); + DataBufferUtils.release(join); + String bodyStr = new String(content, StandardCharsets.UTF_8); + // 防xss攻击过滤 + bodyStr = EscapeUtil.clean(bodyStr); + // 转成字节 + byte[] bytes = bodyStr.getBytes(StandardCharsets.UTF_8); + NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT); + DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length); + buffer.write(bytes); + return buffer; + }); + } + + @Override + public HttpHeaders getHeaders () { + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.putAll(super.getHeaders()); + // 由于修改了请求体的body,导致content-length长度不确定,因此需要删除原先的content-length + httpHeaders.remove(HttpHeaders.CONTENT_LENGTH); + httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked"); + return httpHeaders; + } + + }; + return serverHttpRequestDecorator; + } + + /** + * 是否是Json请求 + * + * @param exchange HTTP请求 + */ + public boolean isJsonRequest (ServerWebExchange exchange) { + String header = exchange.getRequest().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE); + return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE); + } + + @Override + public int getOrder () { + return -100; + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/handler/GatewayExceptionHandler.java b/cloud-gateway/src/main/java/com/muyu/gateway/handler/GatewayExceptionHandler.java new file mode 100644 index 0000000..25e13bf --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/handler/GatewayExceptionHandler.java @@ -0,0 +1,47 @@ +package com.muyu.gateway.handler; + +import com.muyu.common.core.utils.ServletUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler; +import org.springframework.cloud.gateway.support.NotFoundException; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.web.server.ResponseStatusException; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * 网关统一异常处理 + * + * @author muyu + */ +@Order(-1) +@Configuration +public class GatewayExceptionHandler implements ErrorWebExceptionHandler { + private static final Logger log = LoggerFactory.getLogger(GatewayExceptionHandler.class); + + @Override + public Mono handle (ServerWebExchange exchange, Throwable ex) { + ServerHttpResponse response = exchange.getResponse(); + + if (exchange.getResponse().isCommitted()) { + return Mono.error(ex); + } + + String msg; + + if (ex instanceof NotFoundException) { + msg = "服务未找到"; + } else if (ex instanceof ResponseStatusException responseStatusException) { + msg = responseStatusException.getMessage(); + } else { + msg = "内部服务器错误"; + } + + log.error("[网关异常处理]请求路径:{},异常信息:{}", exchange.getRequest().getPath(), ex.getMessage()); + + return ServletUtils.webFluxResponseWriter(response, msg); + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/handler/SentinelFallbackHandler.java b/cloud-gateway/src/main/java/com/muyu/gateway/handler/SentinelFallbackHandler.java new file mode 100644 index 0000000..d93866f --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/handler/SentinelFallbackHandler.java @@ -0,0 +1,35 @@ +package com.muyu.gateway.handler; + +import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager; +import com.alibaba.csp.sentinel.slots.block.BlockException; +import com.muyu.common.core.utils.ServletUtils; +import org.springframework.web.reactive.function.server.ServerResponse; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebExceptionHandler; +import reactor.core.publisher.Mono; + +/** + * 自定义限流异常处理 + * + * @author muyu + */ +public class SentinelFallbackHandler implements WebExceptionHandler { + private Mono writeResponse (ServerResponse response, ServerWebExchange exchange) { + return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "请求超过最大数,请稍候再试"); + } + + @Override + public Mono handle (ServerWebExchange exchange, Throwable ex) { + if (exchange.getResponse().isCommitted()) { + return Mono.error(ex); + } + if (!BlockException.isBlockException(ex)) { + return Mono.error(ex); + } + return handleBlockedRequest(exchange, ex).flatMap(response -> writeResponse(response, exchange)); + } + + private Mono handleBlockedRequest (ServerWebExchange exchange, Throwable throwable) { + return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable); + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/handler/ValidateCodeHandler.java b/cloud-gateway/src/main/java/com/muyu/gateway/handler/ValidateCodeHandler.java new file mode 100644 index 0000000..f3b3206 --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/handler/ValidateCodeHandler.java @@ -0,0 +1,37 @@ +package com.muyu.gateway.handler; + +import com.muyu.common.core.exception.CaptchaException; +import com.muyu.common.core.domain.Result; +import com.muyu.gateway.service.ValidateCodeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.BodyInserters; +import org.springframework.web.reactive.function.server.HandlerFunction; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; + +import java.io.IOException; + +/** + * 验证码获取 + * + * @author muyu + */ +@Component +public class ValidateCodeHandler implements HandlerFunction { + @Autowired + private ValidateCodeService validateCodeService; + + @Override + public Mono handle (ServerRequest serverRequest) { + Result ajax; + try { + ajax = validateCodeService.createCaptcha(); + } catch (CaptchaException | IOException e) { + return Mono.error(e); + } + return ServerResponse.status(HttpStatus.OK).body(BodyInserters.fromValue(ajax)); + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/model/resp/CaptchaCodeResp.java b/cloud-gateway/src/main/java/com/muyu/gateway/model/resp/CaptchaCodeResp.java new file mode 100644 index 0000000..bd12aad --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/model/resp/CaptchaCodeResp.java @@ -0,0 +1,25 @@ +package com.muyu.gateway.model.resp; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author DongZl + * @description: 验证码 + * @Date 2023-11-12 下午 03:36 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CaptchaCodeResp { + + private boolean captchaEnabled; + + private String uuid; + + private String img; + +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/service/ValidateCodeService.java b/cloud-gateway/src/main/java/com/muyu/gateway/service/ValidateCodeService.java new file mode 100644 index 0000000..d3381f4 --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/service/ValidateCodeService.java @@ -0,0 +1,23 @@ +package com.muyu.gateway.service; + +import com.muyu.common.core.exception.CaptchaException; +import com.muyu.common.core.domain.Result; + +import java.io.IOException; + +/** + * 验证码处理 + * + * @author muyu + */ +public interface ValidateCodeService { + /** + * 生成验证码 + */ + Result createCaptcha() throws IOException, CaptchaException; + + /** + * 校验验证码 + */ + void checkCaptcha(String key, String value) throws CaptchaException; +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/service/impl/ValidateCodeServiceImpl.java b/cloud-gateway/src/main/java/com/muyu/gateway/service/impl/ValidateCodeServiceImpl.java new file mode 100644 index 0000000..978a3b4 --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/service/impl/ValidateCodeServiceImpl.java @@ -0,0 +1,109 @@ +package com.muyu.gateway.service.impl; + +import com.google.code.kaptcha.Producer; +import com.muyu.common.core.constant.CacheConstants; +import com.muyu.common.core.constant.Constants; +import com.muyu.common.core.exception.CaptchaException; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.utils.sign.Base64; +import com.muyu.common.core.utils.uuid.IdUtils; +import com.muyu.common.core.domain.Result; +import com.muyu.common.redis.service.RedisService; +import com.muyu.gateway.config.properties.CaptchaProperties; +import com.muyu.gateway.model.resp.CaptchaCodeResp; +import com.muyu.gateway.service.ValidateCodeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.FastByteArrayOutputStream; + +import javax.annotation.Resource; +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +/** + * 验证码实现处理 + * + * @author muyu + */ +@Service +public class ValidateCodeServiceImpl implements ValidateCodeService { + @Resource(name = "captchaProducer") + private Producer captchaProducer; + + @Resource(name = "captchaProducerMath") + private Producer captchaProducerMath; + + @Autowired + private RedisService redisService; + + @Autowired + private CaptchaProperties captchaProperties; + + /** + * 生成验证码 + */ + @Override + public Result createCaptcha () throws IOException, CaptchaException { + boolean captchaEnabled = captchaProperties.getEnabled(); + CaptchaCodeResp.CaptchaCodeRespBuilder respBuilder = CaptchaCodeResp.builder() + .captchaEnabled(captchaEnabled); + if (!captchaEnabled) { + return Result.success(respBuilder.build()); + } + + // 保存验证码信息 + String uuid = IdUtils.simpleUUID(); + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid; + + String capStr = null, code = null; + BufferedImage image = null; + + String captchaType = captchaProperties.getType(); + // 生成验证码 + 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); + } + + redisService.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES); + // 转换流信息写出 + FastByteArrayOutputStream os = new FastByteArrayOutputStream(); + try { + ImageIO.write(image, "jpg", os); + } catch (IOException e) { + return Result.error(e.getMessage()); + } + + CaptchaCodeResp captchaCodeResp = respBuilder.uuid(uuid) + .img(Base64.encode(os.toByteArray())) + .build(); + return Result.success(captchaCodeResp); + } + + /** + * 校验验证码 + */ + @Override + public void checkCaptcha (String code, String uuid) throws CaptchaException { + if (StringUtils.isEmpty(code)) { + throw new CaptchaException("验证码不能为空"); + } + if (StringUtils.isEmpty(uuid)) { + throw new CaptchaException("验证码已失效"); + } + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid; + String captcha = redisService.getCacheObject(verifyKey); + redisService.deleteObject(verifyKey); + + if (!code.equalsIgnoreCase(captcha)) { + throw new CaptchaException("验证码错误"); + } + } +} diff --git a/cloud-gateway/src/main/java/com/muyu/gateway/utils/WebFrameworkUtils.java b/cloud-gateway/src/main/java/com/muyu/gateway/utils/WebFrameworkUtils.java new file mode 100644 index 0000000..68b0a19 --- /dev/null +++ b/cloud-gateway/src/main/java/com/muyu/gateway/utils/WebFrameworkUtils.java @@ -0,0 +1,105 @@ +package com.muyu.gateway.utils; + +import cn.hutool.core.net.NetUtil; +import cn.hutool.core.util.ArrayUtil; +import com.alibaba.fastjson2.JSONObject; +import lombok.extern.log4j.Log4j2; +import org.springframework.cloud.gateway.route.Route; +import org.springframework.cloud.gateway.support.ServerWebExchangeUtils; +import org.springframework.core.io.buffer.DataBufferFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * Web 工具类 + */ +@Log4j2 +public class WebFrameworkUtils { + + private static final String HEADER_TENANT_ID = "tenant-id"; + + private WebFrameworkUtils() {} + + /** + * 将 Gateway 请求中的 header,设置到 HttpHeaders 中 + * + * @param tenantId 租户编号 + * @param httpHeaders WebClient 的请求 + */ + public static void setTenantIdHeader(Long tenantId, HttpHeaders httpHeaders) { + if (tenantId == null) { + return; + } + httpHeaders.set(HEADER_TENANT_ID, String.valueOf(tenantId)); + } + + public static Long getTenantId(ServerWebExchange exchange) { + String tenantId = exchange.getRequest().getHeaders().getFirst(HEADER_TENANT_ID); + return tenantId != null ? Long.parseLong(tenantId) : null; + } + + /** + * 返回 JSON 字符串 + * + * @param exchange 响应 + * @param object 对象,会序列化成 JSON 字符串 + */ + @SuppressWarnings("deprecation") // 必须使用 APPLICATION_JSON_UTF8_VALUE,否则会乱码 + public static Mono writeJSON(ServerWebExchange exchange, Object object) { + // 设置 header + ServerHttpResponse response = exchange.getResponse(); + response.getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8); + // 设置 body + return response.writeWith(Mono.fromSupplier(() -> { + DataBufferFactory bufferFactory = response.bufferFactory(); + try { + return bufferFactory.wrap(JSONObject.toJSONString(object).getBytes()); + } catch (Exception ex) { + ServerHttpRequest request = exchange.getRequest(); + log.error("[writeJSON][uri({}/{}) 发生异常]", request.getURI(), request.getMethod(), ex); + return bufferFactory.wrap(new byte[0]); + } + })); + } + + /** + * 获得客户端 IP + * @param exchange 请求 + * @param otherHeaderNames 其它 header 名字的数组 + * @return 客户端 IP + */ + public static String getClientIP(ServerWebExchange exchange, String... otherHeaderNames) { + String[] headers = { "X-Forwarded-For", "X-Real-IP", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR" }; + if (ArrayUtil.isNotEmpty(otherHeaderNames)) { + headers = ArrayUtil.addAll(headers, otherHeaderNames); + } + // 方式一,通过 header 获取 + String ip; + for (String header : headers) { + ip = exchange.getRequest().getHeaders().getFirst(header); + if (!NetUtil.isUnknown(ip)) { + return NetUtil.getMultistageReverseProxyIp(ip); + } + } + // 方式二,通过 remoteAddress 获取 + if (exchange.getRequest().getRemoteAddress() == null) { + return null; + } + ip = exchange.getRequest().getRemoteAddress().getHostString(); + return NetUtil.getMultistageReverseProxyIp(ip); + } + + /** + * 获得请求匹配的 Route 路由 + * @param exchange 请求 + * @return 路由 + */ + public static Route getGatewayRoute(ServerWebExchange exchange) { + return exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR); + } + +} diff --git a/cloud-gateway/src/main/resources/banner.txt b/cloud-gateway/src/main/resources/banner.txt new file mode 100644 index 0000000..0dd5eee --- /dev/null +++ b/cloud-gateway/src/main/resources/banner.txt @@ -0,0 +1,2 @@ +Spring Boot Version: ${spring-boot.version} +Spring Application Name: ${spring.application.name} diff --git a/cloud-gateway/src/main/resources/logback/dev.xml b/cloud-gateway/src/main/resources/logback/dev.xml new file mode 100644 index 0000000..5ac21d0 --- /dev/null +++ b/cloud-gateway/src/main/resources/logback/dev.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + + + diff --git a/cloud-gateway/src/main/resources/logback/prod.xml b/cloud-gateway/src/main/resources/logback/prod.xml new file mode 100644 index 0000000..971e45e --- /dev/null +++ b/cloud-gateway/src/main/resources/logback/prod.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + ${log.sky.pattern} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cloud-gateway/src/main/resources/logback/test.xml b/cloud-gateway/src/main/resources/logback/test.xml new file mode 100644 index 0000000..971e45e --- /dev/null +++ b/cloud-gateway/src/main/resources/logback/test.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + ${log.sky.pattern} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cloud-modules/cloud-modules-file/pom.xml b/cloud-modules/cloud-modules-file/pom.xml new file mode 100644 index 0000000..e151176 --- /dev/null +++ b/cloud-modules/cloud-modules-file/pom.xml @@ -0,0 +1,82 @@ + + + + com.muyu + cloud-modules + 3.6.3 + + 4.0.0 + + cloud-modules-file + + + cloud-modules-file文件服务 + + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + io.minio + minio + ${minio.version} + + + + + com.muyu + cloud-common-system + + + + + com.muyu + cloud-common-api-doc + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + + diff --git a/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/CloudFileApplication.java b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/CloudFileApplication.java new file mode 100644 index 0000000..4e1c628 --- /dev/null +++ b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/CloudFileApplication.java @@ -0,0 +1,17 @@ +package com.muyu.file; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; + +/** + * 文件服务 + * + * @author muyu + */ +@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) +public class CloudFileApplication { + public static void main (String[] args) { + SpringApplication.run(CloudFileApplication.class, args); + } +} diff --git a/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/config/MinioConfig.java b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/config/MinioConfig.java new file mode 100644 index 0000000..af8c1bb --- /dev/null +++ b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/config/MinioConfig.java @@ -0,0 +1,72 @@ +package com.muyu.file.config; + +import io.minio.MinioClient; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Minio 配置信息 + * + * @author muyu + */ +@Configuration +@ConfigurationProperties(prefix = "minio") +public class MinioConfig { + /** + * 服务地址 + */ + private String url; + + /** + * 用户名 + */ + private String accessKey; + + /** + * 密码 + */ + private String secretKey; + + /** + * 存储桶名称 + */ + private String bucketName; + + public String getUrl () { + return url; + } + + public void setUrl (String url) { + this.url = url; + } + + public String getAccessKey () { + return accessKey; + } + + public void setAccessKey (String accessKey) { + this.accessKey = accessKey; + } + + public String getSecretKey () { + return secretKey; + } + + public void setSecretKey (String secretKey) { + this.secretKey = secretKey; + } + + public String getBucketName () { + return bucketName; + } + + public void setBucketName (String bucketName) { + this.bucketName = bucketName; + } + + @Bean + public MinioClient getMinioClient () { + return MinioClient.builder().endpoint(url).credentials(accessKey, secretKey).build(); + } +} diff --git a/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/config/ResourcesConfig.java b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/config/ResourcesConfig.java new file mode 100644 index 0000000..78a8be0 --- /dev/null +++ b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/config/ResourcesConfig.java @@ -0,0 +1,48 @@ +package com.muyu.file.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.io.File; + +/** + * 通用映射配置 + * + * @author muyu + */ +@Configuration +public class ResourcesConfig implements WebMvcConfigurer { + /** + * 资源映射路径 前缀 + */ + @Value("${file.prefix}") + public String localFilePrefix; + /** + * 上传文件存储在本地的根路径 + */ + @Value("${file.path}") + private String localFilePath; + + @Override + public void addResourceHandlers (ResourceHandlerRegistry registry) { + /** 本地文件上传路径 */ + registry.addResourceHandler(localFilePrefix + "/**") + .addResourceLocations("file:" + localFilePath + File.separator); + } + + /** + * 开启跨域 + */ + @Override + public void addCorsMappings (CorsRegistry registry) { + // 设置允许跨域的路由 + registry.addMapping(localFilePrefix + "/**") + // 设置允许跨域请求的域名 + .allowedOrigins("*") + // 设置允许的方法 + .allowedMethods("GET"); + } +} diff --git a/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/controller/SysFileController.java b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/controller/SysFileController.java new file mode 100644 index 0000000..ed04d11 --- /dev/null +++ b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/controller/SysFileController.java @@ -0,0 +1,44 @@ +package com.muyu.file.controller; + +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.utils.file.FileUtils; +import com.muyu.file.service.ISysFileService; +import com.muyu.common.system.domain.SysFile; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +/** + * 文件请求处理 + * + * @author muyu + */ +@RestController +public class SysFileController { + private static final Logger log = LoggerFactory.getLogger(SysFileController.class); + + @Autowired + private ISysFileService sysFileService; + + /** + * 文件上传请求 + */ + @PostMapping("/upload") + public Result upload (@RequestPart(value = "file") MultipartFile file) { + try { + // 上传并返回访问地址 + String url = sysFileService.uploadFile(file); + SysFile sysFile = new SysFile(); + sysFile.setName(FileUtils.getName(url)); + sysFile.setUrl(url); + return Result.success(sysFile); + } catch (Exception e) { + log.error("上传文件失败", e); + return Result.error(e.getMessage()); + } + } +} diff --git a/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/service/ISysFileService.java b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/service/ISysFileService.java new file mode 100644 index 0000000..54ea680 --- /dev/null +++ b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/service/ISysFileService.java @@ -0,0 +1,21 @@ +package com.muyu.file.service; + +import org.springframework.web.multipart.MultipartFile; + +/** + * 文件上传接口 + * + * @author muyu + */ +public interface ISysFileService { + /** + * 文件上传接口 + * + * @param file 上传的文件 + * + * @return 访问地址 + * + * @throws Exception + */ + String uploadFile(MultipartFile file) throws Exception; +} diff --git a/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/service/LocalSysFileServiceImpl.java b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/service/LocalSysFileServiceImpl.java new file mode 100644 index 0000000..f3ce089 --- /dev/null +++ b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/service/LocalSysFileServiceImpl.java @@ -0,0 +1,50 @@ +package com.muyu.file.service; + +import com.muyu.file.utils.FileUploadUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +/** + * 本地文件存储 + * + * @author muyu + */ +@Primary +@Service +public class LocalSysFileServiceImpl implements ISysFileService { + /** + * 资源映射路径 前缀 + */ + @Value("${file.prefix}") + public String localFilePrefix; + + /** + * 域名或本机访问地址 + */ + @Value("${file.domain}") + public String domain; + + /** + * 上传文件存储在本地的根路径 + */ + @Value("${file.path}") + private String localFilePath; + + /** + * 本地文件上传接口 + * + * @param file 上传的文件 + * + * @return 访问地址 + * + * @throws Exception + */ + @Override + public String uploadFile (MultipartFile file) throws Exception { + String name = FileUploadUtils.upload(localFilePath, file); + String url = domain + localFilePrefix + name; + return url; + } +} diff --git a/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/service/MinioSysFileServiceImpl.java b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/service/MinioSysFileServiceImpl.java new file mode 100644 index 0000000..9a50de4 --- /dev/null +++ b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/service/MinioSysFileServiceImpl.java @@ -0,0 +1,50 @@ +package com.muyu.file.service; + +import com.alibaba.nacos.common.utils.IoUtils; +import com.muyu.file.config.MinioConfig; +import com.muyu.file.utils.FileUploadUtils; +import io.minio.MinioClient; +import io.minio.PutObjectArgs; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.InputStream; + +/** + * Minio 文件存储 + * + * @author muyu + */ +@Service +public class MinioSysFileServiceImpl implements ISysFileService { + @Autowired + private MinioConfig minioConfig; + + @Autowired + private MinioClient client; + + /** + * Minio文件上传接口 + * + * @param file 上传的文件 + * + * @return 访问地址 + * + * @throws Exception + */ + @Override + public String uploadFile (MultipartFile file) throws Exception { + String fileName = FileUploadUtils.extractFilename(file); + InputStream inputStream = file.getInputStream(); + PutObjectArgs args = PutObjectArgs.builder() + .bucket(minioConfig.getBucketName()) + .object(fileName) + .stream(inputStream, file.getSize(), -1) + .contentType(file.getContentType()) + .build(); + client.putObject(args); + IoUtils.closeQuietly(inputStream); + return minioConfig.getUrl() + "/" + minioConfig.getBucketName() + "/" + fileName; + } +} diff --git a/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/utils/FileUploadUtils.java b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/utils/FileUploadUtils.java new file mode 100644 index 0000000..0a48cbf --- /dev/null +++ b/cloud-modules/cloud-modules-file/src/main/java/com/muyu/file/utils/FileUploadUtils.java @@ -0,0 +1,163 @@ +package com.muyu.file.utils; + +import com.muyu.common.core.exception.file.FileException; +import com.muyu.common.core.exception.file.FileNameLengthLimitExceededException; +import com.muyu.common.core.exception.file.FileSizeLimitExceededException; +import com.muyu.common.core.exception.file.InvalidExtensionException; +import com.muyu.common.core.utils.DateUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.utils.file.FileTypeUtils; +import com.muyu.common.core.utils.file.MimeTypeUtils; +import com.muyu.common.core.utils.uuid.Seq; +import org.apache.commons.io.FilenameUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.Objects; + +/** + * 文件上传工具类 + * + * @author muyu + */ +public class FileUploadUtils { + /** + * 默认大小 50M + */ + public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024; + + /** + * 默认的文件名最大长度 100 + */ + public static final int DEFAULT_FILE_NAME_LENGTH = 100; + + /** + * 根据文件路径上传 + * + * @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 (FileException fe) { + throw new IOException(fe.getDefaultMessage(), fe); + } 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(fileName); + } + + /** + * 编码文件名 + */ + public static final String extractFilename (MultipartFile file) { + return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(), + FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), FileTypeUtils.getExtension(file)); + } + + private 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.isAbsolute() ? desc : desc.getAbsoluteFile(); + } + + private static final String getPathFileName (String fileName) throws IOException { + String pathFileName = "/" + fileName; + return pathFileName; + } + + /** + * 文件大小校验 + * + * @param file 上传的文件 + * + * @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 = FileTypeUtils.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 true/false + */ + public static final boolean isAllowedExtension (String extension, String[] allowedExtension) { + for (String str : allowedExtension) { + if (str.equalsIgnoreCase(extension)) { + return true; + } + } + return false; + } +} diff --git a/cloud-modules/cloud-modules-file/src/main/resources/banner.txt b/cloud-modules/cloud-modules-file/src/main/resources/banner.txt new file mode 100644 index 0000000..0dd5eee --- /dev/null +++ b/cloud-modules/cloud-modules-file/src/main/resources/banner.txt @@ -0,0 +1,2 @@ +Spring Boot Version: ${spring-boot.version} +Spring Application Name: ${spring.application.name} diff --git a/cloud-modules/cloud-modules-file/src/main/resources/logback/dev.xml b/cloud-modules/cloud-modules-file/src/main/resources/logback/dev.xml new file mode 100644 index 0000000..30606f7 --- /dev/null +++ b/cloud-modules/cloud-modules-file/src/main/resources/logback/dev.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + + + diff --git a/cloud-modules/cloud-modules-file/src/main/resources/logback/prod.xml b/cloud-modules/cloud-modules-file/src/main/resources/logback/prod.xml new file mode 100644 index 0000000..151a3cb --- /dev/null +++ b/cloud-modules/cloud-modules-file/src/main/resources/logback/prod.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + ${log.sky.pattern} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cloud-modules/cloud-modules-file/src/main/resources/logback/test.xml b/cloud-modules/cloud-modules-file/src/main/resources/logback/test.xml new file mode 100644 index 0000000..151a3cb --- /dev/null +++ b/cloud-modules/cloud-modules-file/src/main/resources/logback/test.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + ${log.sky.pattern} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cloud-modules/cloud-modules-gen/pom.xml b/cloud-modules/cloud-modules-gen/pom.xml new file mode 100644 index 0000000..c5de3a6 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/pom.xml @@ -0,0 +1,93 @@ + + + + com.muyu + cloud-modules + 3.6.3 + + 4.0.0 + + cloud-modules-gen + + + cloud-modules-gen代码生成 + + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + org.apache.velocity + velocity-engine-core + + + + + com.mysql + mysql-connector-j + + + + + com.muyu + cloud-common-log + + + + + com.muyu + cloud-common-api-doc + + + + + com.muyu + cloud-common-xxl + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + + diff --git a/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/CloudGenApplication.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/CloudGenApplication.java new file mode 100644 index 0000000..3cb1f39 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/CloudGenApplication.java @@ -0,0 +1,21 @@ +package com.muyu.gen; + +import com.muyu.common.security.annotation.EnableCustomConfig; +import com.muyu.common.security.annotation.EnableMyFeignClients; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * 代码生成 + * + * @author muyu + */ +@EnableCustomConfig +@EnableMyFeignClients +@SpringBootApplication +public class CloudGenApplication { + public static void main (String[] args) { + SpringApplication.run(CloudGenApplication.class, args); + System.out.println("CloudGen 模块启动成功!"); + } +} diff --git a/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/config/GenConfig.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/config/GenConfig.java new file mode 100644 index 0000000..65d9824 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/config/GenConfig.java @@ -0,0 +1,65 @@ +package com.muyu.gen.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 代码生成相关配置 + * + * @author muyu + */ +@Component +@ConfigurationProperties(prefix = "gen") +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; + } + + public void setAuthor (String author) { + GenConfig.author = author; + } + + public static String getPackageName () { + return packageName; + } + + public void setPackageName (String packageName) { + GenConfig.packageName = packageName; + } + + public static boolean getAutoRemovePre () { + return autoRemovePre; + } + + public void setAutoRemovePre (boolean autoRemovePre) { + GenConfig.autoRemovePre = autoRemovePre; + } + + public static String getTablePrefix () { + return tablePrefix; + } + + public void setTablePrefix (String tablePrefix) { + GenConfig.tablePrefix = tablePrefix; + } +} diff --git a/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/controller/GenController.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/controller/GenController.java new file mode 100644 index 0000000..f93075e --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/controller/GenController.java @@ -0,0 +1,236 @@ +package com.muyu.gen.controller; + +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.text.Convert; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.web.page.TableDataInfo; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.gen.domain.GenTable; +import com.muyu.gen.domain.GenTableColumn; +import com.muyu.gen.domain.GenTableResp; +import com.muyu.gen.service.IGenTableColumnService; +import com.muyu.gen.service.IGenTableService; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.io.IOUtils; + +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 代码生成 操作处理 + * + * @author ruoyi + */ +@RequestMapping("/gen") +@RestController +public class GenController extends BaseController +{ + @Resource + private IGenTableService genTableService; + + @Resource + private HttpServletResponse response; + + @Resource + private IGenTableColumnService genTableColumnService; + + /** + * 查询代码生成列表 + */ + @RequiresPermissions("tool:gen:list") + @GetMapping("/list") + public Result genList(GenTable genTable) + { + startPage(); + List list = genTableService.selectGenTableList(genTable); + return getDataTable(list); + } + + /** + * 修改代码生成业务 + */ + @RequiresPermissions("tool:gen:query") + @GetMapping(value = "/{tableId}") + public Result getInfo(@PathVariable("tableId") Long tableId) + { + GenTable table = genTableService.selectGenTableById(tableId); + List tables = genTableService.selectGenTableAll(); + List list = genTableColumnService.selectGenTableColumnListByTableId(tableId); + Map map = new HashMap(); + map.put("info", table); + map.put("rows", list); + map.put("tables", tables); + return success(map); + } + + + /** + * 查询数据库列表 + */ + @RequiresPermissions("tool:gen:list") + @GetMapping("/db/list") + public Result dataList(GenTable genTable) + { + startPage(); + List list = genTableService.selectDbTableList(genTable); + return getDataTable(list); + } + + /** + * 查询数据表字段列表 + */ + @GetMapping(value = "/column/{tableId}") + public Result columnList(@PathVariable("tableId") Long tableId) + { + TableDataInfo dataInfo = new TableDataInfo(); + List list = genTableColumnService.selectGenTableColumnListByTableId(tableId); + dataInfo.setRows(list); + dataInfo.setTotal(list.size()); + return success(dataInfo); + } + + /** + * 导入表结构(保存) + */ +// @RequiresPermissions("tool:gen:import") + @Log(title = "代码生成", businessType = BusinessType.IMPORT) + @PostMapping("/importTable") + public Result importTableSave(@RequestParam("tables") String tables, @RequestParam("dbName") String dbName) + { + String[] tableNames = Convert.toStrArray(tables); + // 查询表信息 + List tableList = genTableService.selectDbTableListByNames(tableNames,dbName); + genTableService.importGenTable(tableList); + return success(); + } + + /** + * 修改保存代码生成业务 + */ + @RequiresPermissions("tool:gen:edit") + @Log(title = "代码生成", businessType = BusinessType.UPDATE) + @PutMapping + public Result editSave(@Validated @RequestBody GenTable genTable) + { + genTableService.validateEdit(genTable); + genTableService.updateGenTable(genTable); + return success(); + } + + /** + * 删除代码生成 + */ + @RequiresPermissions("tool:gen:remove") + @Log(title = "代码生成", businessType = BusinessType.DELETE) + @DeleteMapping("/{tableIds}") + public Result remove(@PathVariable("tableIds") Long[] tableIds) + { + genTableService.deleteGenTableByIds(tableIds); + return success(); + } + + /** + * 预览代码 + */ + @RequiresPermissions("tool:gen:preview") + @GetMapping("/preview/{tableId}") + public Result preview(@PathVariable("tableId") Long tableId) throws IOException + { + Map dataMap = genTableService.previewCode(tableId); + return success(dataMap); + } + + /** + * 生成代码(下载方式) + */ + @RequiresPermissions("tool:gen:code") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/download/{tableName}") + public void download(@PathVariable("tableName") String tableName) throws IOException + { + byte[] data = genTableService.downloadCode(tableName); + genCode(response, data); + } + + /** + * 生成代码(自定义路径) + */ + @RequiresPermissions("tool:gen:code") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/genCode/{tableName}") + public Result genCode(@PathVariable("tableName") String tableName) + { + genTableService.generatorCode(tableName); + return success(); + } + + /** + * 同步数据库 + */ + @RequiresPermissions("tool:gen:edit") + @Log(title = "代码生成", businessType = BusinessType.UPDATE) + @GetMapping("/synchDb/{tableName}/{dbName}") + public Result synchDb(@PathVariable("tableName") String tableName,@PathVariable("dbName") String dbName) + { + genTableService.synchDb(tableName,dbName); + return success(); + } + + /** + * 批量生成代码 + */ + @RequiresPermissions("tool:gen:code") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/batchGenCode") + public void batchGenCode(@RequestParam("tables") 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.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()); + } + + /** + * 查询所有数据库名称 + */ + @GetMapping("/db/selDbNameAll") + public Result> selDbNameAll(){ + return Result.success(genTableService.selDbNameAll()); + } + + /** + * 查询所有表,不分数据库 + */ + @GetMapping("/db/listAll") + public Result> genListAll() { + return success(genTableService.selectDbTableListAll()); + } + + /** + * 根据数据库名称与表名称查询表字段 + */ + @GetMapping("/selectDbTableColumnsByName") + public Result> selTableAll(@RequestParam("dbName") String dbName,@RequestParam("table") String table){ + List genTableColumns = genTableColumnService.selectDbTableColumnsByName(table, dbName); + return Result.success(genTableColumns); + } +} diff --git a/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/domain/GenTable.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/domain/GenTable.java new file mode 100644 index 0000000..c906725 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/domain/GenTable.java @@ -0,0 +1,378 @@ +package com.muyu.gen.domain; + +import com.muyu.common.core.constant.GenConstants; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.web.domain.BaseEntity; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; +import org.apache.commons.lang3.ArrayUtils; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import java.util.List; + + + + +/** + * 业务表 gen_table + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class GenTable extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 编号 + */ + private Long tableId; + + private String dbName; + + /** + * 表名称 + */ + @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 columns; + + /** + * 其它生成选项 + */ + private String options; + + /** + * 树编码字段 + */ + private String treeCode; + + /** + * 树父编码字段 + */ + private String treeParentCode; + + /** + * 树名称字段 + */ + private String treeName; + + /** + * 上级菜单ID字段 + */ + private String parentMenuId; + + /** + * 上级菜单名称字段 + */ + private String parentMenuName; + + public static boolean isSub (String tplCategory) { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_SUB, tplCategory); + } + + public static boolean isTree (String tplCategory) { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_TREE, tplCategory); + } + + public static boolean isCrud (String tplCategory) { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_CRUD, tplCategory); + } + + 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); + } + + 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 getColumns () { + return columns; + } + + public void setColumns (List 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 boolean isTree () { + return isTree(this.tplCategory); + } + + public boolean isCrud () { + return isCrud(this.tplCategory); + } + + public boolean isSuperColumn (String javaField) { + return isSuperColumn(this.tplCategory, javaField); + } +} diff --git a/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/domain/GenTableColumn.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/domain/GenTableColumn.java new file mode 100644 index 0000000..98e4c28 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/domain/GenTableColumn.java @@ -0,0 +1,358 @@ +package com.muyu.gen.domain; + +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.web.domain.BaseEntity; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import jakarta.validation.constraints.NotBlank; + +/** + * 代码生成业务字段表 gen_table_column + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +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 static boolean isSuperColumn (String javaField) { + return StringUtils.equalsAnyIgnoreCase(javaField, + // BaseEntity + "createBy", "createTime", "updateBy", "updateTime", "remark", + // TreeEntity + "parentName", "parentId", "orderNum", "ancestors"); + } + + public static boolean isUsableColumn (String javaField) { + // isSuperColumn()中的名单用于避免生成多余Domain属性,若某些属性在生成页面时需要用到不能忽略,则放在此处白名单 + return StringUtils.equalsAnyIgnoreCase(javaField, "parentId", "orderNum", "remark"); + } + + public Long getColumnId () { + return columnId; + } + + public void setColumnId (Long columnId) { + this.columnId = columnId; + } + + public Long getTableId () { + return tableId; + } + + public void setTableId (Long tableId) { + this.tableId = tableId; + } + + public String getColumnName () { + return columnName; + } + + public void setColumnName (String columnName) { + this.columnName = columnName; + } + + public String getColumnComment () { + return columnComment; + } + + public void setColumnComment (String columnComment) { + this.columnComment = columnComment; + } + + public String getColumnType () { + return columnType; + } + + public void setColumnType (String columnType) { + this.columnType = columnType; + } + + public String getJavaType () { + return javaType; + } + + public void setJavaType (String javaType) { + this.javaType = javaType; + } + + public String getJavaField () { + return javaField; + } + + public void setJavaField (String javaField) { + this.javaField = javaField; + } + + public String getCapJavaField () { + return StringUtils.capitalize(javaField); + } + + public String getIsPk () { + return isPk; + } + + public void setIsPk (String isPk) { + this.isPk = 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 String getIsRequired () { + return isRequired; + } + + public void setIsRequired (String isRequired) { + this.isRequired = isRequired; + } + + public boolean isRequired () { + return isRequired(this.isRequired); + } + + public boolean isRequired (String isRequired) { + return isRequired != null && StringUtils.equals("1", isRequired); + } + + public String getIsInsert () { + return isInsert; + } + + public void setIsInsert (String isInsert) { + this.isInsert = isInsert; + } + + public boolean isInsert () { + return isInsert(this.isInsert); + } + + public boolean isInsert (String isInsert) { + return isInsert != null && StringUtils.equals("1", isInsert); + } + + public String getIsEdit () { + return isEdit; + } + + public void setIsEdit (String isEdit) { + this.isEdit = isEdit; + } + + public boolean isEdit () { + return isInsert(this.isEdit); + } + + public boolean isEdit (String isEdit) { + return isEdit != null && StringUtils.equals("1", isEdit); + } + + public String getIsList () { + return isList; + } + + public void setIsList (String isList) { + this.isList = isList; + } + + public boolean isList () { + return isList(this.isList); + } + + public boolean isList (String isList) { + return isList != null && StringUtils.equals("1", isList); + } + + public String getIsQuery () { + return isQuery; + } + + public void setIsQuery (String isQuery) { + this.isQuery = isQuery; + } + + public boolean isQuery () { + return isQuery(this.isQuery); + } + + public boolean isQuery (String isQuery) { + return isQuery != null && StringUtils.equals("1", isQuery); + } + + public String getQueryType () { + return queryType; + } + + public void setQueryType (String queryType) { + this.queryType = queryType; + } + + public String getHtmlType () { + return htmlType; + } + + public void setHtmlType (String htmlType) { + this.htmlType = htmlType; + } + + public String getDictType () { + return dictType; + } + + public void setDictType (String dictType) { + this.dictType = dictType; + } + + public Integer getSort () { + return sort; + } + + public void setSort (Integer sort) { + this.sort = sort; + } + + public boolean isSuperColumn () { + return isSuperColumn(this.javaField); + } + + public boolean isUsableColumn () { + return isUsableColumn(javaField); + } + + 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(startStr).append("=").append(endStr).append(","); + } + } + return sb.deleteCharAt(sb.length() - 1).toString(); + } else { + return this.columnComment; + } + } +} diff --git a/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/domain/GenTableResp.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/domain/GenTableResp.java new file mode 100644 index 0000000..70b30dc --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/domain/GenTableResp.java @@ -0,0 +1,43 @@ +package com.muyu.gen.domain; + +import com.muyu.common.core.constant.GenConstants; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.web.domain.BaseEntity; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; +import org.apache.commons.lang3.ArrayUtils; + +import java.util.List; + + +/** + * 业务表 gen_table + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class GenTableResp extends BaseEntity { + + private String dbName; + + /** + * 表名称 + */ + private String tableName; + + /** + * 表描述 + */ + private String tableComment; + + +} diff --git a/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/mapper/GenTableColumnMapper.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/mapper/GenTableColumnMapper.java new file mode 100644 index 0000000..6df5e42 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/mapper/GenTableColumnMapper.java @@ -0,0 +1,64 @@ +package com.muyu.gen.mapper; + +import com.muyu.gen.domain.GenTableColumn; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + + +/** + * 业务字段 数据层 + * + * @author ruoyi + */ +public interface GenTableColumnMapper +{ + /** + * 根据表名称查询列信息 + * + * @param tableName 表名称 + * @param dbName + * @return 列信息 + */ + List selectDbTableColumnsByName(@Param("tableName") String tableName, @Param("dbName") String dbName); + + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + List selectGenTableColumnListByTableId(@Param("tableId") Long tableId); + + /** + * 新增业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + int insertGenTableColumn(GenTableColumn genTableColumn); + + /** + * 修改业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + int updateGenTableColumn(GenTableColumn genTableColumn); + + /** + * 删除业务字段 + * + * @param genTableColumns 列数据 + * @return 结果 + */ + int deleteGenTableColumns(@Param("genTableColumns") List genTableColumns); + + /** + * 批量删除业务字段 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + int deleteGenTableColumnByIds(@Param("ids") Long[] ids); +} diff --git a/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/mapper/GenTableMapper.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/mapper/GenTableMapper.java new file mode 100644 index 0000000..8f81757 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/mapper/GenTableMapper.java @@ -0,0 +1,91 @@ +package com.muyu.gen.mapper; + +import com.muyu.gen.domain.GenTable; +import com.muyu.gen.domain.GenTableResp; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 业务 数据层 + * + * @author ruoyi + */ +public interface GenTableMapper +{ + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * @return 业务集合 + */ + List selectGenTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * @return 数据库表集合 + */ + List selectDbTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @param dbName + * @return 数据库表集合 + */ + List selectDbTableListByNames(@Param("tableNames") String[] tableNames, @Param("dbName") String dbName); + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + List selectGenTableAll(); + + /** + * 查询表ID业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + GenTable selectGenTableById(@Param("id") Long id); + + /** + * 查询表名称业务信息 + * + * @param tableName 表名称 + * @return 业务信息 + */ + GenTable selectGenTableByName(@Param("tableName") String tableName); + + /** + * 新增业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + int insertGenTable(GenTable genTable); + + /** + * 修改业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + int updateGenTable(GenTable genTable); + + /** + * 批量删除业务 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + int deleteGenTableByIds(@Param("ids") Long[] ids); + + List selDbNameAll(); + + List selectDbTableListAll(); +} diff --git a/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/service/GenTableColumnServiceImpl.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/service/GenTableColumnServiceImpl.java new file mode 100644 index 0000000..043c83b --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/service/GenTableColumnServiceImpl.java @@ -0,0 +1,74 @@ +package com.muyu.gen.service; + +import com.muyu.common.core.text.Convert; +import com.muyu.gen.domain.GenTableColumn; +import com.muyu.gen.mapper.GenTableColumnMapper; + import javax.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 业务字段 服务层实现 + * + * @author ruoyi + */ +@Service +public class GenTableColumnServiceImpl implements IGenTableColumnService +{ + @Resource + private GenTableColumnMapper genTableColumnMapper; + + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + @Override + public List 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)); + } + + @Override + public List selectDbTableColumnsByName(String table, String dbName) { + return genTableColumnMapper.selectDbTableColumnsByName(table,dbName); + } +} diff --git a/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/service/GenTableServiceImpl.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/service/GenTableServiceImpl.java new file mode 100644 index 0000000..8cdefc5 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/service/GenTableServiceImpl.java @@ -0,0 +1,537 @@ +package com.muyu.gen.service; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.muyu.common.core.constant.Constants; +import com.muyu.common.core.constant.GenConstants; +import com.muyu.common.core.exception.ServiceException; +import com.muyu.common.core.text.CharsetKit; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.gen.domain.GenTable; +import com.muyu.gen.domain.GenTableColumn; +import com.muyu.gen.domain.GenTableResp; +import com.muyu.gen.mapper.GenTableColumnMapper; +import com.muyu.gen.mapper.GenTableMapper; +import com.muyu.gen.util.GenUtils; +import com.muyu.gen.util.VelocityInitializer; +import com.muyu.gen.util.VelocityUtils; +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 javax.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +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; + +/** + * 业务 服务层实现 + * + * @author ruoyi + */ +@Service +public class GenTableServiceImpl implements IGenTableService +{ + private static final Logger log = LoggerFactory.getLogger(GenTableServiceImpl.class); + + @Resource + private GenTableMapper genTableMapper; + + @Resource + 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 selectGenTableList(GenTable genTable) + { + return genTableMapper.selectGenTableList(genTable); + } + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * @return 数据库表集合 + */ + @Override + public List selectDbTableList(GenTable genTable) + { + return genTableMapper.selectDbTableList(genTable); + } + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @param dbName + * @return 数据库表集合 + */ + @Override + public List selectDbTableListByNames(String[] tableNames, String dbName) + { + return genTableMapper.selectDbTableListByNames(tableNames,dbName); + } + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + @Override + public List selectGenTableAll() + { + return genTableMapper.selectGenTableAll(); + } + + /** + * 修改业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + 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(rollbackFor = Exception.class) + public void deleteGenTableByIds(Long[] tableIds) + { + genTableMapper.deleteGenTableByIds(tableIds); + genTableColumnMapper.deleteGenTableColumnByIds(tableIds); + } + + /** + * 导入表结构 + * + * @param tableList 导入表列表 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void importGenTable(List tableList) + { + String operName = SecurityUtils.getUsername(); + try + { + for (GenTable table : tableList) + { + String dbName = table.getDbName(); + String tableName = table.getTableName(); + GenUtils.initTable(table, operName); + int row = genTableMapper.insertGenTable(table); + if (row > 0) + { + // 保存列信息 + List genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName, dbName); + 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 previewCode(Long tableId) + { + Map dataMap = new LinkedHashMap<>(); + // 查询表信息 + GenTable table = genTableMapper.selectGenTableById(tableId); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List 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 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 表名称 + * @param dbName + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void synchDb(String tableName, String dbName) + { + GenTable table = genTableMapper.selectGenTableByName(tableName); + List tableColumns = table.getColumns(); + Map tableColumnMap = tableColumns.stream().collect(Collectors.toMap(GenTableColumn::getColumnName, Function.identity())); + + List dbTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName, dbName); + if (StringUtils.isEmpty(dbTableColumns)) + { + throw new ServiceException("同步数据失败,原表结构不存在"); + } + List 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 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 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("子表关联的外键名不能为空"); + } + } + } + } + + @Override + public List selDbNameAll() { + return genTableMapper.selDbNameAll(); + } + + @Override + public List selectDbTableListAll() { + return genTableMapper.selectDbTableListAll(); + } + + + /** + * 设置主键列信息 + * + * @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); + } +} diff --git a/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/service/IGenTableColumnService.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/service/IGenTableColumnService.java new file mode 100644 index 0000000..06c3994 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/service/IGenTableColumnService.java @@ -0,0 +1,47 @@ +package com.muyu.gen.service; + +import com.muyu.gen.domain.GenTableColumn; + +import java.util.List; + +/** + * 业务字段 服务层 + * + * @author ruoyi + */ +public interface IGenTableColumnService +{ + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + List selectGenTableColumnListByTableId(Long tableId); + + /** + * 新增业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + int insertGenTableColumn(GenTableColumn genTableColumn); + + /** + * 修改业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + int updateGenTableColumn(GenTableColumn genTableColumn); + + /** + * 删除业务字段信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + int deleteGenTableColumnByIds(String ids); + + List selectDbTableColumnsByName(String table, String dbName); +} diff --git a/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/service/IGenTableService.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/service/IGenTableService.java new file mode 100644 index 0000000..57e7d89 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/service/IGenTableService.java @@ -0,0 +1,129 @@ +package com.muyu.gen.service; + +import com.muyu.gen.domain.GenTable; +import com.muyu.gen.domain.GenTableResp; + +import java.util.List; +import java.util.Map; + +/** + * 业务 服务层 + * + * @author ruoyi + */ +public interface IGenTableService +{ + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * @return 业务集合 + */ + List selectGenTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * @return 数据库表集合 + */ + List selectDbTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @param dbName + * @return 数据库表集合 + */ + List selectDbTableListByNames(String[] tableNames, String dbName); + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + List selectGenTableAll(); + + /** + * 查询业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + GenTable selectGenTableById(Long id); + + /** + * 修改业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + void updateGenTable(GenTable genTable); + + /** + * 删除业务信息 + * + * @param tableIds 需要删除的表数据ID + * @return 结果 + */ + void deleteGenTableByIds(Long[] tableIds); + + /** + * 导入表结构 + * + * @param tableList 导入表列表 + */ + void importGenTable(List tableList); + + /** + * 预览代码 + * + * @param tableId 表编号 + * @return 预览数据列表 + */ + Map previewCode(Long tableId); + + /** + * 生成代码(下载方式) + * + * @param tableName 表名称 + * @return 数据 + */ + byte[] downloadCode(String tableName); + + /** + * 生成代码(自定义路径) + * + * @param tableName 表名称 + * @return 数据 + */ + void generatorCode(String tableName); + + /** + * 同步数据库 + * + * @param tableName 表名称 + * @param dbName + */ + void synchDb(String tableName, String dbName); + + /** + * 批量生成代码(下载方式) + * + * @param tableNames 表数组 + * @return 数据 + */ + byte[] downloadCode(String[] tableNames); + + /** + * 修改保存参数校验 + * + * @param genTable 业务信息 + */ + void validateEdit(GenTable genTable); + + List selDbNameAll(); + + List selectDbTableListAll(); +} diff --git a/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/util/GenUtils.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/util/GenUtils.java new file mode 100644 index 0000000..5aea1f2 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/util/GenUtils.java @@ -0,0 +1,229 @@ +package com.muyu.gen.util; + +import com.muyu.common.core.constant.GenConstants; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.gen.config.GenConfig; +import com.muyu.gen.domain.GenTable; +import com.muyu.gen.domain.GenTableColumn; +import org.apache.commons.lang3.RegExUtils; + +import java.util.Arrays; + +/** + * 代码生成器 工具类 + * + * @author muyu + */ +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/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/util/VelocityInitializer.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/util/VelocityInitializer.java new file mode 100644 index 0000000..cc171b4 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/util/VelocityInitializer.java @@ -0,0 +1,30 @@ +package com.muyu.gen.util; + +import com.muyu.common.core.constant.Constants; +import org.apache.velocity.app.Velocity; + +import java.util.Properties; + +/** + * VelocityEngine工厂 + * + * @author muyu + */ +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/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/util/VelocityUtils.java b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/util/VelocityUtils.java new file mode 100644 index 0000000..9305ff7 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/java/com/muyu/gen/util/VelocityUtils.java @@ -0,0 +1,357 @@ +package com.muyu.gen.util; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.muyu.common.core.constant.GenConstants; +import com.muyu.common.core.utils.DateUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.gen.domain.GenTable; +import com.muyu.gen.domain.GenTableColumn; +import org.apache.velocity.VelocityContext; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * 模板工具类 + * + * @author muyu + */ +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 getTemplateList (String tplCategory) { + List templates = new ArrayList(); + 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 getImportList (GenTable genTable) { + List columns = genTable.getColumns(); + GenTable subGenTable = genTable.getSubTable(); + HashSet importList = new HashSet(); + 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 columns = genTable.getColumns(); + Set dicts = new HashSet(); + addDicts(dicts, columns); + if (StringUtils.isNotNull(genTable.getSubTable())) { + List subColumns = genTable.getSubTable().getColumns(); + addDicts(dicts, subColumns); + } + return StringUtils.join(dicts, ", "); + } + + /** + * 添加字典列表 + * + * @param dicts 字典列表 + * @param columns 列集合 + */ + public static void addDicts (Set dicts, List 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/cloud-modules/cloud-modules-gen/src/main/resources/banner.txt b/cloud-modules/cloud-modules-gen/src/main/resources/banner.txt new file mode 100644 index 0000000..0dd5eee --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/banner.txt @@ -0,0 +1,2 @@ +Spring Boot Version: ${spring-boot.version} +Spring Application Name: ${spring.application.name} diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/logback/dev.xml b/cloud-modules/cloud-modules-gen/src/main/resources/logback/dev.xml new file mode 100644 index 0000000..0b3964c --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/logback/dev.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + + + diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/logback/prod.xml b/cloud-modules/cloud-modules-gen/src/main/resources/logback/prod.xml new file mode 100644 index 0000000..4a9d602 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/logback/prod.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + ${log.sky.pattern} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/logback/test.xml b/cloud-modules/cloud-modules-gen/src/main/resources/logback/test.xml new file mode 100644 index 0000000..4a9d602 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/logback/test.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + ${log.sky.pattern} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/mapper/generator/GenTableColumnMapper.xml b/cloud-modules/cloud-modules-gen/src/main/resources/mapper/generator/GenTableColumnMapper.xml new file mode 100644 index 0000000..ea0616b --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/mapper/generator/GenTableColumnMapper.xml @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + insert into gen_table_column ( + 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 + )values( + #{tableId}, + #{columnName}, + #{columnComment}, + #{columnType}, + #{javaType}, + #{javaField}, + #{isPk}, + #{isIncrement}, + #{isRequired}, + #{isInsert}, + #{isEdit}, + #{isList}, + #{isQuery}, + #{queryType}, + #{htmlType}, + #{dictType}, + #{sort}, + #{createBy}, + sysdate() + ) + + + + update gen_table_column + + column_comment = #{columnComment}, + java_type = #{javaType}, + java_field = #{javaField}, + is_insert = #{isInsert}, + is_edit = #{isEdit}, + is_list = #{isList}, + is_query = #{isQuery}, + is_required = #{isRequired}, + query_type = #{queryType}, + html_type = #{htmlType}, + dict_type = #{dictType}, + sort = #{sort}, + update_by = #{updateBy}, + update_time = sysdate() + + where column_id = #{columnId} + + + + delete from gen_table_column where table_id in ( + + #{tableId} + + ) + + + + + delete from gen_table_column where column_id in ( + + #{item.columnId} + + ) + + + diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/mapper/generator/GenTableMapper.xml b/cloud-modules/cloud-modules-gen/src/main/resources/mapper/generator/GenTableMapper.xml new file mode 100644 index 0000000..ba9b0cf --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/mapper/generator/GenTableMapper.xml @@ -0,0 +1,234 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select table_id, db_name, 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 + + + + + + table_schema in (#{dbName}) + + + table_schema = (select database()) + + + + + + + + + + + + + + + + + + + + + + + insert into gen_table ( + db_name, + table_name, + table_comment, + class_name, + tpl_category, + package_name, + module_name, + business_name, + function_name, + function_author, + gen_type, + gen_path, + remark, + create_by, + create_time + )values( + #{dbName}, + #{tableName}, + #{tableComment}, + #{className}, + #{tplCategory}, + #{packageName}, + #{moduleName}, + #{businessName}, + #{functionName}, + #{functionAuthor}, + #{genType}, + #{genPath}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + update gen_table + + db_name = #{dbName}, + table_name = #{tableName}, + table_comment = #{tableComment}, + sub_table_name = #{subTableName}, + sub_table_fk_name = #{subTableFkName}, + class_name = #{className}, + function_author = #{functionAuthor}, + gen_type = #{genType}, + gen_path = #{genPath}, + tpl_category = #{tplCategory}, package_name = #{packageName}, + module_name = #{moduleName}, + business_name = #{businessName}, + function_name = #{functionName}, + options = #{options}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = sysdate() + + where table_id = #{tableId} + + + + delete from gen_table where table_id in ( + + #{tableId} + + ) + + + diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/controller.java.vm b/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/controller.java.vm new file mode 100644 index 0000000..c62cc8b --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/controller.java.vm @@ -0,0 +1,123 @@ +package ${packageName}.controller; + +import java.util.Arrays; +import java.util.List; +import jakarta.servlet.http.HttpServletResponse; +import javax.annotation.Resource; +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.muyu.common.security.annotation.RequiresPermissions; +import ${packageName}.domain.${ClassName}; +import ${packageName}.service.I${ClassName}Service; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.utils.poi.ExcelUtil; +import com.muyu.common.security.utils.SecurityUtils; +import org.springframework.validation.annotation.Validated; +#if($table.crud || $table.sub) +import com.muyu.common.core.web.page.TableDataInfo; +#elseif($table.tree) +#end + +/** + * ${functionName}Controller + * + * @author ${author} + * @date ${datetime} + */ +@RestController +@RequestMapping("/${businessName}") +public class ${ClassName}Controller extends BaseController +{ + @Resource + private I${ClassName}Service ${className}Service; + + /** + * 查询${functionName}列表 + */ + @RequiresPermissions("${permissionPrefix}:list") + @GetMapping("/list") +#if($table.crud || $table.sub) + public Result> list(${ClassName} ${className}) + { + startPage(); + List<${ClassName}> list = ${className}Service.select${ClassName}List(${className}); + return getDataTable(list); + } +#elseif($table.tree) + public Result<${ClassName}> list(${ClassName} ${className}) + { + List<${ClassName}> list = ${className}Service.select${ClassName}List(${className}); + return success(list); + } +#end + + /** + * 导出${functionName}列表 + */ + @RequiresPermissions("${permissionPrefix}: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}详细信息 + */ + @RequiresPermissions("${permissionPrefix}:query") + @GetMapping(value = "/{${pkColumn.javaField}}") + public Result> getInfo(@PathVariable("${pkColumn.javaField}") ${pkColumn.javaType} ${pkColumn.javaField}) + { + return success(${className}Service.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField})); + } + + /** + * 新增${functionName} + */ + @RequiresPermissions("${permissionPrefix}:add") + @PostMapping + public Result add( + @Validated @RequestBody ${ClassName} ${className}) + { + if (${className}Service.checkIdUnique(${className})) { + return error("新增 ${functionName} '" + ${className} + "'失败,${functionName}已存在"); + } + ${className}.setCreateBy(SecurityUtils.getUsername()); + return toAjax(${className}Service.save(${className})); + } + + /** + * 修改${functionName} + */ + @RequiresPermissions("${permissionPrefix}:edit") + @PutMapping + public Result edit( + @Validated @RequestBody ${ClassName} ${className}) + { + if (!${className}Service.checkIdUnique(${className})) { + return error("修改 ${functionName} '" + ${className} + "'失败,${functionName}不存在"); + } + ${className}.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(${className}Service.updateById(${className})); + } + + /** + * 删除${functionName} + */ + @RequiresPermissions("${permissionPrefix}:remove") + @DeleteMapping("/{${pkColumn.javaField}s}") + public Result remove(@PathVariable("${pkColumn.javaField}s") ${pkColumn.javaType}[] ${pkColumn.javaField}s) + { + ${className}Service.removeBatchByIds(Arrays.asList(${pkColumn.javaField}s)); + return success(); + } +} diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/domain.java.vm b/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/domain.java.vm new file mode 100644 index 0000000..4c89918 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/domain.java.vm @@ -0,0 +1,103 @@ +package ${packageName}.domain; + +#foreach ($import in $importList) +import ${import}; +#end +import com.muyu.common.core.annotation.Excel; +#if($table.crud || $table.sub) +import com.muyu.common.core.web.domain.BaseEntity; +#elseif($table.tree) +import com.muyu.common.core.web.domain.TreeEntity; +#end +import lombok.*; +import lombok.experimental.SuperBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.annotation.IdType; + +/** + * ${functionName}对象 ${tableName} + * + * @author ${author} + * @date ${datetime} + */ + +#if($table.crud || $table.sub) +#set($Entity="BaseEntity") +#elseif($table.tree) +#set($Entity="TreeEntity") +#end +@Data +@Setter +@Getter +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@TableName("${tableName}") +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 + #if($column.javaField == $pkColumn.javaField) + @TableId( type = IdType.AUTO) + #end + private $column.javaType $column.javaField; + +#end +#end + +#if($table.sub) + /** $table.subTable.functionName信息 */ + private List<${subClassName}> ${subclassName}List; + +#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/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/mapper.java.vm b/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/mapper.java.vm new file mode 100644 index 0000000..e6f420b --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/mapper.java.vm @@ -0,0 +1,20 @@ +package ${packageName}.mapper; + +import java.util.List; +import ${packageName}.domain.${ClassName}; +#if($table.sub) +import ${packageName}.domain.${subClassName}; +#end +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * ${functionName}Mapper接口 + * + * @author ${author} + * @date ${datetime} + */ +@Mapper +public interface ${ClassName}Mapper extends BaseMapper<${ClassName}>{ + +} diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/service.java.vm b/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/service.java.vm new file mode 100644 index 0000000..16357f9 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/service.java.vm @@ -0,0 +1,37 @@ +package ${packageName}.service; + +import java.util.List; +import ${packageName}.domain.${ClassName}; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * ${functionName}Service接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface I${ClassName}Service extends IService<${ClassName}> { + /** + * 精确查询${functionName} + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return ${functionName} + */ + ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); + + /** + * 查询${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName}集合 + */ + List<${ClassName}> select${ClassName}List(${ClassName} ${className}); + + /** + * 判断 ${functionName} id是否唯一 + * @param ${className} ${functionName} + * @return 结果 + */ + Boolean checkIdUnique(${ClassName} ${className}); + +} diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/serviceImpl.java.vm b/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/serviceImpl.java.vm new file mode 100644 index 0000000..38f1eca --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/serviceImpl.java.vm @@ -0,0 +1,115 @@ +package ${packageName}.service.impl; + +import java.util.List; +#foreach ($column in $columns) +#if($column.javaField == 'createTime' || $column.javaField == 'updateTime') +import com.muyu.common.core.utils.DateUtils; +#break +#end +#end +import org.springframework.stereotype.Service; +#if($table.sub) +import java.util.ArrayList; +import com.muyu.common.core.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; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.muyu.common.core.utils.StringUtils; +import org.springframework.util.Assert; + +/** + * ${functionName}Service业务层处理 + * + * @author ${author} + * @date ${datetime} + */ +@Service +public class ${ClassName}ServiceImpl + extends ServiceImpl<${ClassName}Mapper, ${ClassName}> + implements I${ClassName}Service { + + /** + * 精确查询${functionName} + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return ${functionName} + */ + @Override + public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}) + { + LambdaQueryWrapper<${ClassName}> queryWrapper = new LambdaQueryWrapper<>(); + Assert.notNull(${pkColumn.javaField}, "${pkColumn.javaField}不可为空"); + queryWrapper.eq(${ClassName}::get${pkColumn.capJavaField}, ${pkColumn.javaField}); + return this.getOne(queryWrapper); + } + + + /** + * 查询${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName} + */ + @Override + public List<${ClassName}> select${ClassName}List(${ClassName} ${className}) + { + LambdaQueryWrapper<${ClassName}> queryWrapper = new LambdaQueryWrapper<>(); +#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 (StringUtils.isNotEmpty(${className}.get${AttrName}())){ + queryWrapper.eq(${ClassName}::get${AttrName}, ${className}.get${AttrName}()); + } + #elseif($queryType == "NE") + if (StringUtils.isNotEmpty(${className}.get${AttrName}())){ + queryWrapper.ne(${ClassName}::get${AttrName}, ${className}.get${AttrName}()); + } + #elseif($queryType == "GT") + if (StringUtils.isNotEmpty(${className}.get${AttrName}())){ + queryWrapper.gt(${ClassName}::get${AttrName}, ${className}.get${AttrName}()); + } + #elseif($queryType == "GTE") + if (StringUtils.isNotEmpty(${className}.get${AttrName}())){ + queryWrapper.ge(${ClassName}::get${AttrName}, ${className}.get${AttrName}()); + } + #elseif($queryType == "LT") + if (StringUtils.isNotEmpty(${className}.get${AttrName}())){ + queryWrapper.lt(${ClassName}::get${AttrName}, ${className}.get${AttrName}()); + } + #elseif($queryType == "LTE") + if (StringUtils.isNotEmpty(${className}.get${AttrName}())){ + queryWrapper.le(${ClassName}::get${AttrName}, ${className}.get${AttrName}()); + } + #elseif($queryType == "LIKE") + if (StringUtils.isNotEmpty(${className}.get${AttrName}())){ + queryWrapper.like(${ClassName}::get${AttrName}, ${className}.get${AttrName}()); + } + #end + #end +#end + return this.list(queryWrapper); + } + + /** + * 唯一 判断 + * @param ${className} ${functionName} + * @return ${functionName} + */ + @Override + public Boolean checkIdUnique(${ClassName} ${className}) { + LambdaQueryWrapper<${ClassName}> queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(${ClassName}::get${pkColumn.capJavaField}, ${className}.get${pkColumn.capJavaField}()); + return this.count(queryWrapper) > 0; + } + +} diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/sub-domain.java.vm b/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/sub-domain.java.vm new file mode 100644 index 0000000..61c6273 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/vm/java/sub-domain.java.vm @@ -0,0 +1,78 @@ +package ${packageName}.domain; + +#foreach ($import in $subImportList) +import ${import}; +#end +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.core.web.domain.BaseEntity; + +/** + * ${subTable.functionName}对象 ${subTableName} + * + * @author ${author} + * @date ${datetime} + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +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/cloud-modules/cloud-modules-gen/src/main/resources/vm/js/api.js.vm b/cloud-modules/cloud-modules-gen/src/main/resources/vm/js/api.js.vm new file mode 100644 index 0000000..9295524 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/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/cloud-modules/cloud-modules-gen/src/main/resources/vm/sql/sql.vm b/cloud-modules/cloud-modules-gen/src/main/resources/vm/sql/sql.vm new file mode 100644 index 0000000..0575583 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/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/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/index-tree.vue.vm b/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/index-tree.vue.vm new file mode 100644 index 0000000..ec7de58 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/index-tree.vue.vm @@ -0,0 +1,505 @@ + + + diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/index.vue.vm b/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/index.vue.vm new file mode 100644 index 0000000..381d342 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/index.vue.vm @@ -0,0 +1,602 @@ + + + diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/v3/index-tree.vue.vm b/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/v3/index-tree.vue.vm new file mode 100644 index 0000000..4e73097 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/v3/index-tree.vue.vm @@ -0,0 +1,474 @@ + + + diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/v3/index.vue.vm b/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/v3/index.vue.vm new file mode 100644 index 0000000..7396c08 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/v3/index.vue.vm @@ -0,0 +1,590 @@ + + + diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/v3/readme.txt b/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/v3/readme.txt new file mode 100644 index 0000000..9f60284 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/vm/vue/v3/readme.txt @@ -0,0 +1 @@ +���ʹ�õ���cloud-Cloud-Vue3ǰ�ˣ���ô��Ҫ����һ�´�Ŀ¼��ģ��index.vue.vm��index-tree.vue.vm�ļ����ϼ�vueĿ¼�� diff --git a/cloud-modules/cloud-modules-gen/src/main/resources/vm/xml/mapper.xml.vm b/cloud-modules/cloud-modules-gen/src/main/resources/vm/xml/mapper.xml.vm new file mode 100644 index 0000000..0ceb3d8 --- /dev/null +++ b/cloud-modules/cloud-modules-gen/src/main/resources/vm/xml/mapper.xml.vm @@ -0,0 +1,135 @@ + + + + + +#foreach ($column in $columns) + +#end + +#if($table.sub) + + + + + + +#foreach ($column in $subTable.columns) + +#end + +#end + + + select#foreach($column in $columns) $column.columnName#if($foreach.count != $columns.size()),#end#end from ${tableName} + + + + + + + + insert into ${tableName} + +#foreach($column in $columns) +#if($column.columnName != $pkColumn.columnName || !$pkColumn.increment) + $column.columnName, +#end +#end + + +#foreach($column in $columns) +#if($column.columnName != $pkColumn.columnName || !$pkColumn.increment) + #{$column.javaField}, +#end +#end + + + + + update ${tableName} + +#foreach($column in $columns) +#if($column.columnName != $pkColumn.columnName) + $column.columnName = #{$column.javaField}, +#end +#end + + where ${pkColumn.columnName} = #{${pkColumn.javaField}} + + + + delete from ${tableName} where ${pkColumn.columnName} = #{${pkColumn.javaField}} + + + + delete from ${tableName} where ${pkColumn.columnName} in + + #{${pkColumn.javaField}} + + +#if($table.sub) + + + delete from ${subTableName} where ${subTableFkName} in + + #{${subTableFkclassName}} + + + + + delete from ${subTableName} where ${subTableFkName} = #{${subTableFkclassName}} + + + + insert into ${subTableName}(#foreach($column in $subTable.columns) $column.columnName#if($foreach.count != $subTable.columns.size()),#end#end) values + + (#foreach($column in $subTable.columns) #{item.$column.javaField}#if($foreach.count != $subTable.columns.size()),#end#end) + + +#end + \ No newline at end of file diff --git a/cloud-modules/cloud-modules-system/pom.xml b/cloud-modules/cloud-modules-system/pom.xml new file mode 100644 index 0000000..4d83d4d --- /dev/null +++ b/cloud-modules/cloud-modules-system/pom.xml @@ -0,0 +1,100 @@ + + + + com.muyu + cloud-modules + 3.6.3 + + 4.0.0 + + cloud-modules-system + + + cloud-modules-system系统模块 + + + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + com.mysql + mysql-connector-j + + + + + com.muyu + cloud-common-datasource + + + + + com.muyu + cloud-common-datascope + + + + + com.muyu + cloud-common-log + + + + + com.muyu + cloud-common-api-doc + + + + + com.muyu + cloud-common-xxl + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + + diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/CloudSystemApplication.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/CloudSystemApplication.java new file mode 100644 index 0000000..71f78fc --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/CloudSystemApplication.java @@ -0,0 +1,23 @@ +package com.muyu.system; + +import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration; +import com.muyu.common.security.annotation.EnableCustomConfig; +import com.muyu.common.security.annotation.EnableMyFeignClients; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; + +/** + * 系统模块 + * + * @author muyu + */ +@EnableCustomConfig +//@EnableCustomSwagger2 +@EnableMyFeignClients +@SpringBootApplication +public class CloudSystemApplication { + public static void main (String[] args) { + SpringApplication.run(CloudSystemApplication.class, args); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysConfigController.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysConfigController.java new file mode 100644 index 0000000..3bced52 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysConfigController.java @@ -0,0 +1,122 @@ +package com.muyu.system.controller; + +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.utils.poi.ExcelUtil; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.web.page.TableDataInfo; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.system.domain.SysConfig; +import com.muyu.system.service.SysConfigService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + +/** + * 参数配置 信息操作处理 + * + * @author muyu + */ +@RestController +@RequestMapping("/config") +@Tag(name = "SysConfigController", description = "系统参数配置") +public class SysConfigController extends BaseController { + + @Autowired + private SysConfigService configService; + + /** + * 获取参数配置列表 + */ + @RequiresPermissions("system:config:list") + @GetMapping("/list") + @Operation(summary = "查询集合", description = "更新水果信息") + public Result> list (SysConfig config) { + startPage(); + List list = configService.pageQuery(config); + return getDataTable(list); + } + + @Log(title = "参数管理", businessType = BusinessType.EXPORT) + @RequiresPermissions("system:config:export") + @PostMapping("/export") + public void export (HttpServletResponse response, SysConfig config) { + List list = configService.pageQuery(config); + ExcelUtil util = new ExcelUtil(SysConfig.class); + util.exportExcel(response, list, "参数数据"); + } + + /** + * 根据参数编号获取详细信息 + */ + @GetMapping(value = "/{configId}") + public Result getInfo (@PathVariable("configId") Long configId) { + return success(configService.getById(configId)); + } + + /** + * 根据参数键名查询参数值 + */ + @GetMapping(value = "/configKey/{configKey}") + public Result getConfigKey (@PathVariable("configKey") String configKey) { + return success(configService.selectConfigByKey(configKey)); + } + + /** + * 新增参数配置 + */ + @RequiresPermissions("system:config:add") + @Log(title = "参数管理", businessType = BusinessType.INSERT) + @PostMapping + public Result add (@Validated @RequestBody SysConfig config) { + if (!configService.checkConfigKeyUnique(config)) { + return error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + config.setCreateBy(SecurityUtils.getUsername()); + return toAjax(configService.save(config)); + } + + /** + * 修改参数配置 + */ + @RequiresPermissions("system:config:edit") + @Log(title = "参数管理", businessType = BusinessType.UPDATE) + @PutMapping + public Result edit (@Validated @RequestBody SysConfig config) { + if (!configService.checkConfigKeyUnique(config)) { + return error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + config.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(configService.updateById(config)); + } + + /** + * 删除参数配置 + */ + @RequiresPermissions("system:config:remove") + @Log(title = "参数管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{configIds}") + public Result remove (@PathVariable("configIds") Long[] configIds) { + configService.removeBatchByIds(Arrays.asList(configIds)); + return success(); + } + + /** + * 刷新参数缓存 + */ + @RequiresPermissions("system:config:remove") + @Log(title = "参数管理", businessType = BusinessType.CLEAN) + @DeleteMapping("/refreshCache") + public Result refreshCache () { + configService.resetConfigCache(); + return success(); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysDeptController.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysDeptController.java new file mode 100644 index 0000000..37e48fc --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysDeptController.java @@ -0,0 +1,113 @@ +package com.muyu.system.controller; + +import com.muyu.common.core.constant.UserConstants; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.domain.Result; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.SysDept; +import com.muyu.system.service.SysDeptService; +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 部门信息 + * + * @author muyu + */ +@RestController +@RequestMapping("/dept") +public class SysDeptController extends BaseController { + + @Autowired + private SysDeptService deptService; + + /** + * 获取部门列表 + */ + @RequiresPermissions("system:dept:list") + @GetMapping("/list") + public Result list (SysDept dept) { + List depts = deptService.selectDeptList(dept); + return success(depts); + } + + /** + * 查询部门列表(排除节点) + */ + @RequiresPermissions("system:dept:list") + @GetMapping("/list/exclude/{deptId}") + public Result excludeChild (@PathVariable(value = "deptId", required = false) Long deptId) { + List depts = deptService.selectDeptList(new SysDept()); + depts.removeIf(d -> d.getDeptId().intValue() == deptId || ArrayUtils.contains(StringUtils.split(d.getAncestors(), ","), deptId + "")); + return success(depts); + } + + /** + * 根据部门编号获取详细信息 + */ + @RequiresPermissions("system:dept:query") + @GetMapping(value = "/{deptId}") + public Result getInfo (@PathVariable("deptId") Long deptId) { + deptService.checkDeptDataScope(deptId); + return success(deptService.selectDeptById(deptId)); + } + + /** + * 新增部门 + */ + @RequiresPermissions("system:dept:add") + @Log(title = "部门管理", businessType = BusinessType.INSERT) + @PostMapping + public Result add (@Validated @RequestBody SysDept dept) { + if (!deptService.checkDeptNameUnique(dept)) { + return error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } + dept.setCreateBy(SecurityUtils.getUsername()); + return toAjax(deptService.insertDept(dept)); + } + + /** + * 修改部门 + */ + @RequiresPermissions("system:dept:edit") + @Log(title = "部门管理", businessType = BusinessType.UPDATE) + @PutMapping + public Result 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(SecurityUtils.getUsername()); + return toAjax(deptService.updateDept(dept)); + } + + /** + * 删除部门 + */ + @RequiresPermissions("system:dept:remove") + @Log(title = "部门管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{deptId}") + public Result remove (@PathVariable("deptId") Long deptId) { + if (deptService.hasChildByDeptId(deptId)) { + return warn("存在下级部门,不允许删除"); + } + if (deptService.checkDeptExistUser(deptId)) { + return warn("部门存在用户,不允许删除"); + } + deptService.checkDeptDataScope(deptId); + return toAjax(deptService.deleteDeptById(deptId)); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysDictDataController.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysDictDataController.java new file mode 100644 index 0000000..f9862d9 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysDictDataController.java @@ -0,0 +1,107 @@ +package com.muyu.system.controller; + +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.utils.poi.ExcelUtil; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.web.page.TableDataInfo; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.SysDictData; +import com.muyu.system.service.SysDictDataService; +import com.muyu.system.service.SysDictTypeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.List; + +/** + * 数据字典信息 + * + * @author muyu + */ +@RestController +@RequestMapping("/dict/data") +public class SysDictDataController extends BaseController { + @Autowired + private SysDictDataService dictDataService; + + @Autowired + private SysDictTypeService dictTypeService; + + @RequiresPermissions("system:dict:list") + @GetMapping("/list") + public Result> list (SysDictData dictData) { + startPage(); + List list = dictDataService.selectDictDataList(dictData); + return getDataTable(list); + } + + @Log(title = "字典数据", businessType = BusinessType.EXPORT) + @RequiresPermissions("system:dict:export") + @PostMapping("/export") + public void export (HttpServletResponse response, SysDictData dictData) { + List list = dictDataService.selectDictDataList(dictData); + ExcelUtil util = new ExcelUtil(SysDictData.class); + util.exportExcel(response, list, "字典数据"); + } + + /** + * 查询字典数据详细 + */ + @RequiresPermissions("system:dict:query") + @GetMapping(value = "/{dictCode}") + public Result getInfo (@PathVariable("dictCode") Long dictCode) { + return success(dictDataService.selectDictDataById(dictCode)); + } + + /** + * 根据字典类型查询字典数据信息 + */ + @GetMapping(value = "/type/{dictType}") + public Result dictType (@PathVariable("dictType") String dictType) { + List data = dictTypeService.selectDictDataByType(dictType); + if (StringUtils.isNull(data)) { + data = new ArrayList(); + } + return success(data); + } + + /** + * 新增字典类型 + */ + @RequiresPermissions("system:dict:add") + @Log(title = "字典数据", businessType = BusinessType.INSERT) + @PostMapping + public Result add (@Validated @RequestBody SysDictData dict) { + dict.setCreateBy(SecurityUtils.getUsername()); + return toAjax(dictDataService.insertDictData(dict)); + } + + /** + * 修改保存字典类型 + */ + @RequiresPermissions("system:dict:edit") + @Log(title = "字典数据", businessType = BusinessType.UPDATE) + @PutMapping + public Result edit (@Validated @RequestBody SysDictData dict) { + dict.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(dictDataService.updateDictData(dict)); + } + + /** + * 删除字典类型 + */ + @RequiresPermissions("system:dict:remove") + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictCodes}") + public Result remove (@PathVariable("dictCode") Long[] dictCodes) { + dictDataService.deleteDictDataByIds(dictCodes); + return success(); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysDictTypeController.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysDictTypeController.java new file mode 100644 index 0000000..c4a7b9f --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysDictTypeController.java @@ -0,0 +1,115 @@ +package com.muyu.system.controller; + +import com.muyu.common.core.utils.poi.ExcelUtil; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.web.page.TableDataInfo; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.SysDictType; +import com.muyu.system.service.SysDictTypeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 数据字典信息 + * + * @author muyu + */ +@RestController +@RequestMapping("/dict/type") +public class SysDictTypeController extends BaseController { + @Autowired + private SysDictTypeService dictTypeService; + + @RequiresPermissions("system:dict:list") + @GetMapping("/list") + public Result> list (SysDictType dictType) { + startPage(); + List list = dictTypeService.selectDictTypeList(dictType); + return getDataTable(list); + } + + @Log(title = "字典类型", businessType = BusinessType.EXPORT) + @RequiresPermissions("system:dict:export") + @PostMapping("/export") + public void export (HttpServletResponse response, SysDictType dictType) { + List list = dictTypeService.selectDictTypeList(dictType); + ExcelUtil util = new ExcelUtil(SysDictType.class); + util.exportExcel(response, list, "字典类型"); + } + + /** + * 查询字典类型详细 + */ + @RequiresPermissions("system:dict:query") + @GetMapping(value = "/{dictId}") + public Result getInfo (@PathVariable("dictId") Long dictId) { + return success(dictTypeService.selectDictTypeById(dictId)); + } + + /** + * 新增字典类型 + */ + @RequiresPermissions("system:dict:add") + @Log(title = "字典类型", businessType = BusinessType.INSERT) + @PostMapping + public Result add (@Validated @RequestBody SysDictType dict) { + if (!dictTypeService.checkDictTypeUnique(dict)) { + return error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dict.setCreateBy(SecurityUtils.getUsername()); + return toAjax(dictTypeService.insertDictType(dict)); + } + + /** + * 修改字典类型 + */ + @RequiresPermissions("system:dict:edit") + @Log(title = "字典类型", businessType = BusinessType.UPDATE) + @PutMapping + public Result edit (@Validated @RequestBody SysDictType dict) { + if (!dictTypeService.checkDictTypeUnique(dict)) { + return error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dict.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(dictTypeService.updateDictType(dict)); + } + + /** + * 删除字典类型 + */ + @RequiresPermissions("system:dict:remove") + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictIds}") + public Result remove (@PathVariable("dictIds") Long[] dictIds) { + dictTypeService.deleteDictTypeByIds(dictIds); + return success(); + } + + /** + * 刷新字典缓存 + */ + @RequiresPermissions("system:dict:remove") + @Log(title = "字典类型", businessType = BusinessType.CLEAN) + @DeleteMapping("/refreshCache") + public Result refreshCache () { + dictTypeService.resetDictCache(); + return success(); + } + + /** + * 获取字典选择框列表 + */ + @GetMapping("/optionselect") + public Result optionselect () { + List dictTypes = dictTypeService.selectDictTypeAll(); + return success(dictTypes); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysLogininforController.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysLogininforController.java new file mode 100644 index 0000000..c1e92b7 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysLogininforController.java @@ -0,0 +1,80 @@ +package com.muyu.system.controller; + +import com.muyu.common.core.constant.CacheConstants; +import com.muyu.common.core.utils.poi.ExcelUtil; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.web.page.TableDataInfo; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.redis.service.RedisService; +import com.muyu.common.security.annotation.InnerAuth; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.system.domain.SysLogininfor; +import com.muyu.system.service.SysLogininforService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 系统访问记录 + * + * @author muyu + */ +@RestController +@RequestMapping("/logininfor") +public class SysLogininforController extends BaseController { + @Autowired + private SysLogininforService logininforService; + + @Autowired + private RedisService redisService; + + @RequiresPermissions("system:logininfor:list") + @GetMapping("/list") + public Result> list (SysLogininfor logininfor) { + startPage(); + List list = logininforService.selectLogininforList(logininfor); + return getDataTable(list); + } + + @Log(title = "登录日志", businessType = BusinessType.EXPORT) + @RequiresPermissions("system:logininfor:export") + @PostMapping("/export") + public void export (HttpServletResponse response, SysLogininfor logininfor) { + List list = logininforService.selectLogininforList(logininfor); + ExcelUtil util = new ExcelUtil(SysLogininfor.class); + util.exportExcel(response, list, "登录日志"); + } + + @RequiresPermissions("system:logininfor:remove") + @Log(title = "登录日志", businessType = BusinessType.DELETE) + @DeleteMapping("/{infoIds}") + public Result remove (@PathVariable("infoIds") Long[] infoIds) { + return toAjax(logininforService.deleteLogininforByIds(infoIds)); + } + + @RequiresPermissions("system:logininfor:remove") + @Log(title = "登录日志", businessType = BusinessType.DELETE) + @DeleteMapping("/clean") + public Result clean () { + logininforService.cleanLogininfor(); + return success(); + } + + @RequiresPermissions("system:logininfor:unlock") + @Log(title = "账户解锁", businessType = BusinessType.OTHER) + @GetMapping("/unlock/{userName}") + public Result unlock (@PathVariable("userName") String userName) { + redisService.deleteObject(CacheConstants.PWD_ERR_CNT_KEY + userName); + return success(); + } + + @InnerAuth + @PostMapping + public Result add (@RequestBody SysLogininfor logininfor) { + return toAjax(logininforService.insertLogininfor(logininfor)); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysMenuController.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysMenuController.java new file mode 100644 index 0000000..e5ae348 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysMenuController.java @@ -0,0 +1,137 @@ +package com.muyu.system.controller; + +import com.muyu.common.core.constant.UserConstants; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.domain.Result; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.system.domain.SysMenu; +import com.muyu.system.domain.resp.RoleMenuTreeResp; +import com.muyu.system.service.SysMenuService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 菜单信息 + * + * @author muyu + */ +@RestController +@RequestMapping("/menu") +public class SysMenuController extends BaseController { + @Autowired + private SysMenuService menuService; + + /** + * 获取菜单列表 + */ + @RequiresPermissions("system:menu:list") + @GetMapping("/list") + public Result list (SysMenu menu) { + Long userId = SecurityUtils.getUserId(); + List menus = menuService.selectMenuList(menu, userId); + return success(menus); + } + + /** + * 根据菜单编号获取详细信息 + */ + @RequiresPermissions("system:menu:query") + @GetMapping(value = "/{menuId}") + public Result getInfo (@PathVariable("menuId") Long menuId) { + return success(menuService.selectMenuById(menuId)); + } + + /** + * 获取菜单下拉树列表 + */ + @GetMapping("/treeselect") + public Result treeselect (SysMenu menu) { + Long userId = SecurityUtils.getUserId(); + List menus = menuService.selectMenuList(menu, userId); + return success(menuService.buildMenuTreeSelect(menus)); + } + + /** + * 加载对应角色菜单列表树 + */ + @GetMapping(value = "/roleMenuTreeselect/{roleId}") + public Result roleMenuTreeselect (@PathVariable("roleId") Long roleId) { + Long userId = SecurityUtils.getUserId(); + List menus = menuService.selectMenuList(userId); + return Result.success( + RoleMenuTreeResp.builder() + .menus(menuService.buildMenuTreeSelect(menus)) + .checkedKeys(menuService.selectMenuListByRoleId(roleId)) + .build() + ); + } + + /** + * 新增菜单 + */ + @RequiresPermissions("system:menu:add") + @Log(title = "菜单管理", businessType = BusinessType.INSERT) + @PostMapping + public Result 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(SecurityUtils.getUsername()); + return toAjax(menuService.insertMenu(menu)); + } + + /** + * 修改菜单 + */ + @RequiresPermissions("system:menu:edit") + @Log(title = "菜单管理", businessType = BusinessType.UPDATE) + @PutMapping + public Result 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(SecurityUtils.getUsername()); + return toAjax(menuService.updateMenu(menu)); + } + + /** + * 删除菜单 + */ + @RequiresPermissions("system:menu:remove") + @Log(title = "菜单管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{menuId}") + public Result remove (@PathVariable("menuId") Long menuId) { + if (menuService.hasChildByMenuId(menuId)) { + return warn("存在子菜单,不允许删除"); + } + if (menuService.checkMenuExistRole(menuId)) { + return warn("菜单已分配,不允许删除"); + } + return toAjax(menuService.deleteMenuById(menuId)); + } + + /** + * 获取路由信息 + * + * @return 路由信息 + */ + @GetMapping("getRouters") + public Result getRouters () { + Long userId = SecurityUtils.getUserId(); + List menus = menuService.selectMenuTreeByUserId(userId); + return success(menuService.buildMenus(menus)); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysNoticeController.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysNoticeController.java new file mode 100644 index 0000000..e6b9ede --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysNoticeController.java @@ -0,0 +1,80 @@ +package com.muyu.system.controller; + +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.web.page.TableDataInfo; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.system.domain.SysNotice; +import com.muyu.system.service.SysNoticeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 公告 信息操作处理 + * + * @author muyu + */ +@RestController +@RequestMapping("/notice") +public class SysNoticeController extends BaseController { + @Autowired + private SysNoticeService noticeService; + + /** + * 获取通知公告列表 + */ + @RequiresPermissions("system:notice:list") + @GetMapping("/list") + public Result> list (SysNotice notice) { + startPage(); + List list = noticeService.selectNoticeList(notice); + return getDataTable(list); + } + + /** + * 根据通知公告编号获取详细信息 + */ + @RequiresPermissions("system:notice:query") + @GetMapping(value = "/{noticeId}") + public Result getInfo (@PathVariable("noticeId") Long noticeId) { + return success(noticeService.selectNoticeById(noticeId)); + } + + /** + * 新增通知公告 + */ + @RequiresPermissions("system:notice:add") + @Log(title = "通知公告", businessType = BusinessType.INSERT) + @PostMapping + public Result add (@Validated @RequestBody SysNotice notice) { + notice.setCreateBy(SecurityUtils.getUsername()); + return toAjax(noticeService.insertNotice(notice)); + } + + /** + * 修改通知公告 + */ + @RequiresPermissions("system:notice:edit") + @Log(title = "通知公告", businessType = BusinessType.UPDATE) + @PutMapping + public Result edit (@Validated @RequestBody SysNotice notice) { + notice.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(noticeService.updateNotice(notice)); + } + + /** + * 删除通知公告 + */ + @RequiresPermissions("system:notice:remove") + @Log(title = "通知公告", businessType = BusinessType.DELETE) + @DeleteMapping("/{noticeIds}") + public Result remove (@PathVariable("noticeIds") Long[] noticeIds) { + return toAjax(noticeService.deleteNoticeByIds(noticeIds)); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysOperlogController.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysOperlogController.java new file mode 100644 index 0000000..bb00d1d --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysOperlogController.java @@ -0,0 +1,67 @@ +package com.muyu.system.controller; + +import com.muyu.common.core.utils.poi.ExcelUtil; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.web.page.TableDataInfo; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.security.annotation.InnerAuth; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.system.domain.SysOperLog; +import com.muyu.system.service.SysOperLogService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 操作日志记录 + * + * @author muyu + */ +@RestController +@RequestMapping("/operlog") +public class SysOperlogController extends BaseController { + @Autowired + private SysOperLogService operLogService; + + @RequiresPermissions("system:operlog:list") + @GetMapping("/list") + public Result> list (SysOperLog operLog) { + startPage(); + List list = operLogService.selectOperLogList(operLog); + return getDataTable(list); + } + + @Log(title = "操作日志", businessType = BusinessType.EXPORT) + @RequiresPermissions("system:operlog:export") + @PostMapping("/export") + public void export (HttpServletResponse response, SysOperLog operLog) { + List list = operLogService.selectOperLogList(operLog); + ExcelUtil util = new ExcelUtil(SysOperLog.class); + util.exportExcel(response, list, "操作日志"); + } + + @Log(title = "操作日志", businessType = BusinessType.DELETE) + @RequiresPermissions("system:operlog:remove") + @DeleteMapping("/{operIds}") + public Result remove (@PathVariable("operIds") Long[] operIds) { + return toAjax(operLogService.deleteOperLogByIds(operIds)); + } + + @RequiresPermissions("system:operlog:remove") + @Log(title = "操作日志", businessType = BusinessType.CLEAN) + @DeleteMapping("/clean") + public Result clean () { + operLogService.cleanOperLog(); + return success(); + } + + @InnerAuth + @PostMapping + public Result add (@RequestBody SysOperLog operLog) { + return toAjax(operLogService.insertOperlog(operLog)); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysPostController.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysPostController.java new file mode 100644 index 0000000..79f3f47 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysPostController.java @@ -0,0 +1,110 @@ +package com.muyu.system.controller; + +import com.muyu.common.core.utils.poi.ExcelUtil; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.web.page.TableDataInfo; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.system.domain.SysPost; +import com.muyu.system.service.SysPostService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 岗位信息操作处理 + * + * @author muyu + */ +@RestController +@RequestMapping("/post") +public class SysPostController extends BaseController { + @Autowired + private SysPostService postService; + + /** + * 获取岗位列表 + */ + @RequiresPermissions("system:post:list") + @GetMapping("/list") + public Result> list (SysPost post) { + startPage(); + List list = postService.selectPostList(post); + return getDataTable(list); + } + + @Log(title = "岗位管理", businessType = BusinessType.EXPORT) + @RequiresPermissions("system:post:export") + @PostMapping("/export") + public void export (HttpServletResponse response, SysPost post) { + List list = postService.selectPostList(post); + ExcelUtil util = new ExcelUtil(SysPost.class); + util.exportExcel(response, list, "岗位数据"); + } + + /** + * 根据岗位编号获取详细信息 + */ + @RequiresPermissions("system:post:query") + @GetMapping(value = "/{postId}") + public Result getInfo (@PathVariable("postId") Long postId) { + return success(postService.selectPostById(postId)); + } + + /** + * 新增岗位 + */ + @RequiresPermissions("system:post:add") + @Log(title = "岗位管理", businessType = BusinessType.INSERT) + @PostMapping + public Result add (@Validated @RequestBody SysPost post) { + if (!postService.checkPostNameUnique(post)) { + return error("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } else if (!postService.checkPostCodeUnique(post)) { + return error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + post.setCreateBy(SecurityUtils.getUsername()); + return toAjax(postService.insertPost(post)); + } + + /** + * 修改岗位 + */ + @RequiresPermissions("system:post:edit") + @Log(title = "岗位管理", businessType = BusinessType.UPDATE) + @PutMapping + public Result edit (@Validated @RequestBody SysPost post) { + if (!postService.checkPostNameUnique(post)) { + return error("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } else if (!postService.checkPostCodeUnique(post)) { + return error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + post.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(postService.updatePost(post)); + } + + /** + * 删除岗位 + */ + @RequiresPermissions("system:post:remove") + @Log(title = "岗位管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{postIds}") + public Result remove (@PathVariable("postIds") Long[] postIds) { + return toAjax(postService.deletePostByIds(postIds)); + } + + /** + * 获取岗位选择框列表 + */ + @GetMapping("/optionselect") + public Result optionselect () { + List posts = postService.selectPostAll(); + return success(posts); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysProfileController.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysProfileController.java new file mode 100644 index 0000000..ae49255 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysProfileController.java @@ -0,0 +1,134 @@ +package com.muyu.system.controller; + +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.utils.file.FileTypeUtils; +import com.muyu.common.core.utils.file.MimeTypeUtils; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.security.service.TokenService; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.remote.RemoteFileService; +import com.muyu.common.system.domain.SysFile; +import com.muyu.common.system.domain.SysUser; +import com.muyu.common.system.domain.LoginUser; +import com.muyu.system.domain.resp.ProfileResp; +import com.muyu.system.service.SysUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Arrays; + +/** + * 个人信息 业务处理 + * + * @author muyu + */ +@RestController +@RequestMapping("/user/profile") +public class SysProfileController extends BaseController { + @Autowired + private SysUserService userService; + + @Autowired + private TokenService tokenService; + + @Autowired + private RemoteFileService remoteFileService; + + /** + * 个人信息 + */ + @GetMapping + public Result profile () { + String username = SecurityUtils.getUsername(); + SysUser user = userService.selectUserByUserName(username); + return Result.success( + ProfileResp.builder() + .roleGroup( userService.selectUserRoleGroup(username) ) + .postGroup( userService.selectUserPostGroup(username) ) + .sysUser(user) + .build() + ); + } + + /** + * 修改用户 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping + public Result updateProfile (@RequestBody SysUser user) { + LoginUser loginUser = SecurityUtils.getLoginUser(); + SysUser currentUser = loginUser.getSysUser(); + 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 Result updatePwd (String oldPassword, String newPassword) { + String username = SecurityUtils.getUsername(); + SysUser user = userService.selectUserByUserName(username); + String password = user.getPassword(); + if (!SecurityUtils.matchesPassword(oldPassword, password)) { + return error("修改密码失败,旧密码错误"); + } + if (SecurityUtils.matchesPassword(newPassword, password)) { + return error("新密码不能与旧密码相同"); + } + if (userService.resetUserPwd(username, SecurityUtils.encryptPassword(newPassword)) > 0) { + // 更新缓存用户密码 + LoginUser loginUser = SecurityUtils.getLoginUser(); + loginUser.getSysUser().setPassword(SecurityUtils.encryptPassword(newPassword)); + tokenService.setLoginUser(loginUser); + return success(); + } + return error("修改密码异常,请联系管理员"); + } + + /** + * 头像上传 + */ + @Log(title = "用户头像", businessType = BusinessType.UPDATE) + @PostMapping("/avatar") + public Result avatar (@RequestParam("avatarfile") MultipartFile file) { + if (!file.isEmpty()) { + LoginUser loginUser = SecurityUtils.getLoginUser(); + String extension = FileTypeUtils.getExtension(file); + if (!StringUtils.equalsAnyIgnoreCase(extension, MimeTypeUtils.IMAGE_EXTENSION)) { + return error("文件格式不正确,请上传" + Arrays.toString(MimeTypeUtils.IMAGE_EXTENSION) + "格式"); + } + Result fileResult = remoteFileService.upload(file); + if (StringUtils.isNull(fileResult) || StringUtils.isNull(fileResult.getData())) { + return error("文件服务异常,请联系管理员"); + } + String url = fileResult.getData().getUrl(); + if (userService.updateUserAvatar(loginUser.getUsername(), url)) { + // 更新缓存用户头像 + loginUser.getSysUser().setAvatar(url); + tokenService.setLoginUser(loginUser); + return Result.success(url); + } + } + return error("上传图片异常,请联系管理员"); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysRoleController.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysRoleController.java new file mode 100644 index 0000000..0926c41 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysRoleController.java @@ -0,0 +1,216 @@ +package com.muyu.system.controller; + +import com.muyu.common.core.utils.poi.ExcelUtil; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.web.page.TableDataInfo; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.SysDept; +import com.muyu.common.system.domain.SysRole; +import com.muyu.common.system.domain.SysUser; +import com.muyu.system.domain.SysUserRole; +import com.muyu.system.domain.resp.DeptTreeResp; +import com.muyu.system.service.SysDeptService; +import com.muyu.system.service.SysRoleService; +import com.muyu.system.service.SysUserService; +import jakarta.websocket.server.PathParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 角色信息 + * + * @author muyu + */ +@RestController +@RequestMapping("/role") +public class SysRoleController extends BaseController { + @Autowired + private SysRoleService roleService; + + @Autowired + private SysUserService userService; + + @Autowired + private SysDeptService deptService; + + @RequiresPermissions("system:role:list") + @GetMapping("/list") + public Result> list (SysRole role) { + startPage(); + List list = roleService.selectRoleList(role); + return getDataTable(list); + } + + @Log(title = "角色管理", businessType = BusinessType.EXPORT) + @RequiresPermissions("system:role:export") + @PostMapping("/export") + public void export (HttpServletResponse response, SysRole role) { + List list = roleService.selectRoleList(role); + ExcelUtil util = new ExcelUtil(SysRole.class); + util.exportExcel(response, list, "角色数据"); + } + + /** + * 根据角色编号获取详细信息 + */ + @RequiresPermissions("system:role:query") + @GetMapping(value = "/{roleId}") + public Result getInfo (@PathVariable("roleId") Long roleId) { + roleService.checkRoleDataScope(roleId); + return success(roleService.selectRoleById(roleId)); + } + + /** + * 新增角色 + */ + @RequiresPermissions("system:role:add") + @Log(title = "角色管理", businessType = BusinessType.INSERT) + @PostMapping + public Result add (@Validated @RequestBody SysRole role) { + if (!roleService.checkRoleNameUnique(role)) { + return error("新增角色'" + role.getRoleName() + "'失败,角色名称已存在"); + } else if (!roleService.checkRoleKeyUnique(role)) { + return error("新增角色'" + role.getRoleName() + "'失败,角色权限已存在"); + } + role.setCreateBy(SecurityUtils.getUsername()); + return toAjax(roleService.insertRole(role)); + + } + + /** + * 修改保存角色 + */ + @RequiresPermissions("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping + public Result edit (@Validated @RequestBody SysRole role) { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + if (!roleService.checkRoleNameUnique(role)) { + return error("修改角色'" + role.getRoleName() + "'失败,角色名称已存在"); + } else if (!roleService.checkRoleKeyUnique(role)) { + return error("修改角色'" + role.getRoleName() + "'失败,角色权限已存在"); + } + role.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(roleService.updateRole(role)); + } + + /** + * 修改保存数据权限 + */ + @RequiresPermissions("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping("/dataScope") + public Result dataScope (@RequestBody SysRole role) { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + return toAjax(roleService.authDataScope(role)); + } + + /** + * 状态修改 + */ + @RequiresPermissions("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public Result changeStatus (@RequestBody SysRole role) { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + role.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(roleService.updateRoleStatus(role)); + } + + /** + * 删除角色 + */ + @RequiresPermissions("system:role:remove") + @Log(title = "角色管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{roleIds}") + public Result remove (@PathVariable("roleIds") Long[] roleIds) { + return toAjax(roleService.deleteRoleByIds(roleIds)); + } + + /** + * 获取角色选择框列表 + */ + @RequiresPermissions("system:role:query") + @GetMapping("/optionselect") + public Result optionselect () { + return success(roleService.selectRoleAll()); + } + + /** + * 查询已分配用户角色列表 + */ + @RequiresPermissions("system:role:list") + @GetMapping("/authUser/allocatedList") + public Result> allocatedList (SysUser user) { + startPage(); + List list = userService.selectAllocatedList(user); + return getDataTable(list); + } + + /** + * 查询未分配用户角色列表 + */ + @RequiresPermissions("system:role:list") + @GetMapping("/authUser/unallocatedList") + public Result> unallocatedList (SysUser user) { + startPage(); + List list = userService.selectUnallocatedList(user); + return getDataTable(list); + } + + /** + * 取消授权用户 + */ + @RequiresPermissions("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/cancel") + public Result cancelAuthUser (@RequestBody SysUserRole userRole) { + return toAjax(roleService.deleteAuthUser(userRole)); + } + + /** + * 批量取消授权用户 + */ + @RequiresPermissions("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/cancelAll") + public Result cancelAuthUserAll (Long roleId, Long[] userIds) { + return toAjax(roleService.deleteAuthUsers(roleId, userIds)); + } + + /** + * 批量选择用户授权 + */ + @RequiresPermissions("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/selectAll") + public Result selectAuthUserAll (@RequestParam("roleId") Long roleId,@RequestParam("userIds") Long[] userIds) { + roleService.checkRoleDataScope(roleId); + return toAjax(roleService.insertAuthUsers(roleId, userIds)); + } + + /** + * 获取对应角色部门树列表 + */ + @RequiresPermissions("system:role:query") + @GetMapping(value = "/deptTree/{roleId}") + public Result deptTree (@PathVariable("roleId") Long roleId) { + return Result.success( + DeptTreeResp.builder() + .depts(deptService.selectDeptTreeList(new SysDept())) + .checkedKeys(deptService.selectDeptListByRoleId(roleId)) + .build() + ); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysUserController.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysUserController.java new file mode 100644 index 0000000..d3aa7ca --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysUserController.java @@ -0,0 +1,299 @@ +package com.muyu.system.controller; + +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.utils.poi.ExcelUtil; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.web.page.TableDataInfo; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.security.annotation.InnerAuth; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.SysDept; +import com.muyu.common.system.domain.SysRole; +import com.muyu.common.system.domain.SysUser; +import com.muyu.common.system.domain.LoginUser; +import com.muyu.system.domain.resp.AuthRoleResp; +import com.muyu.system.domain.resp.UserDetailInfoResp; +import com.muyu.system.domain.resp.UserInfoResp; +import com.muyu.system.service.*; +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 用户信息 + * + * @author muyu + */ +@RestController +@RequestMapping("/user") +public class SysUserController extends BaseController { + @Autowired + private SysUserService userService; + + @Autowired + private SysRoleService roleService; + + @Autowired + private SysDeptService deptService; + + @Autowired + private SysPostService postService; + + @Autowired + private SysPermissionService permissionService; + + @Autowired + private SysConfigService configService; + + /** + * 获取用户列表 + */ + @RequiresPermissions("system:user:list") + @GetMapping("/list") + public Result> list (SysUser user) { + startPage(); + List list = userService.selectUserList(user); + return getDataTable(list); + } + + @GetMapping("/companyList") + public Result> companyList () { + List list = userService.selectCompanyList(); + return Result.success(list); + } + + @Log(title = "用户管理", businessType = BusinessType.EXPORT) + @RequiresPermissions("system:user:export") + @PostMapping("/export") + public void export (HttpServletResponse response, SysUser user) { + List list = userService.selectUserList(user); + ExcelUtil util = new ExcelUtil(SysUser.class); + util.exportExcel(response, list, "用户数据"); + } + + @Log(title = "用户管理", businessType = BusinessType.IMPORT) + @RequiresPermissions("system:user:import") + @PostMapping("/importData") + public Result importData (MultipartFile file, boolean updateSupport) throws Exception { + ExcelUtil util = new ExcelUtil(SysUser.class); + List userList = util.importExcel(file.getInputStream()); + String operName = SecurityUtils.getUsername(); + String message = userService.importUser(userList, updateSupport, operName); + return success(message); + } + + @PostMapping("/importTemplate") + public void importTemplate (HttpServletResponse response) throws IOException { + ExcelUtil util = new ExcelUtil(SysUser.class); + util.importTemplateExcel(response, "用户数据"); + } + + /** + * 获取当前用户信息 + */ + @InnerAuth + @GetMapping("/info/{username}") + public Result info (@PathVariable("username") String username) { + SysUser sysUser = userService.selectUserByUserName(username); + if (StringUtils.isNull(sysUser)) { + return Result.error("用户名或密码错误"); + } + // 角色集合 + Set roles = permissionService.getRolePermission(sysUser); + // 权限集合 + Set permissions = permissionService.getMenuPermission(sysUser); + LoginUser sysUserVo = new LoginUser(); + sysUserVo.setSysUser(sysUser); + sysUserVo.setRoles(roles); + sysUserVo.setPermissions(permissions); + return Result.success(sysUserVo); + } + + /** + * 注册用户信息 + */ + @InnerAuth + @PostMapping("/register") + public Result register (@RequestBody SysUser sysUser) { + String username = sysUser.getUserName(); + if (!("true".equals(configService.selectConfigByKey("sys.account.registerUser")))) { + return Result.error("当前系统没有开启注册功能!"); + } + if (!userService.checkUserNameUnique(sysUser)) { + return Result.error("保存用户'" + username + "'失败,注册账号已存在"); + } + return Result.success(userService.registerUser(sysUser)); + } + + /** + * 获取用户信息 + * + * @return 用户信息 + */ + @GetMapping("getInfo") + public Result getInfo () { + SysUser user = userService.selectUserById(SecurityUtils.getUserId()); + // 角色集合 + Set roles = permissionService.getRolePermission(user); + // 权限集合 + Set permissions = permissionService.getMenuPermission(user); + + return Result.success( + UserInfoResp.builder() + .user(user) + .roles(roles) + .permissions(permissions) + .build() + ); + } + + /** + * 根据用户编号获取详细信息 + */ + @RequiresPermissions("system:user:query") + @GetMapping(value = {"/", "/{userId}"}) + public Result getInfo (@PathVariable(value = "userId", required = false) Long userId) { + userService.checkUserDataScope(userId); + UserDetailInfoResp.UserDetailInfoRespBuilder builder = UserDetailInfoResp.builder(); + List roles = roleService.selectRoleAll(); + builder.roles( + SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()) + ) + .posts( postService.selectPostAll() ); + if (StringUtils.isNotNull(userId)) { + SysUser sysUser = userService.selectUserById(userId); + builder.sysUser(sysUser) + .postIds(postService.selectPostListByUserId(userId)) + .roleIds(sysUser.getRoles().stream().map(SysRole::getRoleId).collect(Collectors.toList())) + .build(); + } + return Result.success(builder.build()); + } + + /** + * 新增用户 + */ + @RequiresPermissions("system:user:add") + @Log(title = "用户管理", businessType = BusinessType.INSERT) + @PostMapping + public Result add (@Validated @RequestBody SysUser user) { + if (!userService.checkUserNameUnique(user)) { + return error("新增用户'" + user.getUserName() + "'失败,登录账号已存在"); + } else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) { + return error("新增用户'" + user.getUserName() + "'失败,手机号码已存在"); + } else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) { + return error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + user.setCreateBy(SecurityUtils.getUsername()); + user.setPassword(SecurityUtils.encryptPassword(user.getPassword())); + return toAjax(userService.insertUser(user)); + } + + /** + * 修改用户 + */ + @RequiresPermissions("system:user:edit") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping + public Result edit (@Validated @RequestBody SysUser user) { + 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() + "'失败,手机号码已存在"); + } else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) { + return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + user.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(userService.updateUser(user)); + } + + /** + * 删除用户 + */ + @RequiresPermissions("system:user:remove") + @Log(title = "用户管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{userIds}") + public Result remove (@PathVariable("userIds") Long[] userIds) { + if (ArrayUtils.contains(userIds, SecurityUtils.getUserId())) { + return error("当前用户不能删除"); + } + return toAjax(userService.deleteUserByIds(userIds)); + } + + /** + * 重置密码 + */ + @RequiresPermissions("system:user:edit") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping("/resetPwd") + public Result resetPwd (@RequestBody SysUser user) { + userService.checkUserAllowed(user); + userService.checkUserDataScope(user.getUserId()); + user.setPassword(SecurityUtils.encryptPassword(user.getPassword())); + user.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(userService.resetPwd(user)); + } + + /** + * 状态修改 + */ + @RequiresPermissions("system:user:edit") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public Result changeStatus (@RequestBody SysUser user) { + userService.checkUserAllowed(user); + userService.checkUserDataScope(user.getUserId()); + user.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(userService.updateUserStatus(user)); + } + + /** + * 根据用户编号获取授权角色 + */ + @RequiresPermissions("system:user:query") + @GetMapping("/authRole/{userId}") + public Result authRole (@PathVariable("userId") Long userId) { + SysUser user = userService.selectUserById(userId); + List roles = roleService.selectRolesByUserId(userId); + return Result.success( + AuthRoleResp.builder() + .roles(SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList())) + .user(user) + .build() + ); + } + + /** + * 用户授权角色 + */ + @RequiresPermissions("system:user:edit") + @Log(title = "用户管理", businessType = BusinessType.GRANT) + @PutMapping("/authRole") + public Result insertAuthRole (@RequestParam("userId") Long userId,@RequestParam("roleIds") Long[] roleIds) { + userService.checkUserDataScope(userId); + userService.insertUserAuth(userId, roleIds); + return success(); + } + + /** + * 获取部门树列表 + */ + @RequiresPermissions("system:user:list") + @GetMapping("/deptTree") + public Result deptTree (SysDept dept) { + return success(deptService.selectDeptTreeList(dept)); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysUserOnlineController.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysUserOnlineController.java new file mode 100644 index 0000000..5b78721 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/controller/SysUserOnlineController.java @@ -0,0 +1,69 @@ +package com.muyu.system.controller; + +import com.muyu.common.core.constant.CacheConstants; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.web.page.TableDataInfo; +import com.muyu.common.log.annotation.Log; +import com.muyu.common.log.enums.BusinessType; +import com.muyu.common.redis.service.RedisService; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.system.domain.LoginUser; +import com.muyu.system.domain.SysUserOnline; +import com.muyu.system.service.SysUserOnlineService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * 在线用户监控 + * + * @author muyu + */ +@RestController +@RequestMapping("/online") +public class SysUserOnlineController extends BaseController { + @Autowired + private SysUserOnlineService userOnlineService; + + @Autowired + private RedisService redisService; + + @RequiresPermissions("monitor:online:list") + @GetMapping("/list") + public Result> list (String ipaddr, String userName) { + Collection keys = redisService.keys(CacheConstants.LOGIN_TOKEN_KEY + "*"); + List userOnlineList = new ArrayList(); + for (String key : keys) { + LoginUser user = redisService.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)) { + userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user)); + } else { + userOnlineList.add(userOnlineService.loginUserToUserOnline(user)); + } + } + Collections.reverse(userOnlineList); + userOnlineList.removeAll(Collections.singleton(null)); + return getDataTable(userOnlineList); + } + + /** + * 强退用户 + */ + @RequiresPermissions("monitor:online:forceLogout") + @Log(title = "在线用户", businessType = BusinessType.FORCE) + @DeleteMapping("/{tokenId}") + public Result forceLogout (@PathVariable("tokenId") String tokenId) { + redisService.deleteObject(CacheConstants.LOGIN_TOKEN_KEY + tokenId); + return success(); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysConfig.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysConfig.java new file mode 100644 index 0000000..6f7e8bf --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysConfig.java @@ -0,0 +1,81 @@ +package com.muyu.system.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.core.annotation.Excel.ColumnType; +import com.muyu.common.core.web.domain.BaseEntity; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +/** + * 参数配置表 sys_config + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +@TableName("sys_config") +public class SysConfig extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 参数主键 + */ + @Excel(name = "参数主键", cellType = ColumnType.NUMERIC) + @TableId( type = IdType.AUTO) + private Long configId; + + /** + * 参数名称 + */ + @Excel(name = "参数名称") + private String configName; + + /** + * 参数键名 + */ + @Excel(name = "参数键名") + private String configKey; + + /** + * 参数键值 + */ + @Excel(name = "参数键值") + private String configValue; + + /** + * 系统内置(Y是 N否) + */ + @Excel(name = "系统内置", readConverterExp = "Y=是,N=否") + private String configType; + + @NotBlank(message = "参数名称不能为空") + @Size(min = 0, max = 100, message = "参数名称不能超过100个字符") + public String getConfigName () { + return configName; + } + + @NotBlank(message = "参数键名长度不能为空") + @Size(min = 0, max = 100, message = "参数键名长度不能超过100个字符") + public String getConfigKey () { + return configKey; + } + + @NotBlank(message = "参数键值不能为空") + @Size(min = 0, max = 500, message = "参数键值长度不能超过500个字符") + public String getConfigValue () { + return configValue; + } + +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysMenu.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysMenu.java new file mode 100644 index 0000000..e4ad4e4 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysMenu.java @@ -0,0 +1,138 @@ +package com.muyu.system.domain; + +import com.muyu.common.core.web.domain.BaseEntity; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import java.util.ArrayList; +import java.util.List; + +/** + * 菜单权限表 sys_menu + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +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; + + /** + * 子菜单 + */ + @Builder.Default + private List children = new ArrayList(); + + @NotBlank(message = "菜单名称不能为空") + @Size(min = 0, max = 50, message = "菜单名称长度不能超过50个字符") + public String getMenuName () { + return menuName; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getOrderNum () { + return orderNum; + } + + @Size(min = 0, max = 200, message = "路由地址不能超过200个字符") + public String getPath () { + return path; + } + + @Size(min = 0, max = 200, message = "组件路径不能超过255个字符") + public String getComponent () { + return component; + } + + @NotBlank(message = "菜单类型不能为空") + public String getMenuType () { + return menuType; + } + + @Size(min = 0, max = 100, message = "权限标识长度不能超过100个字符") + public String getPerms () { + return perms; + } + +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysNotice.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysNotice.java new file mode 100644 index 0000000..a0c30e0 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysNotice.java @@ -0,0 +1,58 @@ +package com.muyu.system.domain; + +import com.muyu.common.core.web.domain.BaseEntity; +import com.muyu.common.core.xss.Xss; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +/** + * 通知公告表 sys_notice + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +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; + + @Xss(message = "公告标题不能包含脚本字符") + @NotBlank(message = "公告标题不能为空") + @Size(min = 0, max = 50, message = "公告标题不能超过50个字符") + public String getNoticeTitle () { + return noticeTitle; + } + } diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysPost.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysPost.java new file mode 100644 index 0000000..326987c --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysPost.java @@ -0,0 +1,78 @@ +package com.muyu.system.domain; + +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.core.annotation.Excel.ColumnType; +import com.muyu.common.core.web.domain.BaseEntity; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +/** + * 岗位表 sys_post + * + * @author muyu + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class SysPost extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 岗位序号 + */ + @Excel(name = "岗位序号", cellType = ColumnType.NUMERIC) + private Long postId; + + /** + * 岗位编码 + */ + @Excel(name = "岗位编码") + private String postCode; + + /** + * 岗位名称 + */ + @Excel(name = "岗位名称") + private String postName; + + /** + * 岗位排序 + */ + @Excel(name = "岗位排序") + private Integer postSort; + + /** + * 状态(0正常 1停用) + */ + @Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private String status; + + /** + * 用户是否存在此岗位标识 默认不存在 + */ + @Builder.Default + private boolean flag = false; + + @NotBlank(message = "岗位编码不能为空") + @Size(min = 0, max = 64, message = "岗位编码长度不能超过64个字符") + public String getPostCode () { + return postCode; + } + + @NotBlank(message = "岗位名称不能为空") + @Size(min = 0, max = 50, message = "岗位名称长度不能超过50个字符") + public String getPostName () { + return postName; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getPostSort () { + return postSort; + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysRoleDept.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysRoleDept.java new file mode 100644 index 0000000..ef9f13c --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysRoleDept.java @@ -0,0 +1,27 @@ +package com.muyu.system.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 角色和部门关联 sys_role_dept + * + * @author muyu + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SysRoleDept { + /** + * 角色ID + */ + private Long roleId; + + /** + * 部门ID + */ + private Long deptId; +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysRoleMenu.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysRoleMenu.java new file mode 100644 index 0000000..fe4074b --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysRoleMenu.java @@ -0,0 +1,28 @@ +package com.muyu.system.domain; + + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 角色和菜单关联 sys_role_menu + * + * @author muyu + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SysRoleMenu { + /** + * 角色ID + */ + private Long roleId; + + /** + * 菜单ID + */ + private Long menuId; +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysUserOnline.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysUserOnline.java new file mode 100644 index 0000000..23d1979 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysUserOnline.java @@ -0,0 +1,52 @@ +package com.muyu.system.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 当前在线会话 + * + * @author muyu + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SysUserOnline { + /** + * 会话编号 + */ + private String tokenId; + + /** + * 用户名称 + */ + private String userName; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地址 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 登录时间 + */ + private Long loginTime; +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysUserPost.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysUserPost.java new file mode 100644 index 0000000..229dc70 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysUserPost.java @@ -0,0 +1,28 @@ +package com.muyu.system.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 用户和岗位关联 sys_user_post + * + * @author muyu + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SysUserPost { + /** + * 用户ID + */ + private Long userId; + + /** + * 岗位ID + */ + private Long postId; + +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysUserRole.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysUserRole.java new file mode 100644 index 0000000..ee9c945 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/SysUserRole.java @@ -0,0 +1,27 @@ +package com.muyu.system.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 用户和角色关联 sys_user_role + * + * @author muyu + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SysUserRole { + /** + * 用户ID + */ + private Long userId; + + /** + * 角色ID + */ + private Long roleId; +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/AuthRoleResp.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/AuthRoleResp.java new file mode 100644 index 0000000..69294b6 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/AuthRoleResp.java @@ -0,0 +1,33 @@ +package com.muyu.system.domain.resp; + + +import com.muyu.common.system.domain.SysRole; +import com.muyu.common.system.domain.SysUser; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.List; + +/** + * @author DongZl + * @description: 授权角色返回结果集 + * @Date 2023-6-19 下午 02:50 + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class AuthRoleResp { + + /** + * 用户信息 + */ + private SysUser user; + + /** + * 角色集合 + */ + private List roles; +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/DeptTreeResp.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/DeptTreeResp.java new file mode 100644 index 0000000..cd72807 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/DeptTreeResp.java @@ -0,0 +1,32 @@ +package com.muyu.system.domain.resp; + + +import com.muyu.system.domain.vo.TreeSelect; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.List; + +/** + * @author DongZl + * @description: 部门树返回结果集 + * @Date 2023-6-19 下午 02:52 + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class DeptTreeResp { + + /** + * 授权的ID + */ + private List checkedKeys; + + /** + * 部门树 + */ + private List depts; +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/ProfileResp.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/ProfileResp.java new file mode 100644 index 0000000..21d9808 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/ProfileResp.java @@ -0,0 +1,34 @@ +package com.muyu.system.domain.resp; + +import com.muyu.common.system.domain.SysUser; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +/** + * @author DongZl + * @description: 个人信息模型对象 + * @Date 2023-6-19 下午 02:05 + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class ProfileResp { + + /** + * 系统用户 + */ + private SysUser sysUser; + + /** + * 用户权限组 + */ + private String roleGroup; + + /** + * 用户岗位组 + */ + private String postGroup; +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/RoleMenuTreeResp.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/RoleMenuTreeResp.java new file mode 100644 index 0000000..696763e --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/RoleMenuTreeResp.java @@ -0,0 +1,31 @@ +package com.muyu.system.domain.resp; + +import com.muyu.system.domain.vo.TreeSelect; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.List; + +/** + * @author DongZl + * @description: 角色返回菜单树 + * @Date 2023-6-19 下午 02:40 + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class RoleMenuTreeResp { + + /** + * 拥有的菜单权限 + */ + private List checkedKeys; + + /** + * 系统所有的菜单 + */ + private List menus; +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/UserDetailInfoResp.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/UserDetailInfoResp.java new file mode 100644 index 0000000..4c61832 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/UserDetailInfoResp.java @@ -0,0 +1,47 @@ +package com.muyu.system.domain.resp; + +import com.muyu.common.system.domain.SysRole; +import com.muyu.common.system.domain.SysUser; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.List; + +/** + * @author DongZl + * @description: 用户详细信息 + * @Date 2023-6-19 下午 02:45 + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class UserDetailInfoResp { + + /** + * 角色权限集合 + */ + private List roles; + + /** + * 岗位集合 + */ + private List posts; + + /** + * 用户信息 + */ + private SysUser sysUser; + + /** + * 用户又有的职位ID + */ + private List postIds; + + /** + * 用户拥有的角色ID + */ + private List roleIds; +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/UserInfoResp.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/UserInfoResp.java new file mode 100644 index 0000000..d15a760 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/resp/UserInfoResp.java @@ -0,0 +1,36 @@ +package com.muyu.system.domain.resp; + +import com.muyu.common.system.domain.SysUser; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.Set; + +/** + * @author DongZl + * @description: 用户信息结果集 + * @Date 2023-6-19 下午 02:42 + */ +@Data +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class UserInfoResp { + + /** + * 用户 + */ + private SysUser user; + + /** + * 角色集合 + */ + private Set roles; + + /** + * 权限集合 + */ + private Set permissions; +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/vo/MetaVo.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/vo/MetaVo.java new file mode 100644 index 0000000..c7d5f25 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/vo/MetaVo.java @@ -0,0 +1,54 @@ +package com.muyu.system.domain.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 路由显示信息 + * + * @author muyu + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MetaVo { + /** + * 设置该路由在侧边栏和面包屑中展示的名字 + */ + private String title; + + /** + * 设置该路由的图标,对应路径src/assets/icons/svg + */ + private String icon; + + /** + * 设置为true,则不会被 缓存 + */ + private boolean noCache; + + /** + * 内链地址(http(s)://开头) + */ + private String link; + + 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; + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/vo/RouterVo.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/vo/RouterVo.java new file mode 100644 index 0000000..6ea8d3f --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/vo/RouterVo.java @@ -0,0 +1,62 @@ +package com.muyu.system.domain.vo; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; +import lombok.Setter; + +import java.util.List; + +/** + * 路由配置信息 + * + * @author muyu + */ +@Data +@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 children; + +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/vo/TreeSelect.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/vo/TreeSelect.java new file mode 100644 index 0000000..d1ef75f --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/domain/vo/TreeSelect.java @@ -0,0 +1,56 @@ +package com.muyu.system.domain.vo; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.muyu.common.system.domain.SysDept; +import com.muyu.system.domain.SysMenu; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Treeselect树结构实体类 + * + * @author muyu + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +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 children; + + + 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()); + } + +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysConfigMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysConfigMapper.java new file mode 100644 index 0000000..ced239a --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysConfigMapper.java @@ -0,0 +1,12 @@ +package com.muyu.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.system.domain.SysConfig; + +/** + * @author DongZl + * @description: 配置mybatis配置 + * @Date 2023-11-13 上午 10:05 + */ +public interface SysConfigMapper extends BaseMapper { +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysDeptMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysDeptMapper.java new file mode 100644 index 0000000..70db361 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysDeptMapper.java @@ -0,0 +1,131 @@ +package com.muyu.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.common.system.domain.SysDept; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 部门管理 数据层 + * + * @author muyu + */ +public interface SysDeptMapper extends BaseMapper { + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * + * @return 部门信息集合 + */ + List selectDeptList(SysDept dept); + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @param deptCheckStrictly 部门树选择项是否关联显示 + * + * @return 选中部门列表 + */ + List selectDeptListByRoleId(@Param("roleId") Long roleId, @Param("deptCheckStrictly") boolean deptCheckStrictly); + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * + * @return 部门信息 + */ + SysDept selectDeptById(Long deptId); + + /** + * 根据ID查询所有子部门 + * + * @param deptId 部门ID + * + * @return 部门列表 + */ + List selectChildrenDeptById(Long deptId); + + /** + * 根据ID查询所有子部门(正常状态) + * + * @param deptId 部门ID + * + * @return 子部门数 + */ + int selectNormalChildrenDeptById(Long deptId); + + /** + * 是否存在子节点 + * + * @param deptId 部门ID + * + * @return 结果 + */ + int hasChildByDeptId(Long deptId); + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * + * @return 结果 + */ + int checkDeptExistUser(Long deptId); + + /** + * 校验部门名称是否唯一 + * + * @param deptName 部门名称 + * @param parentId 父部门ID + * + * @return 结果 + */ + SysDept checkDeptNameUnique(@Param("deptName") String deptName, @Param("parentId") Long parentId); + + /** + * 新增部门信息 + * + * @param dept 部门信息 + * + * @return 结果 + */ + int insertDept(SysDept dept); + + /** + * 修改部门信息 + * + * @param dept 部门信息 + * + * @return 结果 + */ + int updateDept(SysDept dept); + + /** + * 修改所在部门正常状态 + * + * @param deptIds 部门ID组 + */ + void updateDeptStatusNormal(Long[] deptIds); + + /** + * 修改子元素关系 + * + * @param depts 子元素 + * + * @return 结果 + */ + int updateDeptChildren(@Param("depts") List depts); + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * + * @return 结果 + */ + int deleteDeptById(Long deptId); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysDictDataMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysDictDataMapper.java new file mode 100644 index 0000000..f5cdc5d --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysDictDataMapper.java @@ -0,0 +1,106 @@ +package com.muyu.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.common.system.domain.SysDictData; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 字典表 数据层 + * + * @author muyu + */ +public interface SysDictDataMapper extends BaseMapper { + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * + * @return 字典数据集合信息 + */ + List selectDictDataList(SysDictData dictData); + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * + * @return 字典数据集合信息 + */ + List selectDictDataByType(String dictType); + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * + * @return 字典标签 + */ + String selectDictLabel(@Param("dictType") String dictType, @Param("dictValue") String dictValue); + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * + * @return 字典数据 + */ + SysDictData selectDictDataById(Long dictCode); + + /** + * 查询字典数据 + * + * @param dictType 字典类型 + * + * @return 字典数据 + */ + int countDictDataByType(String dictType); + + /** + * 通过字典ID删除字典数据信息 + * + * @param dictCode 字典数据ID + * + * @return 结果 + */ + int deleteDictDataById(Long dictCode); + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + * + * @return 结果 + */ + int deleteDictDataByIds(Long[] dictCodes); + + /** + * 新增字典数据信息 + * + * @param dictData 字典数据信息 + * + * @return 结果 + */ + int insertDictData(SysDictData dictData); + + /** + * 修改字典数据信息 + * + * @param dictData 字典数据信息 + * + * @return 结果 + */ + int updateDictData(SysDictData dictData); + + /** + * 同步修改字典类型 + * + * @param oldDictType 旧字典类型 + * @param newDictType 新旧字典类型 + * + * @return 结果 + */ + int updateDictDataType(@Param("oldDictType") String oldDictType, @Param("newDictType") String newDictType); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysDictTypeMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysDictTypeMapper.java new file mode 100644 index 0000000..3b8473f --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysDictTypeMapper.java @@ -0,0 +1,92 @@ +package com.muyu.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.common.system.domain.SysDictType; + +import java.util.List; + +/** + * 字典表 数据层 + * + * @author muyu + */ +public interface SysDictTypeMapper extends BaseMapper { + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * + * @return 字典类型集合信息 + */ + List selectDictTypeList(SysDictType dictType); + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + List selectDictTypeAll(); + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * + * @return 字典类型 + */ + SysDictType selectDictTypeById(Long dictId); + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * + * @return 字典类型 + */ + SysDictType selectDictTypeByType(String dictType); + + /** + * 通过字典ID删除字典信息 + * + * @param dictId 字典ID + * + * @return 结果 + */ + int deleteDictTypeById(Long dictId); + + /** + * 批量删除字典类型信息 + * + * @param dictIds 需要删除的字典ID + * + * @return 结果 + */ + int deleteDictTypeByIds(Long[] dictIds); + + /** + * 新增字典类型信息 + * + * @param dictType 字典类型信息 + * + * @return 结果 + */ + int insertDictType(SysDictType dictType); + + /** + * 修改字典类型信息 + * + * @param dictType 字典类型信息 + * + * @return 结果 + */ + int updateDictType(SysDictType dictType); + + /** + * 校验字典类型称是否唯一 + * + * @param dictType 字典类型 + * + * @return 结果 + */ + SysDictType checkDictTypeUnique(String dictType); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysLogininforMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysLogininforMapper.java new file mode 100644 index 0000000..0e88949 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysLogininforMapper.java @@ -0,0 +1,45 @@ +package com.muyu.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.common.system.domain.SysLogininfor; + +import java.util.List; + +/** + * 系统访问日志情况信息 数据层 + * + * @author muyu + */ +public interface SysLogininforMapper extends BaseMapper { + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + int insertLogininfor(SysLogininfor logininfor); + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * + * @return 登录记录集合 + */ + List selectLogininforList(SysLogininfor logininfor); + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * + * @return 结果 + */ + int deleteLogininforByIds(Long[] infoIds); + + /** + * 清空系统登录日志 + * + * @return 结果 + */ + int cleanLogininfor(); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysMenuMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysMenuMapper.java new file mode 100644 index 0000000..92a2a01 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysMenuMapper.java @@ -0,0 +1,138 @@ +package com.muyu.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.system.domain.SysMenu; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 菜单表 数据层 + * + * @author muyu + */ +public interface SysMenuMapper extends BaseMapper { + /** + * 查询系统菜单列表 + * + * @param menu 菜单信息 + * + * @return 菜单列表 + */ + List selectMenuList(SysMenu menu); + + /** + * 根据用户所有权限 + * + * @return 权限列表 + */ + List selectMenuPerms(); + + /** + * 根据用户查询系统菜单列表 + * + * @param menu 菜单信息 + * + * @return 菜单列表 + */ + List selectMenuListByUserId(SysMenu menu); + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * + * @return 权限列表 + */ + List selectMenuPermsByRoleId(Long roleId); + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * + * @return 权限列表 + */ + List selectMenuPermsByUserId(Long userId); + + /** + * 根据用户ID查询菜单 + * + * @return 菜单列表 + */ + List selectMenuTreeAll(); + + /** + * 根据用户ID查询菜单 + * + * @param userId 用户ID + * + * @return 菜单列表 + */ + List selectMenuTreeByUserId(Long userId); + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @param menuCheckStrictly 菜单树选择项是否关联显示 + * + * @return 选中菜单列表 + */ + List selectMenuListByRoleId(@Param("roleId") Long roleId, @Param("menuCheckStrictly") boolean menuCheckStrictly); + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * + * @return 菜单信息 + */ + SysMenu selectMenuById(Long menuId); + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * + * @return 结果 + */ + int hasChildByMenuId(Long menuId); + + /** + * 新增菜单信息 + * + * @param menu 菜单信息 + * + * @return 结果 + */ + int insertMenu(SysMenu menu); + + /** + * 修改菜单信息 + * + * @param menu 菜单信息 + * + * @return 结果 + */ + int updateMenu(SysMenu menu); + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * + * @return 结果 + */ + int deleteMenuById(Long menuId); + + /** + * 校验菜单名称是否唯一 + * + * @param menuName 菜单名称 + * @param parentId 父菜单ID + * + * @return 结果 + */ + SysMenu checkMenuNameUnique(@Param("menuName") String menuName, @Param("parentId") Long parentId); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysNoticeMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysNoticeMapper.java new file mode 100644 index 0000000..3d97964 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysNoticeMapper.java @@ -0,0 +1,67 @@ +package com.muyu.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.system.domain.SysNotice; + +import java.util.List; + +/** + * 通知公告表 数据层 + * + * @author muyu + */ +public interface SysNoticeMapper extends BaseMapper { + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * + * @return 公告信息 + */ + SysNotice selectNoticeById(Long noticeId); + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * + * @return 公告集合 + */ + List selectNoticeList(SysNotice notice); + + /** + * 新增公告 + * + * @param notice 公告信息 + * + * @return 结果 + */ + int insertNotice(SysNotice notice); + + /** + * 修改公告 + * + * @param notice 公告信息 + * + * @return 结果 + */ + int updateNotice(SysNotice notice); + + /** + * 批量删除公告 + * + * @param noticeId 公告ID + * + * @return 结果 + */ + int deleteNoticeById(Long noticeId); + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * + * @return 结果 + */ + int deleteNoticeByIds(Long[] noticeIds); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysOperLogMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysOperLogMapper.java new file mode 100644 index 0000000..752bb6b --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysOperLogMapper.java @@ -0,0 +1,52 @@ +package com.muyu.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.common.system.domain.SysOperLog; + +import java.util.List; + +/** + * 操作日志 数据层 + * + * @author muyu + */ +public interface SysOperLogMapper extends BaseMapper { + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + int insertOperlog(SysOperLog operLog); + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * + * @return 操作日志集合 + */ + List selectOperLogList(SysOperLog operLog); + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * + * @return 结果 + */ + int deleteOperLogByIds(Long[] operIds); + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * + * @return 操作日志对象 + */ + SysOperLog selectOperLogById(Long operId); + + /** + * 清空操作日志 + */ + void cleanOperLog(); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysPostMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysPostMapper.java new file mode 100644 index 0000000..b379183 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysPostMapper.java @@ -0,0 +1,110 @@ +package com.muyu.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.system.domain.SysPost; + +import java.util.List; + +/** + * 岗位信息 数据层 + * + * @author muyu + */ +public interface SysPostMapper extends BaseMapper { + /** + * 查询岗位数据集合 + * + * @param post 岗位信息 + * + * @return 岗位数据集合 + */ + List selectPostList(SysPost post); + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + List selectPostAll(); + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * + * @return 角色对象信息 + */ + SysPost selectPostById(Long postId); + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * + * @return 选中岗位ID列表 + */ + List selectPostListByUserId(Long userId); + + /** + * 查询用户所属岗位组 + * + * @param userName 用户名 + * + * @return 结果 + */ + List selectPostsByUserName(String userName); + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * + * @return 结果 + */ + int deletePostById(Long postId); + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * + * @return 结果 + */ + int deletePostByIds(Long[] postIds); + + /** + * 修改岗位信息 + * + * @param post 岗位信息 + * + * @return 结果 + */ + int updatePost(SysPost post); + + /** + * 新增岗位信息 + * + * @param post 岗位信息 + * + * @return 结果 + */ + int insertPost(SysPost post); + + /** + * 校验岗位名称 + * + * @param postName 岗位名称 + * + * @return 结果 + */ + SysPost checkPostNameUnique(String postName); + + /** + * 校验岗位编码 + * + * @param postCode 岗位编码 + * + * @return 结果 + */ + SysPost checkPostCodeUnique(String postCode); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysRoleDeptMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysRoleDeptMapper.java new file mode 100644 index 0000000..1df12da --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysRoleDeptMapper.java @@ -0,0 +1,50 @@ +package com.muyu.system.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.system.domain.SysRoleDept; + +import java.util.List; + +/** + * 角色与部门关联表 数据层 + * + * @author muyu + */ +public interface SysRoleDeptMapper extends BaseMapper { + /** + * 通过角色ID删除角色和部门关联 + * + * @param roleId 角色ID + * + * @return 结果 + */ + int deleteRoleDeptByRoleId(Long roleId); + + /** + * 批量删除角色部门关联信息 + * + * @param ids 需要删除的数据ID + * + * @return 结果 + */ + int deleteRoleDept(Long[] ids); + + /** + * 查询部门使用数量 + * + * @param deptId 部门ID + * + * @return 结果 + */ + int selectCountRoleDeptByDeptId(Long deptId); + + /** + * 批量新增角色部门信息 + * + * @param roleDeptList 角色部门列表 + * + * @return 结果 + */ + int batchRoleDept(List roleDeptList); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysRoleMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysRoleMapper.java new file mode 100644 index 0000000..bb2de63 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysRoleMapper.java @@ -0,0 +1,119 @@ +package com.muyu.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.common.system.domain.SysRole; + +import java.util.List; + +/** + * 角色表 数据层 + * + * @author muyu + */ +public interface SysRoleMapper extends BaseMapper { + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * + * @return 角色数据集合信息 + */ + List selectRoleList(SysRole role); + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * + * @return 角色列表 + */ + List selectRolePermissionByUserId(Long userId); + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + List selectRoleAll(); + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * + * @return 选中角色ID列表 + */ + List selectRoleListByUserId(Long userId); + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * + * @return 角色对象信息 + */ + SysRole selectRoleById(Long roleId); + + /** + * 根据用户ID查询角色 + * + * @param userName 用户名 + * + * @return 角色列表 + */ + List selectRolesByUserName(String userName); + + /** + * 校验角色名称是否唯一 + * + * @param roleName 角色名称 + * + * @return 角色信息 + */ + SysRole checkRoleNameUnique(String roleName); + + /** + * 校验角色权限是否唯一 + * + * @param roleKey 角色权限 + * + * @return 角色信息 + */ + SysRole checkRoleKeyUnique(String roleKey); + + /** + * 修改角色信息 + * + * @param role 角色信息 + * + * @return 结果 + */ + int updateRole(SysRole role); + + /** + * 新增角色信息 + * + * @param role 角色信息 + * + * @return 结果 + */ + int insertRole(SysRole role); + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * + * @return 结果 + */ + int deleteRoleById(Long roleId); + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * + * @return 结果 + */ + int deleteRoleByIds(Long[] roleIds); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysRoleMenuMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysRoleMenuMapper.java new file mode 100644 index 0000000..1978e2d --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysRoleMenuMapper.java @@ -0,0 +1,50 @@ +package com.muyu.system.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.system.domain.SysRoleMenu; + +import java.util.List; + +/** + * 角色与菜单关联表 数据层 + * + * @author muyu + */ +public interface SysRoleMenuMapper extends BaseMapper { + /** + * 查询菜单使用数量 + * + * @param menuId 菜单ID + * + * @return 结果 + */ + int checkMenuExistRole(Long menuId); + + /** + * 通过角色ID删除角色和菜单关联 + * + * @param roleId 角色ID + * + * @return 结果 + */ + int deleteRoleMenuByRoleId(Long roleId); + + /** + * 批量删除角色菜单关联信息 + * + * @param ids 需要删除的数据ID + * + * @return 结果 + */ + int deleteRoleMenu(Long[] ids); + + /** + * 批量新增角色菜单信息 + * + * @param roleMenuList 角色菜单列表 + * + * @return 结果 + */ + int batchRoleMenu(List roleMenuList); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysUserMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysUserMapper.java new file mode 100644 index 0000000..90ae439 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysUserMapper.java @@ -0,0 +1,145 @@ +package com.muyu.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.common.system.domain.SysUser; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 用户表 数据层 + * + * @author muyu + */ +public interface SysUserMapper extends BaseMapper { + /** + * 根据条件分页查询用户列表 + * + * @param sysUser 用户信息 + * + * @return 用户信息集合信息 + */ + List selectUserList(SysUser sysUser); + + /** + * 根据条件分页查询已配用户角色列表 + * + * @param user 用户信息 + * + * @return 用户信息集合信息 + */ + List selectAllocatedList(SysUser user); + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * + * @return 用户信息集合信息 + */ + List selectUnallocatedList(SysUser user); + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * + * @return 用户对象信息 + */ + SysUser selectUserByUserName(String userName); + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * + * @return 用户对象信息 + */ + SysUser selectUserById(Long userId); + + /** + * 新增用户信息 + * + * @param user 用户信息 + * + * @return 结果 + */ + int insertUser(SysUser user); + + /** + * 修改用户信息 + * + * @param user 用户信息 + * + * @return 结果 + */ + int updateUser(SysUser user); + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * + * @return 结果 + */ + int updateUserAvatar(@Param("userName") String userName, @Param("avatar") String avatar); + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * + * @return 结果 + */ + int resetUserPwd(@Param("userName") String userName, @Param("password") String password); + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * + * @return 结果 + */ + int deleteUserById(Long userId); + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * + * @return 结果 + */ + int deleteUserByIds(Long[] userIds); + + /** + * 校验用户名称是否唯一 + * + * @param userName 用户名称 + * + * @return 结果 + */ + SysUser checkUserNameUnique(String userName); + + /** + * 校验手机号码是否唯一 + * + * @param phonenumber 手机号码 + * + * @return 结果 + */ + SysUser checkPhoneUnique(String phonenumber); + + /** + * 校验email是否唯一 + * + * @param email 用户邮箱 + * + * @return 结果 + */ + SysUser checkEmailUnique(String email); + + List selectCompanyList(); + +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysUserPostMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysUserPostMapper.java new file mode 100644 index 0000000..db38b47 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysUserPostMapper.java @@ -0,0 +1,49 @@ +package com.muyu.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.system.domain.SysUserPost; + +import java.util.List; + +/** + * 用户与岗位关联表 数据层 + * + * @author muyu + */ +public interface SysUserPostMapper extends BaseMapper { + /** + * 通过用户ID删除用户和岗位关联 + * + * @param userId 用户ID + * + * @return 结果 + */ + int deleteUserPostByUserId(Long userId); + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * + * @return 结果 + */ + int countUserPostById(Long postId); + + /** + * 批量删除用户和岗位关联 + * + * @param ids 需要删除的数据ID + * + * @return 结果 + */ + int deleteUserPost(Long[] ids); + + /** + * 批量新增用户岗位信息 + * + * @param userPostList 用户角色列表 + * + * @return 结果 + */ + int batchUserPost(List userPostList); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysUserRoleMapper.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysUserRoleMapper.java new file mode 100644 index 0000000..c26f5fa --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/mapper/SysUserRoleMapper.java @@ -0,0 +1,69 @@ +package com.muyu.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.system.domain.SysUserRole; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 用户与角色关联表 数据层 + * + * @author muyu + */ +public interface SysUserRoleMapper extends BaseMapper { + /** + * 通过用户ID删除用户和角色关联 + * + * @param userId 用户ID + * + * @return 结果 + */ + int deleteUserRoleByUserId(Long userId); + + /** + * 批量删除用户和角色关联 + * + * @param ids 需要删除的数据ID + * + * @return 结果 + */ + int deleteUserRole(Long[] ids); + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * + * @return 结果 + */ + int countUserRoleByRoleId(Long roleId); + + /** + * 批量新增用户角色信息 + * + * @param userRoleList 用户角色列表 + * + * @return 结果 + */ + int batchUserRole(List userRoleList); + + /** + * 删除用户和角色关联信息 + * + * @param userRole 用户和角色关联信息 + * + * @return 结果 + */ + int deleteUserRoleInfo(SysUserRole userRole); + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要删除的用户数据ID + * + * @return 结果 + */ + int deleteUserRoleInfos(@Param("roleId") Long roleId, @Param("userIds") Long[] userIds); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/rabbit/RabbitTest.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/rabbit/RabbitTest.java new file mode 100644 index 0000000..82c1c25 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/rabbit/RabbitTest.java @@ -0,0 +1,53 @@ +//package com.muyu.system.rabbit; +// +//import com.alibaba.fastjson2.JSONObject; +//import com.muyu.system.domain.SysConfig; +//import jakarta.annotation.PostConstruct; +//import lombok.extern.log4j.Log4j2; +//import org.springframework.amqp.core.Queue; +//import org.springframework.amqp.rabbit.annotation.RabbitHandler; +//import org.springframework.amqp.rabbit.annotation.RabbitListener; +//import org.springframework.amqp.rabbit.core.RabbitTemplate; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.context.annotation.Bean; +//import org.springframework.web.bind.annotation.RequestMapping; +//import org.springframework.web.bind.annotation.RestController; +// +//@Log4j2 +//@RestController +//@RequestMapping("/rabbit/test") +//public class RabbitTest { +// +// @Autowired +// private RabbitTemplate rabbitTemplate; +// +// @Bean +// public Queue initQueue(){ +// return new Queue("rabbit.test.init"); +// } +// +// @RabbitListener(queues = "rabbit.test.init") +// public void msg(SysConfig sysConfig){ +// log.info("消息队列:[{}], 消息内容:[{}]", "rabbit.test.init", JSONObject.toJSONString(sysConfig)); +// } +// +// @PostConstruct +// public void init(){ +// new Thread(() -> { +// try { +// Thread.sleep(5000); +// } catch (InterruptedException e) { +// throw new RuntimeException(e); +// } +// SysConfig sysConfig = SysConfig.builder() +// .configId(1L) +// .configKey("ceshi-key") +// .configName("测试名称") +// .configType("测试类型") +// .configValue("测试值") +// .build(); +// rabbitTemplate.convertAndSend("rabbit.test.init",sysConfig); +// }).start(); +// } +// +//} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysConfigService.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysConfigService.java new file mode 100644 index 0000000..4ebc241 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysConfigService.java @@ -0,0 +1,44 @@ +package com.muyu.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.muyu.system.domain.SysConfig; + +import java.util.List; + +/** + * @author DongZl + * @description: 配置plus业务层 + * @Date 2023-11-13 上午 10:06 + */ +public interface SysConfigService extends IService { + List pageQuery (SysConfig config); + + /** + * 通过Key进行查询值 + * @param configKey + * @return + */ + String selectConfigByKey (String configKey); + + /** + * 检测参数是否唯一 + * @param config + * @return + */ + boolean checkConfigKeyUnique (SysConfig config); + + /** + * 刷新缓存 + */ + void resetConfigCache (); + + /** + * 清空参数缓存数据 + */ + void clearConfigCache(); + + /** + * 加载参数缓存数据 + */ + void loadingConfigCache(); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysDeptService.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysDeptService.java new file mode 100644 index 0000000..e3d0b28 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysDeptService.java @@ -0,0 +1,138 @@ +package com.muyu.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.muyu.common.system.domain.SysDept; +import com.muyu.system.domain.vo.TreeSelect; + +import java.util.List; + +/** + * 部门管理 服务层 + * + * @author muyu + */ +public interface SysDeptService extends IService { + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * + * @return 部门信息集合 + */ + List selectDeptList(SysDept dept); + + /** + * 查询部门树结构信息 + * + * @param dept 部门信息 + * + * @return 部门树信息集合 + */ + List selectDeptTreeList(SysDept dept); + + /** + * 构建前端所需要树结构 + * + * @param depts 部门列表 + * + * @return 树结构列表 + */ + List buildDeptTree(List depts); + + /** + * 构建前端所需要下拉树结构 + * + * @param depts 部门列表 + * + * @return 下拉树结构列表 + */ + List buildDeptTreeSelect(List depts); + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * + * @return 选中部门列表 + */ + List selectDeptListByRoleId(Long roleId); + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * + * @return 部门信息 + */ + SysDept selectDeptById(Long deptId); + + /** + * 根据ID查询所有子部门(正常状态) + * + * @param deptId 部门ID + * + * @return 子部门数 + */ + int selectNormalChildrenDeptById(Long deptId); + + /** + * 是否存在部门子节点 + * + * @param deptId 部门ID + * + * @return 结果 + */ + boolean hasChildByDeptId(Long deptId); + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * + * @return 结果 true 存在 false 不存在 + */ + boolean checkDeptExistUser(Long deptId); + + /** + * 校验部门名称是否唯一 + * + * @param dept 部门信息 + * + * @return 结果 + */ + boolean checkDeptNameUnique(SysDept dept); + + /** + * 校验部门是否有数据权限 + * + * @param deptId 部门id + */ + void checkDeptDataScope(Long deptId); + + /** + * 新增保存部门信息 + * + * @param dept 部门信息 + * + * @return 结果 + */ + int insertDept(SysDept dept); + + /** + * 修改保存部门信息 + * + * @param dept 部门信息 + * + * @return 结果 + */ + int updateDept(SysDept dept); + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * + * @return 结果 + */ + int deleteDeptById(Long deptId); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysDictDataService.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysDictDataService.java new file mode 100644 index 0000000..9fae9bc --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysDictDataService.java @@ -0,0 +1,66 @@ +package com.muyu.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.muyu.common.system.domain.SysDictData; + +import java.util.List; + +/** + * 字典 业务层 + * + * @author muyu + */ +public interface SysDictDataService extends IService { + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * + * @return 字典数据集合信息 + */ + List selectDictDataList(SysDictData dictData); + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * + * @return 字典标签 + */ + String selectDictLabel(String dictType, String dictValue); + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * + * @return 字典数据 + */ + SysDictData selectDictDataById(Long dictCode); + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + */ + void deleteDictDataByIds(Long[] dictCodes); + + /** + * 新增保存字典数据信息 + * + * @param dictData 字典数据信息 + * + * @return 结果 + */ + int insertDictData(SysDictData dictData); + + /** + * 修改保存字典数据信息 + * + * @param dictData 字典数据信息 + * + * @return 结果 + */ + int updateDictData(SysDictData dictData); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysDictTypeService.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysDictTypeService.java new file mode 100644 index 0000000..b78947e --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysDictTypeService.java @@ -0,0 +1,106 @@ +package com.muyu.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.muyu.common.system.domain.SysDictData; +import com.muyu.common.system.domain.SysDictType; + +import java.util.List; + +/** + * 字典 业务层 + * + * @author muyu + */ +public interface SysDictTypeService extends IService { + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * + * @return 字典类型集合信息 + */ + List selectDictTypeList(SysDictType dictType); + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + List selectDictTypeAll(); + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * + * @return 字典数据集合信息 + */ + List selectDictDataByType(String dictType); + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * + * @return 字典类型 + */ + SysDictType selectDictTypeById(Long dictId); + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * + * @return 字典类型 + */ + SysDictType selectDictTypeByType(String dictType); + + /** + * 批量删除字典信息 + * + * @param dictIds 需要删除的字典ID + */ + void deleteDictTypeByIds(Long[] dictIds); + + /** + * 加载字典缓存数据 + */ + void loadingDictCache(); + + /** + * 清空字典缓存数据 + */ + void clearDictCache(); + + /** + * 重置字典缓存数据 + */ + void resetDictCache(); + + /** + * 新增保存字典类型信息 + * + * @param dictType 字典类型信息 + * + * @return 结果 + */ + int insertDictType(SysDictType dictType); + + /** + * 修改保存字典类型信息 + * + * @param dictType 字典类型信息 + * + * @return 结果 + */ + int updateDictType(SysDictType dictType); + + /** + * 校验字典类型称是否唯一 + * + * @param dictType 字典类型 + * + * @return 结果 + */ + boolean checkDictTypeUnique(SysDictType dictType); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysLogininforService.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysLogininforService.java new file mode 100644 index 0000000..a94e129 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysLogininforService.java @@ -0,0 +1,43 @@ +package com.muyu.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.muyu.common.system.domain.SysLogininfor; + +import java.util.List; + +/** + * 系统访问日志情况信息 服务层 + * + * @author muyu + */ +public interface SysLogininforService extends IService { + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + int insertLogininfor(SysLogininfor logininfor); + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * + * @return 登录记录集合 + */ + List selectLogininforList(SysLogininfor logininfor); + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * + * @return 结果 + */ + int deleteLogininforByIds(Long[] infoIds); + + /** + * 清空系统登录日志 + */ + void cleanLogininfor(); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysMenuService.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysMenuService.java new file mode 100644 index 0000000..70b7733 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysMenuService.java @@ -0,0 +1,161 @@ +package com.muyu.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.muyu.system.domain.SysMenu; +import com.muyu.system.domain.vo.RouterVo; +import com.muyu.system.domain.vo.TreeSelect; + +import java.util.List; +import java.util.Set; + +/** + * 菜单 业务层 + * + * @author muyu + */ +public interface SysMenuService extends IService { + /** + * 根据用户查询系统菜单列表 + * + * @param userId 用户ID + * + * @return 菜单列表 + */ + List selectMenuList(Long userId); + + /** + * 根据用户查询系统菜单列表 + * + * @param menu 菜单信息 + * @param userId 用户ID + * + * @return 菜单列表 + */ + List selectMenuList(SysMenu menu, Long userId); + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * + * @return 权限列表 + */ + Set selectMenuPermsByUserId(Long userId); + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * + * @return 权限列表 + */ + Set selectMenuPermsByRoleId(Long roleId); + + /** + * 根据用户ID查询菜单树信息 + * + * @param userId 用户ID + * + * @return 菜单列表 + */ + List selectMenuTreeByUserId(Long userId); + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * + * @return 选中菜单列表 + */ + List selectMenuListByRoleId(Long roleId); + + /** + * 构建前端路由所需要的菜单 + * + * @param menus 菜单列表 + * + * @return 路由列表 + */ + List buildMenus(List menus); + + /** + * 构建前端所需要树结构 + * + * @param menus 菜单列表 + * + * @return 树结构列表 + */ + List buildMenuTree(List menus); + + /** + * 构建前端所需要下拉树结构 + * + * @param menus 菜单列表 + * + * @return 下拉树结构列表 + */ + List buildMenuTreeSelect(List menus); + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * + * @return 菜单信息 + */ + SysMenu selectMenuById(Long menuId); + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * + * @return 结果 true 存在 false 不存在 + */ + boolean hasChildByMenuId(Long menuId); + + /** + * 查询菜单是否存在角色 + * + * @param menuId 菜单ID + * + * @return 结果 true 存在 false 不存在 + */ + boolean checkMenuExistRole(Long menuId); + + /** + * 新增保存菜单信息 + * + * @param menu 菜单信息 + * + * @return 结果 + */ + int insertMenu(SysMenu menu); + + /** + * 修改保存菜单信息 + * + * @param menu 菜单信息 + * + * @return 结果 + */ + int updateMenu(SysMenu menu); + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * + * @return 结果 + */ + int deleteMenuById(Long menuId); + + /** + * 校验菜单名称是否唯一 + * + * @param menu 菜单信息 + * + * @return 结果 + */ + boolean checkMenuNameUnique(SysMenu menu); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysNoticeService.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysNoticeService.java new file mode 100644 index 0000000..a58ad23 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysNoticeService.java @@ -0,0 +1,67 @@ +package com.muyu.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.muyu.system.domain.SysNotice; + +import java.util.List; + +/** + * 公告 服务层 + * + * @author muyu + */ +public interface SysNoticeService extends IService { + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * + * @return 公告信息 + */ + SysNotice selectNoticeById(Long noticeId); + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * + * @return 公告集合 + */ + List selectNoticeList(SysNotice notice); + + /** + * 新增公告 + * + * @param notice 公告信息 + * + * @return 结果 + */ + int insertNotice(SysNotice notice); + + /** + * 修改公告 + * + * @param notice 公告信息 + * + * @return 结果 + */ + int updateNotice(SysNotice notice); + + /** + * 删除公告信息 + * + * @param noticeId 公告ID + * + * @return 结果 + */ + int deleteNoticeById(Long noticeId); + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * + * @return 结果 + */ + int deleteNoticeByIds(Long[] noticeIds); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysOperLogService.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysOperLogService.java new file mode 100644 index 0000000..96d5a7d --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysOperLogService.java @@ -0,0 +1,54 @@ +package com.muyu.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.muyu.common.system.domain.SysOperLog; + +import java.util.List; + +/** + * 操作日志 服务层 + * + * @author muyu + */ +public interface SysOperLogService extends IService { + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + * + * @return 结果 + */ + int insertOperlog(SysOperLog operLog); + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * + * @return 操作日志集合 + */ + List selectOperLogList(SysOperLog operLog); + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * + * @return 结果 + */ + int deleteOperLogByIds(Long[] operIds); + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * + * @return 操作日志对象 + */ + SysOperLog selectOperLogById(Long operId); + + /** + * 清空操作日志 + */ + void cleanOperLog(); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysPermissionService.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysPermissionService.java new file mode 100644 index 0000000..ad49156 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysPermissionService.java @@ -0,0 +1,30 @@ +package com.muyu.system.service; + +import com.muyu.common.system.domain.SysUser; + +import java.util.Set; + +/** + * 权限信息 服务层 + * + * @author muyu + */ +public interface SysPermissionService { + /** + * 获取角色数据权限 + * + * @param userId 用户Id + * + * @return 角色权限信息 + */ + Set getRolePermission(SysUser user); + + /** + * 获取菜单数据权限 + * + * @param userId 用户Id + * + * @return 菜单权限信息 + */ + Set getMenuPermission(SysUser user); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysPostService.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysPostService.java new file mode 100644 index 0000000..0589f0d --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysPostService.java @@ -0,0 +1,110 @@ +package com.muyu.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.muyu.system.domain.SysPost; + +import java.util.List; + +/** + * 岗位信息 服务层 + * + * @author muyu + */ +public interface SysPostService extends IService { + /** + * 查询岗位信息集合 + * + * @param post 岗位信息 + * + * @return 岗位列表 + */ + List selectPostList(SysPost post); + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + List selectPostAll(); + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * + * @return 角色对象信息 + */ + SysPost selectPostById(Long postId); + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * + * @return 选中岗位ID列表 + */ + List selectPostListByUserId(Long userId); + + /** + * 校验岗位名称 + * + * @param post 岗位信息 + * + * @return 结果 + */ + boolean checkPostNameUnique(SysPost post); + + /** + * 校验岗位编码 + * + * @param post 岗位信息 + * + * @return 结果 + */ + boolean checkPostCodeUnique(SysPost post); + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * + * @return 结果 + */ + int countUserPostById(Long postId); + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * + * @return 结果 + */ + int deletePostById(Long postId); + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * + * @return 结果 + */ + int deletePostByIds(Long[] postIds); + + /** + * 新增保存岗位信息 + * + * @param post 岗位信息 + * + * @return 结果 + */ + int insertPost(SysPost post); + + /** + * 修改保存岗位信息 + * + * @param post 岗位信息 + * + * @return 结果 + */ + int updatePost(SysPost post); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysRoleService.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysRoleService.java new file mode 100644 index 0000000..5e2463e --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysRoleService.java @@ -0,0 +1,191 @@ +package com.muyu.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.muyu.common.system.domain.SysRole; +import com.muyu.system.domain.SysUserRole; + +import java.util.List; +import java.util.Set; + +/** + * 角色业务层 + * + * @author muyu + */ +public interface SysRoleService extends IService { + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * + * @return 角色数据集合信息 + */ + List selectRoleList(SysRole role); + + /** + * 根据用户ID查询角色列表 + * + * @param userId 用户ID + * + * @return 角色列表 + */ + List selectRolesByUserId(Long userId); + + /** + * 根据用户ID查询角色权限 + * + * @param userId 用户ID + * + * @return 权限列表 + */ + Set selectRolePermissionByUserId(Long userId); + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + List selectRoleAll(); + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * + * @return 选中角色ID列表 + */ + List selectRoleListByUserId(Long userId); + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * + * @return 角色对象信息 + */ + SysRole selectRoleById(Long roleId); + + /** + * 校验角色名称是否唯一 + * + * @param role 角色信息 + * + * @return 结果 + */ + boolean checkRoleNameUnique(SysRole role); + + /** + * 校验角色权限是否唯一 + * + * @param role 角色信息 + * + * @return 结果 + */ + boolean checkRoleKeyUnique(SysRole role); + + /** + * 校验角色是否允许操作 + * + * @param role 角色信息 + */ + void checkRoleAllowed(SysRole role); + + /** + * 校验角色是否有数据权限 + * + * @param roleId 角色id + */ + void checkRoleDataScope(Long roleId); + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * + * @return 结果 + */ + int countUserRoleByRoleId(Long roleId); + + /** + * 新增保存角色信息 + * + * @param role 角色信息 + * + * @return 结果 + */ + int insertRole(SysRole role); + + /** + * 修改保存角色信息 + * + * @param role 角色信息 + * + * @return 结果 + */ + int updateRole(SysRole role); + + /** + * 修改角色状态 + * + * @param role 角色信息 + * + * @return 结果 + */ + int updateRoleStatus(SysRole role); + + /** + * 修改数据权限信息 + * + * @param role 角色信息 + * + * @return 结果 + */ + int authDataScope(SysRole role); + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * + * @return 结果 + */ + int deleteRoleById(Long roleId); + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * + * @return 结果 + */ + int deleteRoleByIds(Long[] roleIds); + + /** + * 取消授权用户角色 + * + * @param userRole 用户和角色关联信息 + * + * @return 结果 + */ + int deleteAuthUser(SysUserRole userRole); + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要取消授权的用户数据ID + * + * @return 结果 + */ + int deleteAuthUsers(Long roleId, Long[] userIds); + + /** + * 批量选择授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要删除的用户数据ID + * + * @return 结果 + */ + int insertAuthUsers(Long roleId, Long[] userIds); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysUserOnlineService.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysUserOnlineService.java new file mode 100644 index 0000000..4a1f7f3 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysUserOnlineService.java @@ -0,0 +1,52 @@ +package com.muyu.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.muyu.common.system.domain.LoginUser; +import com.muyu.system.domain.SysUserOnline; + +/** + * 在线用户 服务层 + * + * @author muyu + */ +public interface SysUserOnlineService { + /** + * 通过登录地址查询信息 + * + * @param ipaddr 登录地址 + * @param user 用户信息 + * + * @return 在线用户信息 + */ + SysUserOnline selectOnlineByIpaddr(String ipaddr, LoginUser user); + + /** + * 通过用户名称查询信息 + * + * @param userName 用户名称 + * @param user 用户信息 + * + * @return 在线用户信息 + */ + SysUserOnline selectOnlineByUserName(String userName, LoginUser user); + + /** + * 通过登录地址/用户名称查询信息 + * + * @param ipaddr 登录地址 + * @param userName 用户名称 + * @param user 用户信息 + * + * @return 在线用户信息 + */ + SysUserOnline selectOnlineByInfo(String ipaddr, String userName, LoginUser user); + + /** + * 设置在线用户信息 + * + * @param user 用户信息 + * + * @return 在线用户 + */ + SysUserOnline loginUserToUserOnline(LoginUser user); +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysUserService.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysUserService.java new file mode 100644 index 0000000..a36a4f0 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/SysUserService.java @@ -0,0 +1,231 @@ +package com.muyu.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.muyu.common.system.domain.SysUser; + +import java.util.List; + +/** + * 用户 业务层 + * + * @author muyu + */ +public interface SysUserService extends IService { + /** + * 根据条件分页查询用户列表 + * + * @param user 用户信息 + * + * @return 用户信息集合信息 + */ + List selectUserList(SysUser user); + + /** + * 根据条件分页查询已分配用户角色列表 + * + * @param user 用户信息 + * + * @return 用户信息集合信息 + */ + List selectAllocatedList(SysUser user); + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * + * @return 用户信息集合信息 + */ + List selectUnallocatedList(SysUser user); + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * + * @return 用户对象信息 + */ + SysUser selectUserByUserName(String userName); + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * + * @return 用户对象信息 + */ + SysUser selectUserById(Long userId); + + /** + * 根据用户ID查询用户所属角色组 + * + * @param userName 用户名 + * + * @return 结果 + */ + String selectUserRoleGroup(String userName); + + /** + * 根据用户ID查询用户所属岗位组 + * + * @param userName 用户名 + * + * @return 结果 + */ + String selectUserPostGroup(String userName); + + /** + * 校验用户名称是否唯一 + * + * @param user 用户信息 + * + * @return 结果 + */ + boolean checkUserNameUnique(SysUser user); + + /** + * 校验手机号码是否唯一 + * + * @param user 用户信息 + * + * @return 结果 + */ + boolean checkPhoneUnique(SysUser user); + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + * + * @return 结果 + */ + boolean checkEmailUnique(SysUser user); + + /** + * 校验用户是否允许操作 + * + * @param user 用户信息 + */ + void checkUserAllowed(SysUser user); + + /** + * 校验用户是否有数据权限 + * + * @param userId 用户id + */ + void checkUserDataScope(Long userId); + + /** + * 新增用户信息 + * + * @param user 用户信息 + * + * @return 结果 + */ + int insertUser(SysUser user); + + /** + * 注册用户信息 + * + * @param user 用户信息 + * + * @return 结果 + */ + boolean registerUser(SysUser user); + + /** + * 修改用户信息 + * + * @param user 用户信息 + * + * @return 结果 + */ + int updateUser(SysUser user); + + /** + * 用户授权角色 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + void insertUserAuth(Long userId, Long[] roleIds); + + /** + * 修改用户状态 + * + * @param user 用户信息 + * + * @return 结果 + */ + int updateUserStatus(SysUser user); + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * + * @return 结果 + */ + int updateUserProfile(SysUser user); + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * + * @return 结果 + */ + boolean updateUserAvatar(String userName, String avatar); + + /** + * 重置用户密码 + * + * @param user 用户信息 + * + * @return 结果 + */ + int resetPwd(SysUser user); + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * + * @return 结果 + */ + int resetUserPwd(String userName, String password); + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * + * @return 结果 + */ + int deleteUserById(Long userId); + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * + * @return 结果 + */ + int deleteUserByIds(Long[] userIds); + + /** + * 导入用户数据 + * + * @param userList 用户数据列表 + * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据 + * @param operName 操作用户 + * + * @return 结果 + */ + String importUser(List userList, Boolean isUpdateSupport, String operName); + + List selectCompanyList(); + +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysConfigServiceImpl.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysConfigServiceImpl.java new file mode 100644 index 0000000..bc07cc8 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysConfigServiceImpl.java @@ -0,0 +1,114 @@ +package com.muyu.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.muyu.common.core.constant.CacheConstants; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.redis.service.RedisService; +import com.muyu.system.domain.SysConfig; +import com.muyu.system.mapper.SysConfigMapper; +import com.muyu.system.service.SysConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.Assert; + +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Objects; + +/** + * @author DongZl + * @description: 配置plus业务实现层 + * @Date 2023-11-13 上午 10:06 + */ +@Service +public class SysConfigServiceImpl extends ServiceImpl + implements SysConfigService { + + @Autowired + private RedisService redisService; + + @Override + public List pageQuery (SysConfig config) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StringUtils.isNotEmpty(config.getConfigName())){ + queryWrapper.like(SysConfig::getConfigName, config.getConfigName()); + } + if (StringUtils.isNotEmpty(config.getConfigType())){ + queryWrapper.like(SysConfig::getConfigType, config.getConfigType()); + } + if (StringUtils.isNotEmpty(config.getConfigKey())){ + queryWrapper.like(SysConfig::getConfigKey, config.getConfigKey()); + } + Object beginTime = config.getParams().get("beginTime"); + if (Objects.nonNull(beginTime) && beginTime instanceof Date beginDate){ + queryWrapper.gt(SysConfig::getCreateTime, beginDate); + } + Object endTime = config.getParams().get("endTime"); + if (Objects.nonNull(endTime) && endTime instanceof Date endDate){ + queryWrapper.lt(SysConfig::getCreateTime, endDate); + } + return this.list(queryWrapper); + } + + /** + * 通过Key进行查询值 + * + * @param configKey + * + * @return + */ + @Override + public String selectConfigByKey (String configKey) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + Assert.notNull(configKey, "key不可为空"); + queryWrapper.eq(SysConfig::getConfigKey, configKey); + SysConfig sysConfig = this.getOne(queryWrapper); + return sysConfig.getConfigValue(); + } + + @Override + public boolean checkConfigKeyUnique (SysConfig config) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysConfig::getConfigKey, config.getConfigKey()); + return this.count(queryWrapper) > 0; + } + + @Override + public void resetConfigCache () { + this.clearConfigCache(); + this.loadingConfigCache(); + } + + /** + * 清空参数缓存数据 + */ + @Override + public void clearConfigCache () { + Collection keys = redisService.keys(CacheConstants.SYS_CONFIG_KEY + "*"); + redisService.deleteObject(keys); + } + + /** + * 加载参数缓存数据 + */ + @Override + public void loadingConfigCache () { + List configsList = this.list(); + for (SysConfig config : configsList) { + redisService.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue()); + } + } + + /** + * 设置cache key + * + * @param configKey 参数键 + * + * @return 缓存键key + */ + private String getCacheKey (String configKey) { + return CacheConstants.SYS_CONFIG_KEY + configKey; + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysDeptServiceImpl.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysDeptServiceImpl.java new file mode 100644 index 0000000..6775b95 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysDeptServiceImpl.java @@ -0,0 +1,318 @@ +package com.muyu.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.muyu.common.core.constant.UserConstants; +import com.muyu.common.core.exception.ServiceException; +import com.muyu.common.core.text.Convert; +import com.muyu.common.core.utils.SpringUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.datascope.annotation.DataScope; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.SysDept; +import com.muyu.common.system.domain.SysRole; +import com.muyu.common.system.domain.SysUser; +import com.muyu.system.domain.vo.TreeSelect; +import com.muyu.system.mapper.SysDeptMapper; +import com.muyu.system.mapper.SysRoleMapper; +import com.muyu.system.service.SysDeptService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 部门管理 服务实现 + * + * @author muyu + */ +@Service +public class SysDeptServiceImpl extends ServiceImpl implements SysDeptService { + @Autowired + private SysDeptMapper deptMapper; + + @Autowired + private SysRoleMapper roleMapper; + + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * + * @return 部门信息集合 + */ + @Override + @DataScope(deptAlias = "d") + public List selectDeptList (SysDept dept) { + return deptMapper.selectDeptList(dept); + } + + /** + * 查询部门树结构信息 + * + * @param dept 部门信息 + * + * @return 部门树信息集合 + */ + @Override + public List selectDeptTreeList (SysDept dept) { + List depts = SpringUtils.getAopProxy(this).selectDeptList(dept); + return buildDeptTreeSelect(depts); + } + + /** + * 构建前端所需要树结构 + * + * @param depts 部门列表 + * + * @return 树结构列表 + */ + @Override + public List buildDeptTree (List depts) { + List returnList = new ArrayList(); + List 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 buildDeptTreeSelect (List depts) { + List deptTrees = buildDeptTree(depts); + return deptTrees.stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * + * @return 选中部门列表 + */ + @Override + public List 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 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 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 list, SysDept t) { + // 得到子节点列表 + List childList = getChildList(list, t); + t.setChildren(childList); + for (SysDept tChild : childList) { + if (hasChild(list, tChild)) { + recursionFn(list, tChild); + } + } + } + + /** + * 得到子节点列表 + */ + private List getChildList (List list, SysDept t) { + List tlist = new ArrayList(); + Iterator it = list.iterator(); + while (it.hasNext()) { + SysDept n = it.next(); + if (StringUtils.isNotNull(n.getParentId()) && n.getParentId().longValue() == t.getDeptId().longValue()) { + tlist.add(n); + } + } + return tlist; + } + + /** + * 判断是否有子节点 + */ + private boolean hasChild (List list, SysDept t) { + return getChildList(list, t).size() > 0; + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysDictDataServiceImpl.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysDictDataServiceImpl.java new file mode 100644 index 0000000..777ffbd --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysDictDataServiceImpl.java @@ -0,0 +1,108 @@ +package com.muyu.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.muyu.common.security.utils.DictUtils; +import com.muyu.common.system.domain.SysDictData; +import com.muyu.system.mapper.SysDictDataMapper; +import com.muyu.system.service.SysDictDataService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 字典 业务层处理 + * + * @author muyu + */ +@Service +public class SysDictDataServiceImpl extends ServiceImpl implements SysDictDataService { + @Autowired + private SysDictDataMapper dictDataMapper; + + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * + * @return 字典数据集合信息 + */ + @Override + public List 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 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 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 dictDatas = dictDataMapper.selectDictDataByType(data.getDictType()); + DictUtils.setDictCache(data.getDictType(), dictDatas); + } + return row; + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysDictTypeServiceImpl.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysDictTypeServiceImpl.java new file mode 100644 index 0000000..4117411 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysDictTypeServiceImpl.java @@ -0,0 +1,210 @@ +package com.muyu.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.muyu.common.core.constant.UserConstants; +import com.muyu.common.core.exception.ServiceException; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.security.utils.DictUtils; +import com.muyu.common.system.domain.SysDictData; +import com.muyu.common.system.domain.SysDictType; +import com.muyu.system.mapper.SysDictDataMapper; +import com.muyu.system.mapper.SysDictTypeMapper; +import com.muyu.system.service.SysDictTypeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.PostConstruct; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 字典 业务层处理 + * + * @author muyu + */ +@Service +public class SysDictTypeServiceImpl extends ServiceImpl implements SysDictTypeService { + @Autowired + private SysDictTypeMapper dictTypeMapper; + + @Autowired + private SysDictDataMapper dictDataMapper; + + /** + * 项目启动时,初始化字典到缓存 + */ + @PostConstruct + public void init () { + loadingDictCache(); + } + + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * + * @return 字典类型集合信息 + */ + @Override + public List selectDictTypeList (SysDictType dictType) { + return dictTypeMapper.selectDictTypeList(dictType); + } + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + @Override + public List selectDictTypeAll () { + return dictTypeMapper.selectDictTypeAll(); + } + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * + * @return 字典数据集合信息 + */ + @Override + public List selectDictDataByType (String dictType) { + List 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> dictDataMap = dictDataMapper.selectDictDataList(dictData).stream().collect(Collectors.groupingBy(SysDictData::getDictType)); + for (Map.Entry> 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(rollbackFor = Exception.class) + 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 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/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysLogininforServiceImpl.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysLogininforServiceImpl.java new file mode 100644 index 0000000..1a7c7a0 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysLogininforServiceImpl.java @@ -0,0 +1,64 @@ +package com.muyu.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.muyu.common.system.domain.SysLogininfor; +import com.muyu.system.mapper.SysLogininforMapper; +import com.muyu.system.service.SysLogininforService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 系统访问日志情况信息 服务层处理 + * + * @author muyu + */ +@Service +public class SysLogininforServiceImpl extends ServiceImpl implements SysLogininforService { + + @Autowired + private SysLogininforMapper logininforMapper; + + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + @Override + public int insertLogininfor (SysLogininfor logininfor) { + return logininforMapper.insertLogininfor(logininfor); + } + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * + * @return 登录记录集合 + */ + @Override + public List 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/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysMenuServiceImpl.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysMenuServiceImpl.java new file mode 100644 index 0000000..5049f62 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysMenuServiceImpl.java @@ -0,0 +1,501 @@ +package com.muyu.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.muyu.common.core.constant.Constants; +import com.muyu.common.core.constant.UserConstants; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.SysRole; +import com.muyu.common.system.domain.SysUser; +import com.muyu.system.domain.SysMenu; +import com.muyu.system.domain.vo.MetaVo; +import com.muyu.system.domain.vo.RouterVo; +import com.muyu.system.domain.vo.TreeSelect; +import com.muyu.system.mapper.SysMenuMapper; +import com.muyu.system.mapper.SysRoleMapper; +import com.muyu.system.mapper.SysRoleMenuMapper; +import com.muyu.system.service.SysMenuService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 菜单 业务层处理 + * + * @author muyu + */ +@Service +public class SysMenuServiceImpl extends ServiceImpl implements SysMenuService { + 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 selectMenuList (Long userId) { + return selectMenuList(new SysMenu(), userId); + } + + /** + * 查询系统菜单列表 + * + * @param menu 菜单信息 + * + * @return 菜单列表 + */ + @Override + public List selectMenuList (SysMenu menu, Long userId) { + List 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 selectMenuPermsByUserId (Long userId) { + List perms = menuMapper.selectMenuPermsByUserId(userId); + Set 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 selectMenuPermsByRoleId (Long roleId) { + List perms = menuMapper.selectMenuPermsByRoleId(roleId); + Set 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 selectMenuTreeByUserId (Long userId) { + List 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 selectMenuListByRoleId (Long roleId) { + SysRole role = roleMapper.selectRoleById(roleId); + return menuMapper.selectMenuListByRoleId(roleId, role.isMenuCheckStrictly()); + } + + /** + * 构建前端路由所需要的菜单 + * + * @param menus 菜单列表 + * + * @return 路由列表 + */ + @Override + public List buildMenus (List menus) { + List routers = new LinkedList(); + 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( + MetaVo.builder() + .title(menu.getMenuName()) + .icon(menu.getIcon()) + .noCache(StringUtils.equals("1", menu.getIsCache())) + .link(StringUtils.ishttp(menu.getPath())? menu.getPath():null) + .build() + ); + List 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 childrenList = new ArrayList(); + RouterVo children = new RouterVo(); + children.setPath(menu.getPath()); + children.setComponent(menu.getComponent()); + children.setName(StringUtils.capitalize(menu.getPath())); + children.setMeta( + MetaVo.builder() + .title(menu.getMenuName()) + .icon(menu.getIcon()) + .noCache(StringUtils.equals("1", menu.getIsCache())) + .link(StringUtils.ishttp(menu.getPath())? menu.getPath():null) + .build() + ); + 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 childrenList = new ArrayList(); + 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 buildMenuTree (List menus) { + List returnList = new ArrayList(); + List tempList = menus.stream().map(SysMenu::getMenuId).collect(Collectors.toList()); + for (Iterator iterator = menus.iterator() ; iterator.hasNext() ; ) { + SysMenu menu = 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 buildMenuTreeSelect (List menus) { + List 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; + } + + /** + * 获取路由名称 + * + * @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 getChildPerms (List list, int parentId) { + List returnList = new ArrayList(); + for (Iterator iterator = list.iterator() ; iterator.hasNext() ; ) { + SysMenu t = iterator.next(); + // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点 + if (t.getParentId() == parentId) { + recursionFn(list, t); + returnList.add(t); + } + } + return returnList; + } + + /** + * 递归列表 + * + * @param list 分类表 + * @param t 子节点 + */ + private void recursionFn (List list, SysMenu t) { + // 得到子节点列表 + List childList = getChildList(list, t); + t.setChildren(childList); + for (SysMenu tChild : childList) { + if (hasChild(list, tChild)) { + recursionFn(list, tChild); + } + } + } + + /** + * 得到子节点列表 + */ + private List getChildList (List list, SysMenu t) { + List tlist = new ArrayList(); + Iterator it = list.iterator(); + while (it.hasNext()) { + SysMenu n = it.next(); + if (n.getParentId().longValue() == t.getMenuId().longValue()) { + tlist.add(n); + } + } + return tlist; + } + + /** + * 判断是否有子节点 + */ + private boolean hasChild (List 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/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysNoticeServiceImpl.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysNoticeServiceImpl.java new file mode 100644 index 0000000..8c0c8d5 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysNoticeServiceImpl.java @@ -0,0 +1,93 @@ +package com.muyu.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.muyu.system.domain.SysNotice; +import com.muyu.system.mapper.SysNoticeMapper; +import com.muyu.system.service.SysNoticeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 公告 服务层实现 + * + * @author muyu + */ +@Service +public class SysNoticeServiceImpl extends ServiceImpl implements SysNoticeService { + @Autowired + private SysNoticeMapper noticeMapper; + + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * + * @return 公告信息 + */ + @Override + public SysNotice selectNoticeById (Long noticeId) { + return noticeMapper.selectNoticeById(noticeId); + } + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * + * @return 公告集合 + */ + @Override + public List 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/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysOperLogServiceImpl.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysOperLogServiceImpl.java new file mode 100644 index 0000000..24e7d39 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysOperLogServiceImpl.java @@ -0,0 +1,77 @@ +package com.muyu.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.muyu.common.system.domain.SysOperLog; +import com.muyu.system.mapper.SysOperLogMapper; +import com.muyu.system.service.SysOperLogService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 操作日志 服务层处理 + * + * @author muyu + */ +@Service +public class SysOperLogServiceImpl extends ServiceImpl implements SysOperLogService { + @Autowired + private SysOperLogMapper operLogMapper; + + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + * + * @return 结果 + */ + @Override + public int insertOperlog (SysOperLog operLog) { + return operLogMapper.insertOperlog(operLog); + } + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * + * @return 操作日志集合 + */ + @Override + public List selectOperLogList (SysOperLog operLog) { + return operLogMapper.selectOperLogList(operLog); + } + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * + * @return 结果 + */ + @Override + public int deleteOperLogByIds (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(); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysPermissionServiceImpl.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysPermissionServiceImpl.java new file mode 100644 index 0000000..a8641a1 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysPermissionServiceImpl.java @@ -0,0 +1,77 @@ +package com.muyu.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.muyu.common.system.domain.SysRole; +import com.muyu.common.system.domain.SysUser; +import com.muyu.system.service.SysMenuService; +import com.muyu.system.service.SysPermissionService; +import com.muyu.system.service.SysRoleService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * 用户权限处理 + * + * @author muyu + */ +@Service +public class SysPermissionServiceImpl implements SysPermissionService { + @Autowired + private SysRoleService roleService; + + @Autowired + private SysMenuService menuService; + + /** + * 获取角色数据权限 + * + * @param userId 用户Id + * + * @return 角色权限信息 + */ + @Override + public Set getRolePermission (SysUser user) { + Set roles = new HashSet(); + // 管理员拥有所有权限 + if (user.isAdmin()) { + roles.add("admin"); + } else { + roles.addAll(roleService.selectRolePermissionByUserId(user.getUserId())); + } + return roles; + } + + /** + * 获取菜单数据权限 + * + * @param userId 用户Id + * + * @return 菜单权限信息 + */ + @Override + public Set getMenuPermission (SysUser user) { + Set perms = new HashSet(); + // 管理员拥有所有权限 + if (user.isAdmin()) { + perms.add("*:*:*"); + } else { + List roles = user.getRoles(); + if (!CollectionUtils.isEmpty(roles)) { + // 多角色设置permissions属性,以便数据权限匹配权限 + for (SysRole role : roles) { + Set rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId()); + role.setPermissions(rolePerms); + perms.addAll(rolePerms); + } + } else { + perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId())); + } + } + return perms; + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysPostServiceImpl.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysPostServiceImpl.java new file mode 100644 index 0000000..457ac5a --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysPostServiceImpl.java @@ -0,0 +1,174 @@ +package com.muyu.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.muyu.common.core.constant.UserConstants; +import com.muyu.common.core.exception.ServiceException; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.system.domain.SysPost; +import com.muyu.system.mapper.SysPostMapper; +import com.muyu.system.mapper.SysUserPostMapper; +import com.muyu.system.service.SysPostService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 岗位信息 服务层处理 + * + * @author muyu + */ +@Service +public class SysPostServiceImpl extends ServiceImpl implements SysPostService { + @Autowired + private SysPostMapper postMapper; + + @Autowired + private SysUserPostMapper userPostMapper; + + /** + * 查询岗位信息集合 + * + * @param post 岗位信息 + * + * @return 岗位信息集合 + */ + @Override + public List selectPostList (SysPost post) { + return postMapper.selectPostList(post); + } + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + @Override + public List 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 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/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysRoleServiceImpl.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysRoleServiceImpl.java new file mode 100644 index 0000000..fa832da --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysRoleServiceImpl.java @@ -0,0 +1,399 @@ +package com.muyu.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.muyu.common.core.constant.UserConstants; +import com.muyu.common.core.exception.ServiceException; +import com.muyu.common.core.utils.SpringUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.datascope.annotation.DataScope; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.SysRole; +import com.muyu.common.system.domain.SysUser; +import com.muyu.system.domain.SysRoleDept; +import com.muyu.system.domain.SysRoleMenu; +import com.muyu.system.domain.SysUserRole; +import com.muyu.system.mapper.SysRoleDeptMapper; +import com.muyu.system.mapper.SysRoleMapper; +import com.muyu.system.mapper.SysRoleMenuMapper; +import com.muyu.system.mapper.SysUserRoleMapper; +import com.muyu.system.service.SysRoleService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +/** + * 角色 业务层处理 + * + * @author muyu + */ +@Service +public class SysRoleServiceImpl extends ServiceImpl implements SysRoleService { + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysRoleMenuMapper roleMenuMapper; + + @Autowired + private SysUserRoleMapper userRoleMapper; + + @Autowired + private SysRoleDeptMapper roleDeptMapper; + + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * + * @return 角色数据集合信息 + */ + @Override + @DataScope(deptAlias = "d") + public List selectRoleList (SysRole role) { + return roleMapper.selectRoleList(role); + } + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * + * @return 角色列表 + */ + @Override + public List selectRolesByUserId (Long userId) { + List userRoles = roleMapper.selectRolePermissionByUserId(userId); + List 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 selectRolePermissionByUserId (Long userId) { + List perms = roleMapper.selectRolePermissionByUserId(userId); + Set 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 selectRoleAll () { + return SpringUtils.getAopProxy(this).selectRoleList(new SysRole()); + } + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * + * @return 选中角色ID列表 + */ + @Override + public List 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 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(rollbackFor = Exception.class) + public int insertRole (SysRole role) { + // 新增角色信息 + roleMapper.insertRole(role); + return insertRoleMenu(role); + } + + /** + * 修改保存角色信息 + * + * @param role 角色信息 + * + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int updateRole (SysRole role) { + // 修改角色信息 + roleMapper.updateRole(role); + // 删除角色与菜单关联 + roleMenuMapper.deleteRoleMenuByRoleId(role.getRoleId()); + return insertRoleMenu(role); + } + + /** + * 修改角色状态 + * + * @param role 角色信息 + * + * @return 结果 + */ + @Override + public int updateRoleStatus (SysRole role) { + return roleMapper.updateRole(role); + } + + /** + * 修改数据权限信息 + * + * @param role 角色信息 + * + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + 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 list = new ArrayList(); + 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 list = new ArrayList(); + 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(rollbackFor = Exception.class) + public int deleteRoleById (Long roleId) { + // 删除角色与菜单关联 + roleMenuMapper.deleteRoleMenuByRoleId(roleId); + // 删除角色与部门关联 + roleDeptMapper.deleteRoleDeptByRoleId(roleId); + return roleMapper.deleteRoleById(roleId); + } + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteRoleByIds (Long[] roleIds) { + for (Long roleId : roleIds) { + checkRoleAllowed(new SysRole(roleId)); + checkRoleDataScope(roleId); + SysRole role = selectRoleById(roleId); + if (countUserRoleByRoleId(roleId) > 0) { + throw new ServiceException(String.format("%1$s已分配,不能删除", role.getRoleName())); + } + } + // 删除角色与菜单关联 + roleMenuMapper.deleteRoleMenu(roleIds); + // 删除角色与部门关联 + roleDeptMapper.deleteRoleDept(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 list = new ArrayList(); + for (Long userId : userIds) { + SysUserRole ur = new SysUserRole(); + ur.setUserId(userId); + ur.setRoleId(roleId); + list.add(ur); + } + return userRoleMapper.batchUserRole(list); + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysUserOnlineServiceImpl.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysUserOnlineServiceImpl.java new file mode 100644 index 0000000..8ede754 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysUserOnlineServiceImpl.java @@ -0,0 +1,85 @@ +package com.muyu.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.system.domain.LoginUser; +import com.muyu.system.domain.SysUserOnline; +import com.muyu.system.service.SysUserOnlineService; +import org.springframework.stereotype.Service; + +/** + * 在线用户 服务层处理 + * + * @author muyu + */ +@Service +public class SysUserOnlineServiceImpl implements SysUserOnlineService { + /** + * 通过登录地址查询信息 + * + * @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)) { + return null; + } + SysUserOnline sysUserOnline = new SysUserOnline(); + sysUserOnline.setTokenId(user.getToken()); + sysUserOnline.setUserName(user.getUsername()); + sysUserOnline.setIpaddr(user.getIpaddr()); + sysUserOnline.setLoginTime(user.getLoginTime()); + return sysUserOnline; + } +} diff --git a/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysUserServiceImpl.java b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysUserServiceImpl.java new file mode 100644 index 0000000..7bba2ee --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/java/com/muyu/system/service/impl/SysUserServiceImpl.java @@ -0,0 +1,509 @@ +package com.muyu.system.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.muyu.common.core.constant.UserConstants; +import com.muyu.common.core.exception.ServiceException; +import com.muyu.common.core.utils.SpringUtils; +import com.muyu.common.core.utils.StringUtils; +import com.muyu.common.core.utils.bean.BeanValidators; +import com.muyu.common.datascope.annotation.DataScope; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.common.system.domain.SysRole; +import com.muyu.common.system.domain.SysUser; +import com.muyu.system.domain.SysPost; +import com.muyu.system.domain.SysUserPost; +import com.muyu.system.domain.SysUserRole; +import com.muyu.system.mapper.*; +import com.muyu.system.service.SysUserService; +import com.muyu.system.service.SysConfigService; +import jakarta.validation.Validator; +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 java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 用户 业务层处理 + * + * @author muyu + */ +@Service +public class SysUserServiceImpl extends ServiceImpl implements SysUserService { + private static final Logger log = LoggerFactory.getLogger(SysUserServiceImpl.class); + @Autowired + protected Validator validator; + @Autowired + private SysUserMapper userMapper; + @Autowired + private SysRoleMapper roleMapper; + @Autowired + private SysPostMapper postMapper; + @Autowired + private SysUserRoleMapper userRoleMapper; + @Autowired + private SysUserPostMapper userPostMapper; + @Autowired + private SysConfigService configService; + + /** + * 根据条件分页查询用户列表 + * + * @param user 用户信息 + * + * @return 用户信息集合信息 + */ + @Override + @DataScope(deptAlias = "d", userAlias = "u") + public List selectUserList (SysUser user) { + return userMapper.selectUserList(user); + } + + /** + * 根据条件分页查询已分配用户角色列表 + * + * @param user 用户信息 + * + * @return 用户信息集合信息 + */ + @Override + @DataScope(deptAlias = "d", userAlias = "u") + public List selectAllocatedList (SysUser user) { + return userMapper.selectAllocatedList(user); + } + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * + * @return 用户信息集合信息 + */ + @Override + @DataScope(deptAlias = "d", userAlias = "u") + public List 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 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 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 users = SpringUtils.getAopProxy(this).selectUserList(user); + if (StringUtils.isEmpty(users)) { + throw new ServiceException("没有权限访问用户数据!"); + } + } + } + + /** + * 新增保存用户信息 + * + * @param user 用户信息 + * + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int insertUser (SysUser user) { + // 新增用户信息 + int rows = userMapper.insertUser(user); + // 新增用户岗位关联 + insertUserPost(user); + // 新增用户与角色管理 + insertUserRole(user); + return rows; + } + + /** + * 注册用户信息 + * + * @param user 用户信息 + * + * @return 结果 + */ + @Override + public boolean registerUser (SysUser user) { + return userMapper.insertUser(user) > 0; + } + + /** + * 修改保存用户信息 + * + * @param user 用户信息 + * + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int updateUser (SysUser user) { + Long userId = user.getUserId(); + // 删除用户与角色关联 + userRoleMapper.deleteUserRoleByUserId(userId); + // 新增用户与角色管理 + insertUserRole(user); + // 删除用户与岗位关联 + userPostMapper.deleteUserPostByUserId(userId); + // 新增用户与岗位管理 + insertUserPost(user); + return userMapper.updateUser(user); + } + + /** + * 用户授权角色 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + @Override + @Transactional(rollbackFor = Exception.class) + 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 insertUserPost (SysUser user) { + Long[] posts = user.getPostIds(); + if (StringUtils.isNotEmpty(posts)) { + // 新增用户与岗位管理 + List list = new ArrayList(); + 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 list = new ArrayList(); + for (Long roleId : roleIds) { + SysUserRole ur = new SysUserRole(); + ur.setUserId(userId); + ur.setRoleId(roleId); + list.add(ur); + } + userRoleMapper.batchUserRole(list); + } + } + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteUserById (Long userId) { + // 删除用户与角色关联 + userRoleMapper.deleteUserRoleByUserId(userId); + // 删除用户与岗位表 + userPostMapper.deleteUserPostByUserId(userId); + return userMapper.deleteUserById(userId); + } + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteUserByIds (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 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("
" + 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("
" + successNum + "、账号 " + user.getUserName() + " 更新成功"); + } else { + failureNum++; + failureMsg.append("
" + failureNum + "、账号 " + user.getUserName() + " 已存在"); + } + } catch (Exception e) { + failureNum++; + String msg = "
" + 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 selectCompanyList() { + + return userMapper.selectCompanyList(); + } + +} diff --git a/cloud-modules/cloud-modules-system/src/main/resources/banner.txt b/cloud-modules/cloud-modules-system/src/main/resources/banner.txt new file mode 100644 index 0000000..0dd5eee --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/banner.txt @@ -0,0 +1,2 @@ +Spring Boot Version: ${spring-boot.version} +Spring Application Name: ${spring.application.name} diff --git a/cloud-modules/cloud-modules-system/src/main/resources/logback/dev.xml b/cloud-modules/cloud-modules-system/src/main/resources/logback/dev.xml new file mode 100644 index 0000000..d1b5629 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/logback/dev.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/logback/prod.xml b/cloud-modules/cloud-modules-system/src/main/resources/logback/prod.xml new file mode 100644 index 0000000..76a0d8f --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/logback/prod.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + ${log.sky.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + + + ERROR + + ACCEPT + + DENY + + + + + + + + ${log.sky.pattern} + + + + + + + + + + + + + + + + + + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/logback/test.xml b/cloud-modules/cloud-modules-system/src/main/resources/logback/test.xml new file mode 100644 index 0000000..2cd69e4 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/logback/test.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + ${log.sky.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + + + ERROR + + ACCEPT + + DENY + + + + + + + + ${log.sky.pattern} + + + + + + + + + + + + + + + + + + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysConfigMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysConfigMapper.xml new file mode 100644 index 0000000..2c35243 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysConfigMapper.xml @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + select config_id, + config_name, + config_key, + config_value, + config_type, + create_by, + create_time, + update_by, + update_time, + remark + from sys_config + + + + + + + and config_id = #{configId} + + + and config_key = #{configKey} + + + + + + + + + + + + + + insert into sys_config ( + config_name, + config_key, + config_value, + config_type, + create_by, + remark, + create_time + )values( + #{configName}, + #{configKey}, + #{configValue}, + #{configType}, + #{createBy}, + #{remark}, + sysdate() + ) + + + + update sys_config + + config_name = #{configName}, + config_key = #{configKey}, + config_value = #{configValue}, + config_type = #{configType}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = sysdate() + + where config_id = #{configId} + + + + delete + from sys_config + where config_id = #{configId} + + + + delete from sys_config where config_id in + + #{configId} + + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysDeptMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysDeptMapper.xml new file mode 100644 index 0000000..a571d8b --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysDeptMapper.xml @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + insert into sys_dept( + dept_id, + parent_id, + dept_name, + ancestors, + order_num, + leader, + phone, + email, + status, + create_by, + create_time + )values( + #{deptId}, + #{parentId}, + #{deptName}, + #{ancestors}, + #{orderNum}, + #{leader}, + #{phone}, + #{email}, + #{status}, + #{createBy}, + sysdate() + ) + + + + update sys_dept + + parent_id = #{parentId}, + dept_name = #{deptName}, + ancestors = #{ancestors}, + order_num = #{orderNum}, + leader = #{leader}, + phone = #{phone}, + email = #{email}, + status = #{status}, + update_by = #{updateBy}, + update_time = sysdate() + + where dept_id = #{deptId} + + + + update sys_dept set ancestors = + + when #{item.deptId} then #{item.ancestors} + + where dept_id in + + #{item.deptId} + + + + + update sys_dept set status = '0' where dept_id in + + #{deptId} + + + + + update sys_dept + set del_flag = '2' + where dept_id = #{deptId} + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysDictDataMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysDictDataMapper.xml new file mode 100644 index 0000000..5128c99 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysDictDataMapper.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + delete + from sys_dict_data + where dict_code = #{dictCode} + + + + delete from sys_dict_data where dict_code in + + #{dictCode} + + + + + update sys_dict_data + + dict_sort = #{dictSort}, + dict_label = #{dictLabel}, + dict_value = #{dictValue}, + dict_type = #{dictType}, + css_class = #{cssClass}, + list_class = #{listClass}, + is_default = #{isDefault}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where dict_code = #{dictCode} + + + + update sys_dict_data + set dict_type = #{newDictType} + where dict_type = #{oldDictType} + + + + insert into sys_dict_data( + dict_sort, + dict_label, + dict_value, + dict_type, + css_class, + list_class, + is_default, + status, + remark, + create_by, + create_time + )values( + #{dictSort}, + #{dictLabel}, + #{dictValue}, + #{dictType}, + #{cssClass}, + #{listClass}, + #{isDefault}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysDictTypeMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysDictTypeMapper.xml new file mode 100644 index 0000000..7c0ebd1 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysDictTypeMapper.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + select dict_id, dict_name, dict_type, status, create_by, create_time, remark + from sys_dict_type + + + + + + + + + + + + + + delete + from sys_dict_type + where dict_id = #{dictId} + + + + delete from sys_dict_type where dict_id in + + #{dictId} + + + + + update sys_dict_type + + dict_name = #{dictName}, + dict_type = #{dictType}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where dict_id = #{dictId} + + + + insert into sys_dict_type( + dict_name, + dict_type, + status, + remark, + create_by, + create_time + )values( + #{dictName}, + #{dictType}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysLogininforMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysLogininforMapper.xml new file mode 100644 index 0000000..114599c --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysLogininforMapper.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + insert into sys_logininfor (user_name, status, ipaddr, msg, access_time) + values (#{userName}, #{status}, #{ipaddr}, #{msg}, sysdate()) + + + + + + delete from sys_logininfor where info_id in + + #{infoId} + + + + + truncate table sys_logininfor + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysMenuMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysMenuMapper.xml new file mode 100644 index 0000000..1dac5c4 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysMenuMapper.xml @@ -0,0 +1,258 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + update sys_menu + + menu_name = #{menuName}, + parent_id = #{parentId}, + order_num = #{orderNum}, + path = #{path}, + component = #{component}, + `query` = #{query}, + is_frame = #{isFrame}, + is_cache = #{isCache}, + menu_type = #{menuType}, + visible = #{visible}, + status = #{status}, + perms = #{perms}, + icon = #{icon}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where menu_id = #{menuId} + + + + insert into sys_menu( + menu_id, + parent_id, + menu_name, + order_num, + path, + component, + `query`, + is_frame, + is_cache, + menu_type, + visible, + status, + perms, + icon, + remark, + create_by, + create_time + )values( + #{menuId}, + #{parentId}, + #{menuName}, + #{orderNum}, + #{path}, + #{component}, + #{query}, + #{isFrame}, + #{isCache}, + #{menuType}, + #{visible}, + #{status}, + #{perms}, + #{icon}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + delete + from sys_menu + where menu_id = #{menuId} + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysNoticeMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysNoticeMapper.xml new file mode 100644 index 0000000..0687403 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysNoticeMapper.xml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + insert into sys_notice ( + notice_title, + notice_type, + notice_content, + status, + remark, + create_by, + create_time + )values( + #{noticeTitle}, + #{noticeType}, + #{noticeContent}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + update sys_notice + + notice_title = #{noticeTitle}, + notice_type = #{noticeType}, + notice_content = #{noticeContent}, + status = #{status}, + update_by = #{updateBy}, + update_time = sysdate() + + where notice_id = #{noticeId} + + + + delete + from sys_notice + where notice_id = #{noticeId} + + + + delete from sys_notice where notice_id in + + #{noticeId} + + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysOperLogMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysOperLogMapper.xml new file mode 100644 index 0000000..1834ea1 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysOperLogMapper.xml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + select oper_id, + title, + business_type, + method, + request_method, + operator_type, + oper_name, + dept_name, + oper_url, + oper_ip, + oper_param, + json_result, + status, + error_msg, + oper_time, + cost_time + from sys_oper_log + + + + insert into sys_oper_log(title, business_type, method, request_method, operator_type, oper_name, dept_name, + oper_url, oper_ip, oper_param, json_result, status, error_msg, cost_time, oper_time) + values (#{title}, #{businessType}, #{method}, #{requestMethod}, #{operatorType}, #{operName}, #{deptName}, + #{operUrl}, #{operIp}, #{operParam}, #{jsonResult}, #{status}, #{errorMsg}, #{costTime}, sysdate()) + + + + + + delete from sys_oper_log where oper_id in + + #{operId} + + + + + + + truncate table sys_oper_log + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysPostMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysPostMapper.xml new file mode 100644 index 0000000..63142c3 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysPostMapper.xml @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + select post_id, + post_code, + post_name, + post_sort, + status, + create_by, + create_time, + remark + from sys_post + + + + + + + + + + + + + + + + + + update sys_post + + post_code = #{postCode}, + post_name = #{postName}, + post_sort = #{postSort}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where post_id = #{postId} + + + + insert into sys_post( + post_id, + post_code, + post_name, + post_sort, + status, + remark, + create_by, + create_time + )values( + #{postId}, + #{postCode}, + #{postName}, + #{postSort}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + delete + from sys_post + where post_id = #{postId} + + + + delete from sys_post where post_id in + + #{postId} + + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml new file mode 100644 index 0000000..1a9ab92 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + delete + from sys_role_dept + where role_id = #{roleId} + + + + + + delete from sys_role_dept where role_id in + + #{roleId} + + + + + insert into sys_role_dept(role_id, dept_id) values + + (#{item.roleId},#{item.deptId}) + + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysRoleMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysRoleMapper.xml new file mode 100644 index 0000000..f6b56b3 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysRoleMapper.xml @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + 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 + 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 + + + + + + + + + + + + + + + + + + + + insert into sys_role( + role_id, + role_name, + role_key, + role_sort, + data_scope, + menu_check_strictly, + dept_check_strictly, + status, + remark, + create_by, + create_time + )values( + #{roleId}, + #{roleName}, + #{roleKey}, + #{roleSort}, + #{dataScope}, + #{menuCheckStrictly}, + #{deptCheckStrictly}, + #{status}, + #{remark}, + #{createBy}, + sysdate() + ) + + + + update sys_role + + role_name = #{roleName}, + role_key = #{roleKey}, + role_sort = #{roleSort}, + data_scope = #{dataScope}, + menu_check_strictly = #{menuCheckStrictly}, + dept_check_strictly = #{deptCheckStrictly}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = sysdate() + + where role_id = #{roleId} + + + + update sys_role + set del_flag = '2' + where role_id = #{roleId} + + + + update sys_role set del_flag = '2' where role_id in + + #{roleId} + + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml new file mode 100644 index 0000000..22d2609 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + delete + from sys_role_menu + where role_id = #{roleId} + + + + delete from sys_role_menu where role_id in + + #{roleId} + + + + + insert into sys_role_menu(role_id, menu_id) values + + (#{item.roleId},#{item.menuId}) + + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysUserMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysUserMapper.xml new file mode 100644 index 0000000..c23843a --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysUserMapper.xml @@ -0,0 +1,277 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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.database_name, + 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 + + + + + + + + + + + + + + + + + + + + + + insert into sys_user( + user_id, + dept_id, + user_name, + nick_name, + email, + avatar, + phonenumber, + sex, + password, + status, + create_by, + remark, + create_time + )values( + #{userId}, + #{deptId}, + #{userName}, + #{nickName}, + #{email}, + #{avatar}, + #{phonenumber}, + #{sex}, + #{password}, + #{status}, + #{createBy}, + #{remark}, + sysdate() + ) + + + + update sys_user + + dept_id = #{deptId}, + user_name = #{userName}, + nick_name = #{nickName}, + email = #{email}, + phonenumber = #{phonenumber}, + sex = #{sex}, + avatar = #{avatar}, + password = #{password}, + status = #{status}, + login_ip = #{loginIp}, + login_date = #{loginDate}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = sysdate() + + where user_id = #{userId} + + + + update sys_user + set status = #{status} + where user_id = #{userId} + + + + update sys_user + set avatar = #{avatar} + where user_name = #{userName} + + + + update sys_user + set password = #{password} + where user_name = #{userName} + + + + update sys_user + set del_flag = '2' + where user_id = #{userId} + + + + update sys_user set del_flag = '2' where user_id in + + #{userId} + + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysUserPostMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysUserPostMapper.xml new file mode 100644 index 0000000..30e4f06 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysUserPostMapper.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + delete + from sys_user_post + where user_id = #{userId} + + + + + + delete from sys_user_post where user_id in + + #{userId} + + + + + insert into sys_user_post(user_id, post_id) values + + (#{item.userId},#{item.postId}) + + + + diff --git a/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysUserRoleMapper.xml b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysUserRoleMapper.xml new file mode 100644 index 0000000..b93bc72 --- /dev/null +++ b/cloud-modules/cloud-modules-system/src/main/resources/mapper/system/SysUserRoleMapper.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + delete + from sys_user_role + where user_id = #{userId} + + + + + + delete from sys_user_role where user_id in + + #{userId} + + + + + insert into sys_user_role(user_id, role_id) values + + (#{item.userId},#{item.roleId}) + + + + + delete + from sys_user_role + where user_id = #{userId} + and role_id = #{roleId} + + + + delete from sys_user_role where role_id=#{roleId} and user_id in + + #{userId} + + + diff --git a/cloud-modules/cloud-modules-vehiclegateway/pom.xml b/cloud-modules/cloud-modules-vehiclegateway/pom.xml new file mode 100644 index 0000000..6be8747 --- /dev/null +++ b/cloud-modules/cloud-modules-vehiclegateway/pom.xml @@ -0,0 +1,120 @@ + + + 4.0.0 + + com.muyu + cloud-modules + 3.6.3 + + + cloud-modules-vehiclegateway + + + 17 + 17 + UTF-8 + + + + + + + org.springframework.cloud + spring-cloud-starter-bootstrap + 4.1.2 + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + com.mysql + mysql-connector-j + + + + + com.muyu + cloud-common-datasource + + + + + com.muyu + cloud-common-datascope + + + + + com.muyu + cloud-common-log + + + + + com.muyu + cloud-common-api-doc + + + + + com.muyu + cloud-common-rabbit + + + + org.springframework.boot + spring-boot-autoconfigure + + + + + org.eclipse.paho + org.eclipse.paho.client.mqttv3 + 1.2.5 + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + + diff --git a/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/VehicleGatewayApplication.java b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/VehicleGatewayApplication.java new file mode 100644 index 0000000..62108bf --- /dev/null +++ b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/VehicleGatewayApplication.java @@ -0,0 +1,13 @@ +package com.muyu; + +import com.muyu.common.security.annotation.EnableMyFeignClients; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +@EnableMyFeignClients +public class VehicleGatewayApplication { + public static void main(String[] args) { + SpringApplication.run(VehicleGatewayApplication.class,args); + } +} diff --git a/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/vehicle/VehicleInstance.java b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/vehicle/VehicleInstance.java new file mode 100644 index 0000000..c55960e --- /dev/null +++ b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/vehicle/VehicleInstance.java @@ -0,0 +1,132 @@ +package com.muyu.vehicle; + +import com.alibaba.fastjson.JSONObject; +import com.muyu.web.common.ScheduledThreadPool; +import com.muyu.web.domain.MqttProperties; +import com.muyu.web.domain.VehicleInfo; +import com.muyu.web.domain.model.PositionModel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; + +import java.util.Objects; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ScheduledFuture; + +@Data +@Builder +@Log4j2 +@NoArgsConstructor +@AllArgsConstructor +public class VehicleInstance { + /** + * 路线轨迹编码 + */ + private String positionCode; + + /** + * 路径队列 + */ + private LinkedBlockingQueue positionQueue=new LinkedBlockingQueue<>(); + + /** + * 车辆 + */ + private VehicleInfo vehicleInfo; + + /** + * 车辆工作线程 + */ + private VehicleThread vehicleThread; + + /** + * MQTT配置 + */ + private MqttProperties mqttProperties; + + /** + * 线程提交回调 + */ + private ScheduledFuturescheduledFuture; + + /** + * 连接上报 + */ + private MqttClient client; + + + /** + * 获取当前车辆VIN + */ + public String getVin(){ + return this.vehicleInfo.getVin(); + } + /** + * 获取车辆租户信息 + */ + public String getTenantId(){ + return this.vehicleInfo.getTenantId(); + } + + /** + * 发送消息 + */ + public void sengMsg(String msg){ + + } + + + /** + * 初始化客户端 + */ + public void initClient(){ + try { + client = new MqttClient(mqttProperties.getBroker(), mqttProperties.getClientId(), new MemoryPersistence()); + MqttConnectOptions options = new MqttConnectOptions(); + //设置用户名和密码 + if (Objects.nonNull(mqttProperties.getUserName())&&Objects.nonNull(mqttProperties.getPassword())){ + options.setUserName(mqttProperties.getUserName()); + options.setPassword(mqttProperties.getPassword().toCharArray()); + } + options.setConnectionTimeout(1); + options.setKeepAliveInterval(20); + //连接 + client.connect(options); + log.debug("车辆:[{}]客户端初始化成功连接配置:{}",getVin(), + JSONObject.toJSONString(this.mqttProperties) + ); + VehicleThread vehicleThread = new VehicleThread(); + vehicleThread.setVehicleInstance(this); + this.setVehicleThread(vehicleThread); + ScheduledFuture submit = ScheduledThreadPool.submit(vehicleThread); + this.setScheduledFuture(submit); + log.info("初始化车辆上报模拟线程开始:[{}]", this.getVin()); + } catch (MqttException e) { + log.error("车辆:[{}] 客户端初始化异常", getVin(), e); + throw new RuntimeException(e); + } + } + + /** + * 是否连接在线 + */ + public boolean isOnline(){ + if (this.client==null){ + return false; + } + return this.client.isConnected(); + } + + /** + * 是否建立车辆模拟线程 + */ + public boolean isSend(){ + return this.vehicleThread!=null; + } +} diff --git a/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/vehicle/VehicleThread.java b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/vehicle/VehicleThread.java new file mode 100644 index 0000000..79433ba --- /dev/null +++ b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/vehicle/VehicleThread.java @@ -0,0 +1,24 @@ +package com.muyu.vehicle; + +import lombok.Data; +import lombok.extern.log4j.Log4j2; + +@Data +@Log4j2 +public class VehicleThread implements Runnable{ + /** + * 是否停止线程 + */ + private volatile boolean isStop; + + /** + * 车辆实例对象 + */ + private VehicleInstance vehicleInstance; + @Override + public void run() { + if (!isStop){ + log.info("{}-上报数据",this.vehicleInstance.getVin()); + } + } +} diff --git a/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/vehicle/api/ClientAdmin.java b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/vehicle/api/ClientAdmin.java new file mode 100644 index 0000000..fd2f51b --- /dev/null +++ b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/vehicle/api/ClientAdmin.java @@ -0,0 +1,12 @@ +package com.muyu.vehicle.api; + +/** + * 客户端管理 + */ +public interface ClientAdmin { + /** + * 获取车辆负载地址 + */ + + +} diff --git a/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/vehicle/core/LocalContainer.java b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/vehicle/core/LocalContainer.java new file mode 100644 index 0000000..5db7cf5 --- /dev/null +++ b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/vehicle/core/LocalContainer.java @@ -0,0 +1,24 @@ +package com.muyu.vehicle.core; + +import com.muyu.vehicle.VehicleInstance; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class LocalContainer { + private static final Map> tenantVehicleDataMap + =new ConcurrentHashMap<>(); + + /** + * 获取租户ID下车辆 + */ + public static MapgetVehicleDataMap(String tenantId){ + return tenantVehicleDataMap.computeIfAbsent(tenantId,k->new ConcurrentHashMap<>()); + } + + /** + * + */ + + +} diff --git a/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/vehicle/core/VehicleConfiguration.java b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/vehicle/core/VehicleConfiguration.java new file mode 100644 index 0000000..0d3b1ff --- /dev/null +++ b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/vehicle/core/VehicleConfiguration.java @@ -0,0 +1,13 @@ +package com.muyu.vehicle.core; + +import lombok.AllArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.context.annotation.Configuration; + +@Log4j2 +@Configuration +@AllArgsConstructor +public class VehicleConfiguration { +} diff --git a/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/common/ScheduledThreadPool.java b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/common/ScheduledThreadPool.java new file mode 100644 index 0000000..ab4c0a7 --- /dev/null +++ b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/common/ScheduledThreadPool.java @@ -0,0 +1,33 @@ +package com.muyu.web.common; + +import net.sf.jsqlparser.statement.select.KSQLWindow; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +public class ScheduledThreadPool { + /** + * 周期线程数 CPU*2+1 + */ + private static final ScheduledExecutorService scheduledThreadPool= Executors.newScheduledThreadPool( + Runtime.getRuntime().availableProcessors()*2+1 + ); + + public static ScheduledFuturesubmit(Runnable thread){ + // 参数分别是: 任务, 多久后开始执行, 每隔多久执行一次(周期),时间单位 + return submit(thread, 1); + } + + public static ScheduledFuturesubmit(Runnable thread,long period){ + return scheduledThreadPool.scheduleAtFixedRate(thread,0,period, TimeUnit.SECONDS); + } + + /** + * 关闭线程池 + */ + public static void shutdown(){ + scheduledThreadPool.shutdown(); + } +} diff --git a/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/controller/testController.java b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/controller/testController.java new file mode 100644 index 0000000..6c5bd44 --- /dev/null +++ b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/controller/testController.java @@ -0,0 +1,40 @@ +package com.muyu.web.controller; + +import com.muyu.common.core.utils.uuid.UUID; +import org.eclipse.paho.client.mqttv3.*; + +public class testController { + public static void main(String[] args) { + String broker = "tcp://47.101.53.251:1883"; + String clientId = "SX-"+ UUID.randomUUID().toString(); + MqttClient client; + + try { + client = new MqttClient(broker, clientId); + MqttConnectOptions connectOptions = new MqttConnectOptions(); + connectOptions.setCleanSession(true); + System.out.println("Connect to broker:"+broker); + client.connect(connectOptions); + System.out.println("Connected"); + client.setCallback(new MqttCallback() { + @Override + public void connectionLost(Throwable throwable) { + System.out.println("连接丢失"); + } + + @Override + public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception { + System.out.println("消息到达:"+new String(mqttMessage.getPayload())+topic); + } + + @Override + public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) { + + } + }); + } catch (MqttException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/domain/MqttProperties.java b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/domain/MqttProperties.java new file mode 100644 index 0000000..03e468c --- /dev/null +++ b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/domain/MqttProperties.java @@ -0,0 +1,42 @@ +package com.muyu.web.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * mqtt配置类 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class MqttProperties { + /** + * 节点 + */ + private String broker; + + /** + * 主题 + */ + private String topic; + /** + * 用户名 + */ + private String userName; + + /** + * 密码 + */ + private String password; + + /** + * 节点ID + */ + private String clientId; + /** + * 上报级别 + */ + private int qos=0; + +} diff --git a/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/domain/ServerConfig.java b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/domain/ServerConfig.java new file mode 100644 index 0000000..f480f5d --- /dev/null +++ b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/domain/ServerConfig.java @@ -0,0 +1,76 @@ +package com.muyu.web.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.muyu.web.domain.model.ServerConfigModel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.function.Supplier; + +/** + * 服务器配置类 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@TableName(value = "server_config") +public class ServerConfig { + + /** + * 主键 + */ + @TableId(value = "id",type = IdType.AUTO) + private Long id; + + + /** + * 主机地址 + */ + private String host; + + /** + * 端口号 + */ + private String port; + + /** + * 负载地址 + */ + private String url; + + /** + * 默认MOTT地址 + */ + private String defaultMqttAddr; + + /** + * 默认MQTT主题 + */ + private String defaultMqttTopic; + + /** + * 默认MOTT交付级别 + */ + private Integer defaultMqttQos; + + + public static ServerConfig modeBuild(ServerConfigModel serverConfigModel, Supplier idKey){ + return builder() + .id(idKey.get()) + .host(serverConfigModel.getHost()) + .port(serverConfigModel.getPort()) + .url(serverConfigModel.getUrl()) + .defaultMqttAddr(serverConfigModel.getDefaultMqttAddr()) + .defaultMqttTopic(serverConfigModel.getDefaultMqttTopic()) + .defaultMqttQos(serverConfigModel.getDefaultMqttQos()) + .build(); + + + + } +} diff --git a/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/domain/VehicleInfo.java b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/domain/VehicleInfo.java new file mode 100644 index 0000000..b717527 --- /dev/null +++ b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/domain/VehicleInfo.java @@ -0,0 +1,92 @@ +package com.muyu.web.domain; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.function.Supplier; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@TableName("vehicle_info") +public class VehicleInfo { + + /** + * 主键 + */ + @TableId( + value = "id", + type = IdType.AUTO + ) + private Long id; + /** + * VIN + */ + private String vin; + /** + * VIN + */ + @TableField(value = "tenant_id", fill = FieldFill.INSERT) + private String tenantId; + + /** + * 报文模板 + */ + @TableField("message_template_id") + private Long messageTemplateId; + + /** + * 电池剩余电量 + */ + @TableField("remaining_battery") + private BigDecimal remainingBattery; + + /** + * 电池电量 + */ + @TableField("battery_level") + private BigDecimal batteryLevel; + + /** + * 上一次经度 + */ + @TableField("last_longitude") + private String lastLongitude; + + /** + * 上一次维度 + */ + @TableField("last_latitude") + private String lastLatitude; + + /** + * 总里程 + */ + @TableField("total_mileage") + private BigDecimal totalMileage; + + /** + * 创建时间 + */ + @TableField(value = "create_time", fill = FieldFill.INSERT) + private Date createTime; + + + public static VehicleInfo create(String vin, SuppliermessageTemplateId){ + return VehicleInfo.builder() + .vin(vin) + .messageTemplateId(messageTemplateId.get()) + .createTime(new Date()) + .totalMileage(BigDecimal.ZERO) + .build(); + } + + + +} diff --git a/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/domain/model/MqttServerModel.java b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/domain/model/MqttServerModel.java new file mode 100644 index 0000000..8e56869 --- /dev/null +++ b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/domain/model/MqttServerModel.java @@ -0,0 +1,37 @@ +package com.muyu.web.domain.model; + + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + Mqtt服务器模型 + */ + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class MqttServerModel { + private static final Logger log=LoggerFactory.getLogger(MqttServerModel.class); + + /** + * MQTT服务节点 + */ + private String broker; + + /** + * MQTT订阅主题 + */ + private String topic; + + public String getBroker(){ + log.info("broker:{}",broker); + return broker.contains("tcp://")?broker:"tcp://"+broker+":1883"; + } + + +} + diff --git a/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/domain/model/PositionModel.java b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/domain/model/PositionModel.java new file mode 100644 index 0000000..7a9e920 --- /dev/null +++ b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/domain/model/PositionModel.java @@ -0,0 +1,33 @@ +package com.muyu.web.domain.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @description: 位置模型 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PositionModel { + /** + * 经度 + */ + private String longitude; + + /** + * 维度 + */ + private String latitude; + + public static PositionModel strBuild (String positionStr) { + String[] split = positionStr.split(","); + return PositionModel.builder() + .longitude(split[0]) + .latitude(split[1]) + .build(); + } +} diff --git a/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/domain/model/ServerConfigModel.java b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/domain/model/ServerConfigModel.java new file mode 100644 index 0000000..3418155 --- /dev/null +++ b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/domain/model/ServerConfigModel.java @@ -0,0 +1,53 @@ +package com.muyu.web.domain.model; + +import com.muyu.web.domain.ServerConfig; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ServerConfigModel { + /** + * 主机地址 + */ + private String host; + + /** + * 端口号 + */ + private String port; + /** + * 负载地址 + */ + private String url; + + /** + * 默认MOTT地址 + */ + private String defaultMqttAddr; + + /** + * 默认MQTT主题 + */ + private String defaultMqttTopic; + + /** + * 默认MOTT交付级别 + */ + private Integer defaultMqttQos; + + public static ServerConfigModel serverConfigModelBuild(ServerConfig serverConfig){ + return builder() + .host(serverConfig.getHost().trim()) + .port(serverConfig.getPort()) + .url(serverConfig.getUrl().trim()) + .defaultMqttAddr(serverConfig.getDefaultMqttAddr().trim()) + .defaultMqttTopic(serverConfig.getDefaultMqttTopic().trim()) + .defaultMqttQos(serverConfig.getDefaultMqttQos()) + .build(); + } +} diff --git a/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/domain/req/VehicleConnectionReq.java b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/domain/req/VehicleConnectionReq.java new file mode 100644 index 0000000..9368d2d --- /dev/null +++ b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/domain/req/VehicleConnectionReq.java @@ -0,0 +1,34 @@ +package com.muyu.web.domain.req; + +import com.alibaba.fastjson.annotation.JSONField; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class VehicleConnectionReq { + + @JSONField(name = "vin") + private String vin; + + /** + * 时间戳 + */ + private String timestamp; + + + /** + * 用户名 + */ + @JSONField(name = "username") + private String userName; + + /** + * 随机字符串 + */ + private String nonce; +} diff --git a/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/service/VehicleInstanceService.java b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/service/VehicleInstanceService.java new file mode 100644 index 0000000..0e4d702 --- /dev/null +++ b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/service/VehicleInstanceService.java @@ -0,0 +1,12 @@ +package com.muyu.web.service; + +public interface VehicleInstanceService { + + + /** + * 车辆客户端初始化 + */ + void vehicleClientStart(String vin); + + +} diff --git a/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/service/impl/VehicleInstanceServiceImpl.java b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/service/impl/VehicleInstanceServiceImpl.java new file mode 100644 index 0000000..13a408c --- /dev/null +++ b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/service/impl/VehicleInstanceServiceImpl.java @@ -0,0 +1,25 @@ +package com.muyu.web.service.impl; + +import com.muyu.common.core.utils.uuid.UUID; +import com.muyu.web.domain.req.VehicleConnectionReq; +import com.muyu.web.service.VehicleInstanceService; +import com.muyu.web.utils.MD5Util; +import lombok.extern.log4j.Log4j2; +import org.springframework.stereotype.Service; + +@Log4j2 +@Service +public class VehicleInstanceServiceImpl implements VehicleInstanceService { + @Override + public void vehicleClientStart(String vin) { + log.info("车辆{},开始上线",vin); + String timestamp = String.valueOf(System.currentTimeMillis()); + VehicleConnectionReq.builder() + .vin(vin) + .timestamp(timestamp) + .userName(MD5Util.encrypted(vin+timestamp)) + .nonce(MD5Util.encrypted(UUID.randomUUID().toString().replace("-",""))) + .build(); + // + } +} diff --git a/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/utils/MD5Util.java b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/utils/MD5Util.java new file mode 100644 index 0000000..7668abb --- /dev/null +++ b/cloud-modules/cloud-modules-vehiclegateway/src/main/java/com/muyu/web/utils/MD5Util.java @@ -0,0 +1,52 @@ +package com.muyu.web.utils; + +import lombok.extern.log4j.Log4j2; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +@Log4j2 +public class MD5Util { + private static final Integer SALT_LENGTH = 12; + + /** + * 指定数组转化为16进制字符串 + */ + public static String byteToHexString(byte[]b){ + StringBuilder stringBuilder = new StringBuilder(); + for (byte value : b) { + String hex = Integer.toHexString(value & 0xFF); + if (hex.length()==1){ + hex='0'+hex; + } + stringBuilder.append(hex.toUpperCase()); + } + return stringBuilder.toString(); + } + + /** + * 获得加密后的口令 + */ + public static String encrypted(String str){ + try { + byte[]pwd=null; + SecureRandom random = new SecureRandom(); + byte[] salt = new byte[SALT_LENGTH]; + random.nextBytes(salt); + MessageDigest md=null; + md = MessageDigest.getInstance("MD5"); + md.update(salt); + md.update(str.getBytes(StandardCharsets.UTF_8)); + byte[] digest = md.digest(); + pwd=new byte[digest.length+SALT_LENGTH]; + System.arraycopy(salt,0,pwd,0,SALT_LENGTH); + System.arraycopy(digest,0,pwd,SALT_LENGTH,digest.length); + return byteToHexString(pwd); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/cloud-modules/cloud-modules-vehiclegateway/src/main/resources/banner.txt b/cloud-modules/cloud-modules-vehiclegateway/src/main/resources/banner.txt new file mode 100644 index 0000000..0dd5eee --- /dev/null +++ b/cloud-modules/cloud-modules-vehiclegateway/src/main/resources/banner.txt @@ -0,0 +1,2 @@ +Spring Boot Version: ${spring-boot.version} +Spring Application Name: ${spring.application.name} diff --git a/cloud-modules/cloud-modules-vehiclegateway/src/main/resources/logback/dev.xml b/cloud-modules/cloud-modules-vehiclegateway/src/main/resources/logback/dev.xml new file mode 100644 index 0000000..621579c --- /dev/null +++ b/cloud-modules/cloud-modules-vehiclegateway/src/main/resources/logback/dev.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + + + diff --git a/cloud-modules/cloud-modules-vehiclegateway/src/main/resources/logback/prod.xml b/cloud-modules/cloud-modules-vehiclegateway/src/main/resources/logback/prod.xml new file mode 100644 index 0000000..2419bf9 --- /dev/null +++ b/cloud-modules/cloud-modules-vehiclegateway/src/main/resources/logback/prod.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + ${log.sky.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + + + ERROR + + ACCEPT + + DENY + + + + + + + + ${log.sky.pattern} + + + + + + + + + + + + + + + + + + + + diff --git a/cloud-modules/cloud-modules-vehiclegateway/src/main/resources/logback/test.xml b/cloud-modules/cloud-modules-vehiclegateway/src/main/resources/logback/test.xml new file mode 100644 index 0000000..2419bf9 --- /dev/null +++ b/cloud-modules/cloud-modules-vehiclegateway/src/main/resources/logback/test.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + ${log.sky.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + + + ERROR + + ACCEPT + + DENY + + + + + + + + ${log.sky.pattern} + + + + + + + + + + + + + + + + + + + + diff --git a/cloud-modules/cloud-modules-wechat/pom.xml b/cloud-modules/cloud-modules-wechat/pom.xml new file mode 100644 index 0000000..8d94920 --- /dev/null +++ b/cloud-modules/cloud-modules-wechat/pom.xml @@ -0,0 +1,59 @@ + + + 4.0.0 + + + com.muyu + cloud-modules + 3.6.3 + + cloud-modules-wechat + + + 17 + 17 + UTF-8 + + + + + + com.squareup.okhttp3 + okhttp + 4.9.3 + + + com.thoughtworks.xstream + xstream + 1.4.20 + + + org.projectlombok + lombok + 1.18.34 + + + org.springframework.boot + spring-boot-starter + 3.3.2 + + + org.springframework.boot + spring-boot-starter-web + 3.3.2 + + + + org.dom4j + dom4j + 2.1.3 + + + com.alibaba.fastjson2 + fastjson2 + 2.0.43 + + + diff --git a/cloud-modules/cloud-modules-wechat/src/main/java/com/muyu/wechat/CloudWeChatApplication.java b/cloud-modules/cloud-modules-wechat/src/main/java/com/muyu/wechat/CloudWeChatApplication.java new file mode 100644 index 0000000..61c9d69 --- /dev/null +++ b/cloud-modules/cloud-modules-wechat/src/main/java/com/muyu/wechat/CloudWeChatApplication.java @@ -0,0 +1,18 @@ +package com.muyu.wechat; + + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @ClassDescription: + * @JdkVersion: 1.8 + * @Author: YZL + * @Created: 2024/9/19 14:26 + */ +@SpringBootApplication +public class CloudWeChatApplication { + public static void main (String[] args) { + SpringApplication.run(CloudWeChatApplication.class, args); + } +} diff --git a/cloud-modules/cloud-modules-wechat/src/main/java/com/muyu/wechat/controller/WxTestController.java b/cloud-modules/cloud-modules-wechat/src/main/java/com/muyu/wechat/controller/WxTestController.java new file mode 100644 index 0000000..ab32460 --- /dev/null +++ b/cloud-modules/cloud-modules-wechat/src/main/java/com/muyu/wechat/controller/WxTestController.java @@ -0,0 +1,155 @@ +package com.muyu.wechat.controller; + + +import com.muyu.wechat.domain.Message; +import com.muyu.wechat.message.Article; +import com.muyu.wechat.message.NewMessage; +import com.muyu.wechat.util.TokenUtil; +import com.thoughtworks.xstream.XStream; +import io.micrometer.common.util.StringUtils; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.http.HttpServletRequest; +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.Element; +import org.dom4j.io.SAXReader; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.io.IOException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.*; + +/** + * @ClassDescription: + * @JdkVersion: 1.8 + * @Author: YZL + * @Created: 2024/9/19 20:26 + */ +@RestController +public class WxTestController { + + + @PostMapping("/") + public String receiveMessage(HttpServletRequest request) { + ServletInputStream inputStream = null; + HashMap map = new HashMap<>(); + try { + inputStream = request.getInputStream(); + SAXReader saxReader = new SAXReader(); + //读取request输入流,获得Document对象 + Document document = saxReader.read(inputStream); + //获得root结点 + Element rootElement = document.getRootElement(); + //获取所有的子节点 + List elements = rootElement.elements(); + for (Element element : elements) { + map.put(element.getName(), element.getStringValue()); + } + + + } catch (IOException e) { + throw new RuntimeException(e); + } catch (DocumentException e) { + throw new RuntimeException(e); + } + System.out.println(map); + //回复消息 + String message = ""; + if ("图文".equals(map.get("Content"))) { + message = getReplyNewsMessage(map); + } else { + message = getReplyMessage(map); + + } + return message; + } + + /** + * 获得回复的消息内容 + * + * @param map + * @return + */ + private String getReplyMessage(HashMap map) { + Message message = new Message(); + message.setToUserName(map.get("FromUserName")); + message.setFromUserName(map.get("ToUserName")); +// message.setMsgType(map.get("MsgType")); + message.setMsgType("text"); + message.setCreateTime(System.currentTimeMillis() / 1000); + message.setContent("官方回复:您好"); + //XStream将java对象转换为xml字符串 + XStream xStream = new XStream(); + xStream.processAnnotations(Message.class); + String xml = xStream.toXML(message); + return xml; + } + + + @GetMapping("/") + public String check(@RequestParam("signature") String signature, @RequestParam("timestamp") String timestamp, + @RequestParam("nonce") String nonce, @RequestParam("echostr") String echostr) { + + //1.将token、timestamp、nonce三个参数进行字典排序 + String token = "asdasd"; + List list = Arrays.asList(token, timestamp, nonce); + //排序 + Collections.sort(list); + //2.将三个参数字符串拼接成一个字符串进行sha1加密 + StringBuilder stringBuilder = new StringBuilder(); + for (String s : list) { + stringBuilder.append(s); + } + //加密 + try { + MessageDigest instance = MessageDigest.getInstance("sha1"); + //使用sha1进行加密,获得byte数组 + byte[] digest = instance.digest(stringBuilder.toString().getBytes()); + StringBuilder sum = new StringBuilder(); + for (byte b : digest) { + sum.append(Integer.toHexString((b >> 4) & 15)); + sum.append(Integer.toHexString(b & 15)); + } + //3.开发者获得加密后的字符串可与signature对比,标识该请求来源于微信 + if (!StringUtils.isEmpty(signature) && signature.contentEquals(sum)) { + return echostr; + } + + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + return null; + } + + @PostMapping("/test") + public void test() { + String accessToken = TokenUtil.getAccessToken(); + System.out.println(accessToken); + } + + private String getReplyNewsMessage(Map map) { + NewMessage newsMessage = new NewMessage(); + newsMessage.setToUserName(map.get("FromUserName")); + newsMessage.setFromUserName(map.get("ToUserName")); + newsMessage.setMsgType("news"); + newsMessage.setCreateTime(System.currentTimeMillis() / 1000); + newsMessage.setArticleCount(1); + List
articles = new ArrayList<>(); + Article article = new Article(); + article.setTitle("oneone哦恩恩"); + article.setDescription("详细描述--------------信息"); + article.setUrl("https://www.baidu.com"); + article.setPicUrl("http://mmbiz.qpic.cn/mmbiz_jpg/RiaWwmABEMmkFKQMeQZLLYaxknlzE9CxSozVSH42iaXiaQcia5hPPUicuNYbS8dG99zsMZ1ic266ialM42Mbn8SkN54kA/0"); + articles.add(article); + newsMessage.setArticles(articles); + //XStream将java对象转换为xml字符串 + XStream xStream = new XStream(); + xStream.processAnnotations(NewMessage.class); + String xml = xStream.toXML(newsMessage); + return xml; + } +} diff --git a/cloud-modules/cloud-modules-wechat/src/main/java/com/muyu/wechat/domain/Message.java b/cloud-modules/cloud-modules-wechat/src/main/java/com/muyu/wechat/domain/Message.java new file mode 100644 index 0000000..069961b --- /dev/null +++ b/cloud-modules/cloud-modules-wechat/src/main/java/com/muyu/wechat/domain/Message.java @@ -0,0 +1,29 @@ +package com.muyu.wechat.domain; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; + +/** + * @ClassDescription: + * @JdkVersion: 1.8 + * @Author: YZL + * @Created: 2024/9/19 20:26 + */ +@Data +@XStreamAlias("xml") +public class Message { + @XStreamAlias("ToUserName") + private String toUserName; + + @XStreamAlias("FromUserName") + private String fromUserName; + + @XStreamAlias("CreateTime") + private Long createTime; + + @XStreamAlias("MsgType") + private String msgType; + + @XStreamAlias("Content") + private String content; +} diff --git a/cloud-modules/cloud-modules-wechat/src/main/java/com/muyu/wechat/message/Article.java b/cloud-modules/cloud-modules-wechat/src/main/java/com/muyu/wechat/message/Article.java new file mode 100644 index 0000000..873df00 --- /dev/null +++ b/cloud-modules/cloud-modules-wechat/src/main/java/com/muyu/wechat/message/Article.java @@ -0,0 +1,28 @@ +package com.muyu.wechat.message; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @ClassDescription: + * @JdkVersion: 1.8 + * @Author: YZL + * @Created: 2024/9/19 20:26 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@XStreamAlias("item") +public class Article { + @XStreamAlias("Title") + private String title; + @XStreamAlias("Description") + private String description; + @XStreamAlias("PicUrl") + private String picUrl; + @XStreamAlias("Url") + private String url; + +} diff --git a/cloud-modules/cloud-modules-wechat/src/main/java/com/muyu/wechat/message/NewMessage.java b/cloud-modules/cloud-modules-wechat/src/main/java/com/muyu/wechat/message/NewMessage.java new file mode 100644 index 0000000..9b49422 --- /dev/null +++ b/cloud-modules/cloud-modules-wechat/src/main/java/com/muyu/wechat/message/NewMessage.java @@ -0,0 +1,34 @@ +package com.muyu.wechat.message; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @ClassDescription: + * @JdkVersion: 1.8 + * @Author: YZL + * @Created: 2024/9/19 20:26 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@XStreamAlias("xml") +public class NewMessage { + @XStreamAlias("ToUserName") + private String toUserName; + @XStreamAlias("FromUserName") + private String fromUserName; + @XStreamAlias("CreateTime") + private long createTime; + @XStreamAlias("MsgType") + private String msgType; + @XStreamAlias("ArticleCount") + private int articleCount; + @XStreamAlias("Articles") + private List
articles; + +} diff --git a/cloud-modules/cloud-modules-wechat/src/main/java/com/muyu/wechat/util/OkHttpUtils.java b/cloud-modules/cloud-modules-wechat/src/main/java/com/muyu/wechat/util/OkHttpUtils.java new file mode 100644 index 0000000..f330aa8 --- /dev/null +++ b/cloud-modules/cloud-modules-wechat/src/main/java/com/muyu/wechat/util/OkHttpUtils.java @@ -0,0 +1,41 @@ +package com.muyu.wechat.util; + +import okhttp3.*; + +import java.io.IOException; +/** + * @ClassDescription: + * @JdkVersion: 1.8 + * @Author: YZL + * @Created: 2024/9/19 20:26 + */ +public class OkHttpUtils { + + private static final OkHttpClient client = new OkHttpClient(); + + public static String sendGetRequest(String urlString) { + Request request = new Request.Builder() + .url(urlString) + .build(); + try (Response response = client.newCall(request).execute()) { + return response.body().string(); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + public static String sendPostRequest(String urlString, String params) { + RequestBody requestBody = RequestBody.create(params, MediaType.parse("application/json; charset=utf-8")); + Request request = new Request.Builder() + .url(urlString) + .post(requestBody) + .build(); + try (Response response = client.newCall(request).execute()) { + return response.body().string(); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } +} diff --git a/cloud-modules/cloud-modules-wechat/src/main/java/com/muyu/wechat/util/TokenUtil.java b/cloud-modules/cloud-modules-wechat/src/main/java/com/muyu/wechat/util/TokenUtil.java new file mode 100644 index 0000000..a90a422 --- /dev/null +++ b/cloud-modules/cloud-modules-wechat/src/main/java/com/muyu/wechat/util/TokenUtil.java @@ -0,0 +1,40 @@ +package com.muyu.wechat.util; + +import com.alibaba.fastjson2.JSON; + +import com.muyu.wechat.domain.AccessToken; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +/** + * @ClassDescription: + * @JdkVersion: 1.8 + * @Author: YZL + * @Created: 2024/9/19 20:26 + */ +@Component +public class TokenUtil { + private final static String APP_ID = "wx1d843111e24945c4"; + + private final static String APP_SECRET = "f625fc9e03e6bb5f4959b51f0650f1a8"; + + private static final AccessToken accessToken = new AccessToken(); + + + public static void getToken() { + String url = String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", APP_ID, APP_SECRET); + String request = OkHttpUtils.sendGetRequest(url); + AccessToken wechatToken = JSON.parseObject(request, AccessToken.class); + if (wechatToken != null) { + accessToken.setExpiresTime(wechatToken.getExpires_in()); + accessToken.setAccess_token(wechatToken.getAccess_token()); + } + } + + public static String getAccessToken() { + getToken(); + return accessToken.getAccess_token(); + } + +} diff --git a/cloud-modules/pom.xml b/cloud-modules/pom.xml new file mode 100644 index 0000000..d504b42 --- /dev/null +++ b/cloud-modules/pom.xml @@ -0,0 +1,35 @@ + + + + com.muyu + cloud-server + 3.6.3 + + 4.0.0 + + + cloud-modules-system + cloud-modules-gen + + cloud-modules-file + + + cloud-modules-wechat + + + + + + saas + cloud-modules-vehiclegateway + + + cloud-modules + pom + + + cloud-modules业务模块 + + + diff --git a/cloud-modules/saas/pom.xml b/cloud-modules/saas/pom.xml new file mode 100644 index 0000000..83f4ea5 --- /dev/null +++ b/cloud-modules/saas/pom.xml @@ -0,0 +1,25 @@ + + + 4.0.0 + + com.muyu + cloud-modules + 3.6.3 + + + saas + pom + + saas-common + saas-server + + + + 17 + 17 + UTF-8 + + + diff --git a/cloud-modules/saas/saas-common/pom.xml b/cloud-modules/saas/saas-common/pom.xml new file mode 100644 index 0000000..bda0d95 --- /dev/null +++ b/cloud-modules/saas/saas-common/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + com.muyu + saas + 3.6.3 + + + com.muyu.common + saas-common + + + 17 + 17 + UTF-8 + + + + saas-common公共模块 + + + + + com.muyu + cloud-common-core + + + io.swagger.core.v3 + swagger-annotations + 2.2.8 + + + + diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/CarType.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/CarType.java new file mode 100644 index 0000000..52ea7a0 --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/CarType.java @@ -0,0 +1,18 @@ +package com.muyu.common.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@TableName(value = "car_type",autoResultMap = true) +public class CarType { + @TableId(value = "id") + private Long id; + private String typeName; + private Long templateId; +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/DataType.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/DataType.java new file mode 100644 index 0000000..b70a583 --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/DataType.java @@ -0,0 +1,37 @@ +package com.muyu.common.domain; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; +import java.io.Serializable; +/** + * @Author:liuxinyue + * @Package:com.sheep.message.domain + * @Project:cloud-server-c + * @name:DataType + * @Date:2024/9/19 19:32 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@SuperBuilder +@Tag(name = "数据类型表") +@TableName(value = "data_type",autoResultMap = true) +public class DataType implements Serializable{ + + /** + * 数据类型ID + */ + @TableId(value = "data_type_id",type = IdType.AUTO) + private Integer DataTypeId; + /** + * 数据类型名称 + */ + private String DataTypeName; + +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/Enterprise.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/Enterprise.java new file mode 100644 index 0000000..d02f6ab --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/Enterprise.java @@ -0,0 +1,33 @@ +package com.muyu.common.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @Author YuPing + * @Description 企业运营实体类 + * @Version 1.0 + * @Data 2024-09-26 20:22:04 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Enterprise { + /** + * 企业编号 + */ + private Integer enterpriseId; + /** + * 企业名称 + */ + private String enterpriseName; + /** + * 企业车辆数量 + */ + private Integer enterpriseCarCount; + /** + * 企业电子围栏数量 + */ + private Integer enterpriseFenceCount; +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/MessageTemplate.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/MessageTemplate.java new file mode 100644 index 0000000..b14c894 --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/MessageTemplate.java @@ -0,0 +1,136 @@ +package com.muyu.common.domain; + +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.sql.Date; + +/** + * @Author:liuxinyue + * @Package:com.template.domain + * @Project:cloud-server + * @name:MessageTemplate + * @Date:2024/9/21 12:09 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@SuperBuilder +@Tag(name = "报文") +public class MessageTemplate { + + /** + * 主键 + */ + private Integer messageId; + /** + * 车辆VIN码 + */ + private String vinCode; + /** + * 时间戳 + */ + private Date timeStamp; + /** + * 经度 + */ + private String longItude; + /** + * 纬度 + */ + private String latitude; + /** + * 车速 + */ + private String speedVehicle; + /** + * 总里程 + */ + private String totalMileage; + /** + * 总电压 + */ + private String totalVoltage; + /** + * 总电流 + */ + private String combinedCurrent; + /** + * 绝缘电阻 + */ + private String insulationResistance; + /** + * 档位 + */ + private String gearPosition; + /** + * 加速踏板行程值 + */ + private String acceleratorPedalTravelValue; + /** + * 制动踏板行程值 + */ + private String brakePedalTravelValue; + /** + * 燃料消耗率 + */ + private String specificFuelConsumption; + /** + * 电机控制器温度 + */ + private String motorControllerTemperature; + /** + * 电机转速 + */ + private String motorSpeed; + /** + * 电机转矩 + */ + private String motorTorque; + /** + * 电机温度 + */ + private String motorTemperature; + /** + * 电机电压 + */ + private String motorVoltage; + /** + * 电机电流 + */ + private String motorCurrent; + private String powerBatteryRemainingSoc; + private String maximumFeedbackPower; + private String maximumDischargePower; + private String bmsSelfCheckCounter; + private String powerBattery; + private String totalVoltageLoadSide; + private String maximumVoltage; + private String minimumVoltage; + private String maximumTemperature; + private String minimumTemperature; + private String availableCapacity; + private String vehicleStatus; + private String chargingState; + private String operationalStatus; + private String soc; + private String energyStorageDevices; + private String driveMotorCondition; + private String whetherWorks; + private String eas; + private String ptc; + private String eps; + private String abs; + private String mcu; + private String heatingState; + private String powerBatteryStatus; + private String stateBatteryInsulation; + private String dcdc; + private String chg; + private String checkDigit; + private String cutoffBit; + +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/MessageTemplateType.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/MessageTemplateType.java new file mode 100644 index 0000000..6d84684 --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/MessageTemplateType.java @@ -0,0 +1,73 @@ +package com.muyu.common.domain; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.io.Serializable; + +/** + * @Author:liuxinyue + * @Package:com.sheep.message.domain + * @Project:cloud-server-c + * @name:MessageTemplateType + * @Date:2024/9/18 21:01 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@SuperBuilder +@Tag(name = "报文模版表") +@TableName(value = "message_template_type",autoResultMap = true) +public class MessageTemplateType implements Serializable { + + /** + * 主键 + */ + @TableId(value = "message_template_type_id",type = IdType.AUTO) + private String messageTemplateTypeId; + /** + * 报文类别 + */ + private String messageClass; + /** + * 编码 + */ + private String messageCode; + /** + *标签 + */ + private String messageField; + /** + *起始位 + */ + private Integer startIndex; + /** + *终止位 + */ + private Integer endIndex; + /** + *数据类型ID + */ + private Integer dataTypeId; + /** + * 数据类型名称 + */ + private String dataTypeName; + /** + *最小值 + */ + private String fixedValue; + /** + *最大值 + */ + private String rangeValue; + /** + * 模版ID + */ + private Integer templateId; +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/SysCar.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/SysCar.java new file mode 100644 index 0000000..9041490 --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/SysCar.java @@ -0,0 +1,30 @@ +package com.muyu.common.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.muyu.common.core.web.domain.BaseEntity; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +@TableName(value = "sys_car",autoResultMap = true) +public class SysCar extends BaseEntity { + @TableId(value = "id",type = IdType.AUTO) + private Long id; + private String carVin; + private Long carTypeId; + private String state; + private String carMotorManufacturer; + private String carMotorModel; + private String carBatteryManufacturer; + private String carBatteryModel; + private Long strategyId; + private Long groupId; + +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/SysCarEnterprise.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/SysCarEnterprise.java new file mode 100644 index 0000000..3cc0014 --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/SysCarEnterprise.java @@ -0,0 +1,59 @@ +package com.muyu.common.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.core.web.domain.BaseEntity; +import lombok.*; + +/** + * 企业信息 sys_car_enterprise + * @author YuanZiLong + * @package com.muyu.breakdown.domain + * @name: SysCarEnterprise + * @date: 2024/9/26 19:54 + */ + +@Data +@Setter +@Getter +@NoArgsConstructor +@AllArgsConstructor +@TableName("sys_car_enterprise") +public class SysCarEnterprise extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** 企业id*/ + @TableId(type = IdType.AUTO) + private Long id; + + /** 企业名称*/ + @Excel(name = "企业名称") + private String enterpriseName; + /** 用户姓名 */ + @Excel(name = "用户姓名") + private String name; + + @Excel(name = "用户职位") + private String position; + + @Excel(name = "公司所在省") + private String province; + + @Excel(name = "公司所在市") + private String city; + + @Excel(name = "公司所在县/区") + private String county; + + @Excel(name = "公司详细地址") + private String address; + + @Excel(name = "统一社会信用代码") + private String creditCode; + + @Excel(name = "营业执照") + private String businessLicense; + +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/SysCarFault.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/SysCarFault.java new file mode 100644 index 0000000..af996e5 --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/SysCarFault.java @@ -0,0 +1,116 @@ +package com.muyu.common.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.core.web.domain.BaseEntity; +import lombok.*; +import lombok.experimental.SuperBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 车辆故障管理对象 sys_car_fault + * @author YuanZiLong + * @package: com.muyu.breakdown.domain + * @name: SysCarFault + * @date: 2024/9/20 10:56 + */ +@Data +@Setter +@Getter +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@TableName("sys_car_fault") +public class SysCarFault extends BaseEntity{ + private static final long serialVersionUID = 1L; + + /** 参数主键 */ + @TableId( type = IdType.AUTO) + private Long id; + + + /** 故障码编码 */ + @Excel(name = "故障码编码") + private String faultCode; + + /** 故障名 */ + @Excel(name = "故障名") + private String faultName; + + /** 故障类型 */ + @Excel(name = "故障类型") + private Long typeId; + + + + /** 故障标签 */ + @Excel(name = "故障标签") + private String faultLabel; + + + + /** 故障位 */ + @Excel(name = "故障位") + private String faultBit; + + /** 故障值 */ + @Excel(name = "故障值") + private String faultValue; + + /** 故障级别 (0.低 ,2.中 ,3.高) */ + @Excel(name = "故障级别 (0.低 ,2.中 ,3.高)") + private Long faultRank; + + /** 故障描述信息 */ + @Excel(name = "故障描述信息") + private String faultDesc; + + /** 故障最小阈值 */ + @Excel(name = "故障最小阈值") + private Integer faultMinThreshold; + + /** 故障最大阈值 */ + @Excel(name = "故障最大阈值") + private Integer faultMaxThreshold; + + /** 启用状态(1.待处理 2.处理中 3.已处理 4.忽略) */ + @Excel(name = "启用状态(1.待处理 2.处理中 3.已处理 4.忽略)") + private Integer status; + + /** 是否警告(0.开启 1.禁止) */ + @Excel(name = "是否警告(0.开启 1.禁止)") + private Integer warnStatus; + + /**车辆类型 */ + @Excel(name = "车辆类型") + private Integer carTypeId; + + + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("faultCode", getFaultCode()) + .append("faultName", getFaultName()) + .append("typeId", getTypeId()) + .append("faultLabel", getFaultLabel()) + .append("faultBit", getFaultBit()) + .append("faultValue", getFaultValue()) + .append("faultRank", getFaultRank()) + .append("faultDesc", getFaultDesc()) + .append("faultMinThreshold", getFaultMinThreshold()) + .append("faultMaxThreshold", getFaultMaxThreshold()) + .append("status", getStatus()) + .append("warnStatus", getWarnStatus()) + .append("remark", getRemark()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .toString(); + } +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/SysCarFaultLog.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/SysCarFaultLog.java new file mode 100644 index 0000000..ad4ccc0 --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/SysCarFaultLog.java @@ -0,0 +1,58 @@ +package com.muyu.common.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.core.web.domain.BaseEntity; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import java.util.Date; + +/** + * 故障记录对象 sys_car_fault_log + * @author YuanZiLong + * @package: com.muyu.breakdown.domain + * @name: SysCarFaultLog + * @date: 2024/9/22 20:17 + */ +@Data +@Setter +@Getter +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@TableName("sys_car_fault_log") +public class SysCarFaultLog extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** 参数主键 */ + @TableId( type = IdType.AUTO) + private Long id; + + /** 故障码编码 */ + + private Integer sysCarFaultId; + @Excel(name = "故障码编码") + private String faultCode; + + /**记录时间*/ + @Excel(name = "记录时间") + private Date createTime; + + /** 结束时间*/ + @Excel(name = "结束时间") + private Date updateTime; + + + /** VIN码 */ + @Excel(name = "VIN码") + private String vin; + + /** 处理状态 1-解决 2-处理中 3-忽略 */ + @Excel(name = "处理状态 1-解决 2-处理中 3-忽略") + private Integer status; + + +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/SysCarFaultMessage.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/SysCarFaultMessage.java new file mode 100644 index 0000000..fff86b4 --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/SysCarFaultMessage.java @@ -0,0 +1,42 @@ +package com.muyu.common.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.muyu.common.core.web.domain.BaseEntity; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import java.util.Date; + +/** + * 站内信息对象 sys_car_fault_message + * @author YuanZiLong + * @package: com.muyu.breakdown.domain + * @name: SysCarFaultMessage + * @date: 2024/9/22 11:57 + */ +@Data +@Setter +@Getter +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@TableName("sys_car_fault_message") +public class SysCarFaultMessage extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** 参数主键 */ + @TableId( type = IdType.AUTO) + private Long id; + + /** 内容 */ + private String content; + + /**开始时间 */ + private Date createTime; + + /**是否已读 */ + private Integer status; + +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/SysCarLog.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/SysCarLog.java new file mode 100644 index 0000000..4d13240 --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/SysCarLog.java @@ -0,0 +1,25 @@ +package com.muyu.common.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@TableName(value = "sys_car_log",autoResultMap = true) +public class SysCarLog { + @TableId(value = "id",type = IdType.AUTO) + private String id; + private String carVin; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date startTime; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date endTime; +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/Template.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/Template.java new file mode 100644 index 0000000..58f9c2d --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/Template.java @@ -0,0 +1,50 @@ +package com.muyu.common.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.Date; + +/** + * @Author:liuxinyue + * @Package:com.template.domain + * @Project:cloud-server-c + * @name:Template + * @Date:2024/9/20 12:04 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@SuperBuilder +@Tag(name = "报文模版") +@TableName(value = "t_template",autoResultMap = true) +public class Template { + + /** + * 模版ID + */ + @TableId(value = "template_id",type = IdType.AUTO) + private Integer templateId; + /** + * 租户ID + */ + private Integer houseId; + /** + * 模版名称 + */ + private String templateName; + /** + * 模版描述 + */ + private String templateDescribe; + /** + * 创建时间 + */ + private Date createTime; +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/WarnLogs.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/WarnLogs.java new file mode 100644 index 0000000..6127a4c --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/WarnLogs.java @@ -0,0 +1,72 @@ +package com.muyu.common.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.muyu.common.core.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; + +/** + * 预警日志对象 warn_logs + * + * @author muyu + * @date 2024-09-20 + */ + +@Data +@NoArgsConstructor +@AllArgsConstructor +@TableName("warn_logs") +public class WarnLogs{ + private static final long serialVersionUID = 1L; + + /** 预警日志id */ + @TableId( type = IdType.AUTO) + private Long id; + + /** 车辆vin码 */ + @Excel(name = "车辆vin码") + private String vin; + + /** 规则id */ + @Excel(name = "规则id") + private Long warnRuleId; + + /** 开始时间 */ + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "开始时间", width = 30, dateFormat = "yyyy-MM-dd") + private Date startTime; + + /** 结束时间 */ + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "结束时间", width = 30, dateFormat = "yyyy-MM-dd") + private Date endTime; + + /** 最大值 */ + @Excel(name = "最大值") + private Long maxValue; + + /** 最小值 */ + @Excel(name = "最小值") + private Long minValue; + + /** 平均值 */ + @Excel(name = "平均值") + private Long avgValue; + + /** 中位数 */ + @Excel(name = "中位数") + private Long medianValue; + + /** 是否发送预警 */ + @Excel(name = "是否发送预警") + private Long status; + + + +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/WarnRule.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/WarnRule.java new file mode 100644 index 0000000..aa0005d --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/WarnRule.java @@ -0,0 +1,62 @@ +package com.muyu.common.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.muyu.common.core.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 预警规则对象 warn_rule + * + * @author muyu + * @date 2024-09-20 + */ + +@Data +@NoArgsConstructor +@AllArgsConstructor +@TableName("warn_rule") +public class WarnRule { + private static final long serialVersionUID = 1L; + + /** 规则id */ + @TableId( type = IdType.AUTO) + private Long id; + + /** 规则名称 */ + @Excel(name = "规则名称") + private String ruleName; + + /** 策略id */ + @Excel(name = "策略id") + private Long strategyId; + + /** 报文数据类型id */ + @Excel(name = "报文数据类型id") + private Long msgTypeId; + + /** 滑窗时间 */ + @Excel(name = "滑窗时间") + private Long slideTime; + + /** 滑窗频率 */ + @Excel(name = "滑窗频率") + private Long slideFrequency; + + /** 上升率 */ + @Excel(name = "上升率") + private Long growthRate; + + /** 波动率 */ + @Excel(name = "波动率") + private Long volatilityRate; + + /** 下降率 */ + @Excel(name = "下降率") + private Long decreaseRate; + + +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/WarnStrategy.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/WarnStrategy.java new file mode 100644 index 0000000..fc29da5 --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/WarnStrategy.java @@ -0,0 +1,44 @@ +package com.muyu.common.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.muyu.common.core.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 预警策略对象 warn_strategy + * + * @author muyu + * @date 2024-09-20 + */ + +@Data +@NoArgsConstructor +@AllArgsConstructor +@TableName("warn_strategy") +public class WarnStrategy { + private static final long serialVersionUID = 1L; + + /** 策略id */ + @TableId( type = IdType.AUTO) + private Long id; + + /** 车辆类型id */ + @Excel(name = "车辆类型id") + private Long carTypeId; + + /** 策略名称 */ + @Excel(name = "策略名称") + private String strategyName; + + /** 报文模版id */ + @Excel(name = "报文模版id") + private Long templateId; + + + + +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/database/ElectronicFence.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/database/ElectronicFence.java new file mode 100644 index 0000000..1d1e476 --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/database/ElectronicFence.java @@ -0,0 +1,123 @@ +package com.muyu.common.domain.database; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.muyu.common.core.web.domain.BaseEntity; +import com.muyu.common.domain.req.ElectroicFenceAddReq; +import com.muyu.common.domain.req.ElectroicFenceUpdReq; +import com.muyu.common.domain.resp.ElectronicFenceResp; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.function.Supplier; + +/** + * @Author:yuping + * @Package:com.muyu.fence.domain + * @Project:cloud-server + * @name:ElectronicFence + * @Date:2024/9/17 16:34 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +@SuperBuilder +@TableName(value = "electronic_fence",autoResultMap = true) +public class ElectronicFence extends BaseEntity { + + /** + * 主键id + */ + @TableId(value = "id",type = IdType.AUTO) + private Long id; + + /** + * 围栏名称 + */ + + private String name; + + /** + * 围栏类型 + */ + + private String fenceType; + + /** + *经纬度信息 + */ + + private String longitudeLatitude; + + /** + * 电子围栏状态(正常,停用) + */ + + private String status; + +// /** +// * 电子围栏的开始时间 +// */ +// +// private Date startTime; +// /** +// * 电子围栏的结束时间 +// */ +// private Date endTime; + + /** + * 描述信息 + */ + + private String fenceDesc; + +public static ElectronicFenceResp bullerResp(ElectronicFence electronicFence){ + + return ElectronicFenceResp.builder() + .id(electronicFence.getId()) + .name(electronicFence.getName()) + .status(electronicFence.getStatus()) + .fenceType(electronicFence.getFenceType()) + .longitudeLatitude(electronicFence.getLongitudeLatitude()) + .desc(electronicFence.getFenceDesc()) + .build(); +} + + +public static ElectronicFence buildElectroicAdd(ElectroicFenceAddReq electroicFenceAddReq){ + + return ElectronicFence.builder() + .name(electroicFenceAddReq.getName()) + .fenceDesc(electroicFenceAddReq.getFenceDesc()) + .status(electroicFenceAddReq.getStatus()) + .longitudeLatitude(electroicFenceAddReq.getLongitudeLatitude()) + .fenceType(electroicFenceAddReq.getFenceType()) + .build(); + + +} + +public static ElectronicFence buildByElectronicUpd(ElectroicFenceUpdReq electroicFenceUpdReq, Supplier longSupplier){ + + return ElectronicFence.builder() + .id(longSupplier.get()) + .name(electroicFenceUpdReq.getName()) + .status(electroicFenceUpdReq.getStatus()) + .fenceDesc(electroicFenceUpdReq.getFenceDesc()) + .longitudeLatitude(electroicFenceUpdReq.getLongitudeLatitude()) + .build(); + +} + + + + + + + +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/database/ElectronicFenceGroup.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/database/ElectronicFenceGroup.java new file mode 100644 index 0000000..17b2f28 --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/database/ElectronicFenceGroup.java @@ -0,0 +1,114 @@ +package com.muyu.common.domain.database; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.muyu.common.core.web.domain.BaseEntity; +import com.muyu.common.domain.req.ElectronicFenceGroupAddReq; +import com.muyu.common.domain.req.ElectronicFenceGroupUpdReq; +import com.muyu.common.domain.resp.ElectronicFenceGroupResp; +import com.muyu.common.domain.resp.GroupFenceListresp; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.function.Supplier; + +/** + * @Author:yuping + * @Package:com.muyu.fence.domain + * @Project:cloud-server + * @name:ElectronicFenceGroup + * @Date:2024/9/18 11:14 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@SuperBuilder +@TableName(value = "electronic_fence_group",autoResultMap = true) +public class ElectronicFenceGroup extends BaseEntity { + + /** + * 主键 + */ + @TableId(value = "id",type = IdType.AUTO) + private Long id; + /** + * 围栏组优先级 + */ + private Integer priority; + /** + * 围栏组名称 + */ + private String groupName; + /** + * 围栏组类型 + */ + private String groupType; + + /** + * 启用状态 + */ + private String status; + + + public static GroupFenceListresp buildGroupFence(ElectronicFenceGroup electronicFenceGroup){ + + return GroupFenceListresp.builder() + .id(electronicFenceGroup.getId()) + .groupName(electronicFenceGroup.groupName) + .priority(electronicFenceGroup.getPriority()) + .status(electronicFenceGroup.getStatus()) + .groupType(electronicFenceGroup.groupType) + .build(); + + } + + + + + public static ElectronicFenceGroupResp buildElectronicFenceGroupResp (ElectronicFenceGroup electronicFenceGroup){ + + return ElectronicFenceGroupResp.builder() + .id(electronicFenceGroup.getId()) + .groupName(electronicFenceGroup.groupName) + .priority(electronicFenceGroup.getPriority()) + .status(electronicFenceGroup.getStatus()) + .groupType(electronicFenceGroup.groupType) + .build(); + + } + + public static ElectronicFenceGroup buildByAdd(ElectronicFenceGroupAddReq addReq){ + + + return ElectronicFenceGroup.builder() + .groupName(addReq.getGroupName()) + .groupType(addReq.getGroupType()) + .status(addReq.getStatus()) + .priority(addReq.getPriority()) + .build(); + + } + + public static ElectronicFenceGroup buildByUpd(ElectronicFenceGroupUpdReq updReq, Supplier ids){ + + + return ElectronicFenceGroup.builder() + .id(ids.get()) + .groupName(updReq.getGroupName()) + .groupType(updReq.getGroupType()) + .status(updReq.getStatus()) + .priority(updReq.getPriority()) + .build(); + + } + + + + + + + +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/database/FenceGroupMid.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/database/FenceGroupMid.java new file mode 100644 index 0000000..e668aac --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/database/FenceGroupMid.java @@ -0,0 +1,32 @@ +package com.muyu.common.domain.database; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @Author:yuping + * @Package:com.muyu.fence.domain + * @Project:cloud-server + * @name:FenceGroupMid + * @Date:2024/9/19 21:01 + */ +@Tag(name = "围栏组连接表") +@Data +@AllArgsConstructor +@NoArgsConstructor +@TableName(value = "fence_group_mid",autoResultMap = true) +public class FenceGroupMid { + + /** + * 中间表的自增 + */ + @TableId(value = "id",type = IdType.AUTO) + private Long id; + private Long groupId; + private Long fenceId; +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/ElectroicFenceAddReq.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/ElectroicFenceAddReq.java new file mode 100644 index 0000000..a2fecad --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/ElectroicFenceAddReq.java @@ -0,0 +1,74 @@ +package com.muyu.common.domain.req; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; + +/** + * @Author:yuping + * @Package:com.muyu.fence.domain.req + * @Project:cloud-server + * @name:ElectroicAdd + * @Date:2024/9/17 20:53 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ElectroicFenceAddReq { + + /** + * 主键id + */ + @TableId(value = "id",type = IdType.AUTO) + private Long id; + + /** + * 围栏名称 + */ + + private String name; + + /** + * 围栏类型(驶入,驶出) + */ + + private String fenceType; + + /** + *经纬度信息 + */ + + private String longitudeLatitude; + + /** + * 电子围栏状态(正常,停用) + */ + + private String status; + + /** + * 描述信息 + */ + + private String fenceDesc; + + /** + * 电子围栏的开始时间 + */ + + private Date startTime; + /** + * 电子围栏的结束时间 + */ + private Date endTime; + + + + +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/ElectroicFenceListReq.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/ElectroicFenceListReq.java new file mode 100644 index 0000000..5265c85 --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/ElectroicFenceListReq.java @@ -0,0 +1,65 @@ +package com.muyu.common.domain.req; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; + +/** + * @Author:yuping + * @Package:com.muyu.fence.domain.req + * @Project:cloud-server + * @name:ElectroicFenceReq + * @Date:2024/9/17 20:04 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ElectroicFenceListReq { + + /** + * 围栏名称 + */ + + private String name; + + /** + * 围栏类型(驶入,驶出) + */ + + private String fenceType; + + /** + *经纬度信息 + */ + + private String longitudeLatitude; + + /** + * 电子围栏状态(正常,停用) + */ + + private String status; + + + + /** + * 电子围栏的开始时间 + */ + + private Date startTime; + /** + * 电子围栏的结束时间 + */ + private Date endTime; + + + + + + + +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/ElectroicFenceUpdReq.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/ElectroicFenceUpdReq.java new file mode 100644 index 0000000..f0369c7 --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/ElectroicFenceUpdReq.java @@ -0,0 +1,70 @@ +package com.muyu.common.domain.req; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @Author:yuping + * @Package:com.muyu.fence.domain.req + * @Project:cloud-server + * @name:ElectroicFenceUpdReq + * @Date:2024/9/17 21:03 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ElectroicFenceUpdReq { + + + /** + * 主键id + */ + + private Long id; + + /** + * 围栏名称 + */ + + private String name; + + /** + * 围栏类型(驶入,驶出) + */ + + private String fenceType; + + /** + *经纬度信息 + */ + + private String longitudeLatitude; + + /** + * 电子围栏状态(正常,停用) + */ + + private String status; + + /** + * 描述信息 + */ + + private String fenceDesc; + + + + + + + + + + + + + +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/ElectronicFenceGroupAddReq.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/ElectronicFenceGroupAddReq.java new file mode 100644 index 0000000..dc32d20 --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/ElectronicFenceGroupAddReq.java @@ -0,0 +1,60 @@ +package com.muyu.common.domain.req; + +import com.muyu.common.domain.resp.ElectronicFenceResp; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @Author:yuping + * @Package:com.muyu.fence.domain.req + * @Project:cloud-server + * @name:ElectronicFenceGroupAddReq + * @Date:2024/9/20 19:14 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ElectronicFenceGroupAddReq { + + + + /** + * 围栏组优先级 + */ + private Integer priority; + /** + * 围栏组名称 + */ + private String groupName; + /** + * 围栏组类型 + */ + private String groupType; + + /** + * 启用状态 + */ + private String status; + + /** + * 选中围栏 + */ + + List electronicFenceRespList; + + + + + + + + + + + +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/ElectronicFenceGroupListReq.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/ElectronicFenceGroupListReq.java new file mode 100644 index 0000000..833e64c --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/ElectronicFenceGroupListReq.java @@ -0,0 +1,56 @@ +package com.muyu.common.domain.req; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @Author:yuping + * @Package:com.muyu.fence.domain.req + * @Project:cloud-server + * @name:ElectronicFenceGroupListReq + * @Date:2024/9/20 16:06 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ElectronicFenceGroupListReq { + + + /** + * 主键 + */ + @TableId(value = "id",type = IdType.AUTO) + private Long id; + /** + * 围栏组优先级 + */ + private Integer priority; + /** + * 围栏组名称 + */ + private String groupName; + /** + * 围栏组类型 + */ + private String groupType; + + /** + * 启用状态 + */ + private String status; + + + + + + + + + + +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/ElectronicFenceGroupUpdReq.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/ElectronicFenceGroupUpdReq.java new file mode 100644 index 0000000..73fad79 --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/ElectronicFenceGroupUpdReq.java @@ -0,0 +1,66 @@ +package com.muyu.common.domain.req; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.muyu.common.domain.resp.ElectronicFenceResp; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @Author:yuping + * @Package:com.muyu.fence.domain.req + * @Project:cloud-server + * @name:ElectronicFenceGroupAddReq + * @Date:2024/9/20 19:14 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ElectronicFenceGroupUpdReq { + + + /** + * 主键 + */ + @TableId(value = "id",type = IdType.AUTO) + private Long id; + /** + * 围栏组优先级 + */ + private Integer priority; + /** + * 围栏组名称 + */ + private String groupName; + /** + * 围栏组类型 + */ + private String groupType; + + /** + * 启用状态 + */ + private String status; + + + /** + * 选中围栏 + */ + List electronicFenceRespList; + + + + + + + + + + + +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/FenceAndGroupBoundReq.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/FenceAndGroupBoundReq.java new file mode 100644 index 0000000..0d6c4e0 --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/FenceAndGroupBoundReq.java @@ -0,0 +1,35 @@ +package com.muyu.common.domain.req; + +import com.muyu.common.domain.resp.ElectronicFenceResp; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @Author:yuping + * @Package:com.muyu.fence.domain.req + * @Project:cloud-server + * @name:FenceAndGroupBoundReq + * @Date:2024/9/21 9:05 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Tag(name = "用于绑定围栏和围栏组的请求") +public class FenceAndGroupBoundReq { + /** + * 围栏组的主键 + */ + private Long id; + + /** + * 选中的围栏 + */ + + private List electronicFenceRespList; + + +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/FenceWayReq.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/FenceWayReq.java new file mode 100644 index 0000000..d420edc --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/FenceWayReq.java @@ -0,0 +1,20 @@ +package com.muyu.common.domain.req; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @Author:yuping + * @Package:com.muyu.fence.domain + * @Project:cloud-server + * @name:FenceWay + * @Date:2024/9/20 9:27 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class FenceWayReq { + private Long id; + private String longitudeLatitude; +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/SysCarReq.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/SysCarReq.java new file mode 100644 index 0000000..a773921 --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/SysCarReq.java @@ -0,0 +1,20 @@ +package com.muyu.common.domain.req; + + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class SysCarReq { + private String carVin; + private String state; + private String carMotorManufacturer; + private String carMotorModel; + private String carBatteryManufacturer; + private String carBatteryModel; + + +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/WarnStrategyReq.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/WarnStrategyReq.java new file mode 100644 index 0000000..7ac776f --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/req/WarnStrategyReq.java @@ -0,0 +1,36 @@ +package com.muyu.common.domain.req; + +import com.muyu.common.core.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 预警策略对象 warn_strategy + * + * @author muyu + * @date 2024-09-20 + */ + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class WarnStrategyReq { + private static final long serialVersionUID = 1L; + + /** 车辆类型id */ + @Excel(name = "车辆类型id") + private Long carTypeId; + + /** 策略名称 */ + @Excel(name = "策略名称") + private String strategyName; + + /** 报文模版id */ + @Excel(name = "报文模版id") + private Long templateId; + + + + +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/CarTypeResp.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/CarTypeResp.java new file mode 100644 index 0000000..ac4a196 --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/CarTypeResp.java @@ -0,0 +1,19 @@ +package com.muyu.common.domain.resp; + + +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.domain.CarType; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class CarTypeResp extends CarType { + /** 报文模版id */ + @Excel(name = "报文模版名称") + private String templateName; +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/ElectronicFenceGroupResp.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/ElectronicFenceGroupResp.java new file mode 100644 index 0000000..bfc8e7f --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/ElectronicFenceGroupResp.java @@ -0,0 +1,60 @@ +package com.muyu.common.domain.resp; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @Author:yuping + * @Package:com.muyu.fence.domain.resp + * @Project:cloud-server + * @name:ElectronicFenceGroupResp + * @Date:2024/9/22 10:22 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Tag(name = "回显围栏组及绑定的电子围栏") +public class ElectronicFenceGroupResp { + + /** + * 主键 + */ + @TableId(value = "id",type = IdType.AUTO) + private Long id; + /** + * 围栏组优先级 + */ + private Integer priority; + /** + * 围栏组名称 + */ + private String groupName; + /** + * 围栏组类型 + */ + private String groupType; + + /** + * 启用状态 + */ + private String status; + + + /** + * 绑定的电子围栏 + */ + List electronicFenceRespList; + + + + + +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/ElectronicFenceResp.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/ElectronicFenceResp.java new file mode 100644 index 0000000..e20b965 --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/ElectronicFenceResp.java @@ -0,0 +1,68 @@ +package com.muyu.common.domain.resp; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +/** + * @Author:yuping + * @Package:com.muyu.fence.domain + * @Project:cloud-server + * @name:ElectronicFence + * @Date:2024/9/17 16:34 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class ElectronicFenceResp { + + /** + * 主键id + */ + private Long id; + + /** + * 围栏名称 + */ + + private String name; + + /** + * 围栏类型(驶入,驶出) + */ + + private String fenceType; + + /** + *经纬度信息 + */ + +private String longitudeLatitude; + + /** + * 电子围栏状态(正常,停用) + */ + +private String status; + + /** + * 描述信息 + */ + +private String desc; + +//private String groupType; +// +//private int priority; + + + + + + + + +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/GroupFenceListresp.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/GroupFenceListresp.java new file mode 100644 index 0000000..c6f947a --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/GroupFenceListresp.java @@ -0,0 +1,52 @@ +package com.muyu.common.domain.resp; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @Author:yuping + * @Package:com.muyu.fence.domain.req + * @Project:cloud-server + * @name:GroupFenceListresp + * @Date:2024/9/20 9:04 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Tag(name = "围栏组列表") +public class GroupFenceListresp { + + /** + * 主键 + */ + @TableId(value = "id",type = IdType.AUTO) + private Long id; + /** + * 围栏组优先级 + */ + private Integer priority; + /** + * 围栏组名称 + */ + private String groupName; + /** + * 围栏组类型 + */ + private String groupType; + + /** + * 启用状态 + */ + private String status; + + + + + +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/SysCarFaultLogVo.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/SysCarFaultLogVo.java new file mode 100644 index 0000000..59dcebb --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/SysCarFaultLogVo.java @@ -0,0 +1,24 @@ +package com.muyu.common.domain.resp; + +import com.muyu.common.domain.SysCarFaultLog; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @Author:liuxinyue + * @Package:com.muyu.domain.resp + * @Project:cloud-server + * @name:SysCarFaultLogVo + * @Date:2024/9/23 18:45 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class SysCarFaultLogVo extends SysCarFaultLog { + + private String faultCode; + + private String faultName; + +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/SysCarVo.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/SysCarVo.java new file mode 100644 index 0000000..e7f12b8 --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/SysCarVo.java @@ -0,0 +1,26 @@ +package com.muyu.common.domain.resp; + +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.domain.SysCar; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class SysCarVo extends SysCar { + + @Excel(name = "车辆类型名称") + private String typeName; + @Excel(name = "策略名称") + private String strategyName; + @Excel(name = "围栏组名称") + private String groupName; + + + + +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/WarnLogsResp.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/WarnLogsResp.java new file mode 100644 index 0000000..7af5b86 --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/WarnLogsResp.java @@ -0,0 +1,17 @@ +package com.muyu.common.domain.resp; + + +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.domain.WarnLogs; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class WarnLogsResp extends WarnLogs { + /** 规则名称 */ + @Excel(name = "规则名称") + private String ruleName; +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/WarnRuleResp.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/WarnRuleResp.java new file mode 100644 index 0000000..6d3466c --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/WarnRuleResp.java @@ -0,0 +1,19 @@ +package com.muyu.common.domain.resp; + +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.domain.WarnRule; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class WarnRuleResp extends WarnRule { + @Excel(name = "策略名称") + private String strategyName; + + + @Excel(name = "报文数值") + private String messageField; +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/WarnStrategyResp.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/WarnStrategyResp.java new file mode 100644 index 0000000..2fc423a --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/resp/WarnStrategyResp.java @@ -0,0 +1,24 @@ +package com.muyu.common.domain.resp; + + +import com.muyu.common.core.annotation.Excel; +import com.muyu.common.domain.WarnStrategy; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class WarnStrategyResp extends WarnStrategy { + /** 车辆类型名称 */ + private String typeName; + + /** 策略名称 */ + @Excel(name = "策略名称") + private String strategyName; + + /** 报文模版id */ + @Excel(name = "报文模版名称") + private String templateName; +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/utils/ElectricFenceModel.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/utils/ElectricFenceModel.java new file mode 100644 index 0000000..7d3bef3 --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/utils/ElectricFenceModel.java @@ -0,0 +1,75 @@ +package com.muyu.common.domain.utils; + +/** + * @Author:yuping + * @Package:com.muyu.fence.domain + * @Project:cloud-server + * @name:ElectricFenceModel + * @Date:2024/9/17 17:27 + */ + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 电子围栏规则计算模型 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ElectricFenceModel implements Comparable { + //车架号 + private String vin = ""; + //电子围栏结果表UUID + private Long uuid = -999999L; + //上次状态 0 里面 1 外面 + private int lastStatus = -999999; + //当前状态 0 里面 1 外面 + private int nowStatus = -999999; + //位置时间 yyyy-MM-dd HH:mm:ss + private String gpsTime = ""; + //位置纬度-- + private Double lat = -999999D; + //位置经度-- + private Double lng = -999999D; + //电子围栏ID + private int eleId = -999999; + //电子围栏名称 + private String eleName = ""; + //中心点地址 + private String address = ""; + //中心点纬度 + private Double latitude; + //中心点经度 + private Double longitude = -999999D; + //电子围栏半径 + private Float radius = -999999F; + //出围栏时间 + private String outEleTime = null; + //进围栏时间 + private String inEleTime = null; + //是否在mysql结果表中 + private Boolean inMysql = false; + //状态报警 0:出围栏 1:进围栏 + private int statusAlarm = -999999; + //报警信息 + private String statusAlarmMsg = ""; + //终端时间 + private String terminalTime = ""; + // 扩展字段 终端时间 + private Long terminalTimestamp = -999999L; + + @Override + public int compareTo(ElectricFenceModel o) { + if(this.getTerminalTimestamp() > o.getTerminalTimestamp()){ + return 1; + } + else if(this.getTerminalTimestamp() < o.getTerminalTimestamp()){ + return -1; + }else{ + return 0; + } + } +} + diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/utils/ElectricFenceResultTmp.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/utils/ElectricFenceResultTmp.java new file mode 100644 index 0000000..efb592b --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/utils/ElectricFenceResultTmp.java @@ -0,0 +1,53 @@ +package com.muyu.common.domain.utils; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; + +/** + * @Author:yuping + * @Package:com.muyu.fence.domain + * @Project:cloud-server + * @name:ElectricFenceResultTmp + * @Date:2024/9/17 17:57 + */ +/** + * 电子围栏转换临时对象 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ElectricFenceResultTmp { + //电子围栏id + private int id; + //电子围栏名称 + private String name; + //电子围栏中心地址 + private String address; + //电子围栏半径 + private float radius; + //电子围栏中心点的经度 + private double longitude; + //电子围栏中心点的维度 + private double latitude; + //电子围栏的开始时间 + private Date startTime; + //电子围栏的结束时间 + private Date endTime; + + @Override + public String toString() { + return "ElectricFenceResultTmp{" + + "id=" + id + + ", name='" + name + '\'' + + ", address='" + address + '\'' + + ", radius=" + radius + + ", longitude=" + longitude + + ", latitude=" + latitude + + ", startTime=" + startTime + + ", endTime=" + endTime + + '}'; + } +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/utils/ElectronicFenceResult.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/utils/ElectronicFenceResult.java new file mode 100644 index 0000000..bf7a50e --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/utils/ElectronicFenceResult.java @@ -0,0 +1,55 @@ +package com.muyu.common.domain.utils; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.sql.Timestamp; +import java.util.Date; + +/** + * @Author:yuping + * @Package:com.muyu.fence.domain + * @Project:cloud-server + * @name:ElectronicFenceResult + * @Date:2024/9/17 17:43 + */ + +/** + * 电子围栏分析结果数据结构 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class ElectronicFenceResult { + /** + * 主键 + */ + private Long id; + /** + * 车辆唯一标识 + */ + private String vin; + /** + * + */ + private String inTime; + private String outTime; + /** + * gps定位时间 + */ + private Date gpsTime; + + private Double lat; + private Double lng; + private Integer eleId; + private String eleName; + private String address; + private Double latitude; + private Double longitude; + private Double radius; + private String terminalTime; + private Date processTime; +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/utils/ElectronicFenceSetting.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/utils/ElectronicFenceSetting.java new file mode 100644 index 0000000..4238c01 --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/domain/utils/ElectronicFenceSetting.java @@ -0,0 +1,63 @@ +package com.muyu.common.domain.utils; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +/** + * @Author:yuping + * @Package:com.muyu.fence.domain + * @Project:cloud-server + * @name:ElectronicFenceSetting + * @Date:2024/9/17 16:47 + */ +@Data +@SuperBuilder +@AllArgsConstructor +@NoArgsConstructor +@TableName(value = "electronic_fence_setting",autoResultMap = true) +public class ElectronicFenceSetting { + /** + * 主键id + */ + @TableId(value = "id",type = IdType.AUTO) + private String id; + /** + * 电子围栏名称 + */ + private String name; + /** + * 电子围栏中心地址 + */ + private String address; + /** + * 电子围栏半径 + */ + private String radius; + /** + * 电子围栏中心点经度 + */ + private String longitude; + /** + * 电子围栏围栏中心点维度 + */ + private String latitude; + /** + * 电子围栏的开始时间 + */ + private String startTime; + /** + * 电子围栏的结束时间 + */ + private String endTime; + /** + * 电子围栏的状态(开启,关闭) + */ + private String status; + +} diff --git a/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/util/PageUtils.java b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/util/PageUtils.java new file mode 100644 index 0000000..146d370 --- /dev/null +++ b/cloud-modules/saas/saas-common/src/main/java/com/muyu/common/util/PageUtils.java @@ -0,0 +1,50 @@ +package com.muyu.common.util; + +import lombok.Data; + +import java.util.List; + +/** + * @Author mengyu + * @Description 分页封装类 + * @Version 1.0 + * @Data 2024-09-08 14:58:59 + */ + +@Data +public class PageUtils { + /** + * 总记录数 + */ + private long totalCount; + + /** + * 每页显示几条记录 + */ + private int pageSize; + + /** + * 总页数 + */ + private int totalPage; + + /** + * 当前页数 + */ + private int pageIndex; + + /** + * 分页数据 + */ + private List list; + + public PageUtils(List list, long totalCount, int pageIndex, int pageSize) { + this.list = list; + this.totalCount = totalCount; + this.pageSize = pageSize; + this.pageIndex = pageIndex; + this.totalPage = (int) Math.ceil((double) totalCount / pageSize); + } + +} + diff --git a/cloud-modules/saas/saas-server/pom.xml b/cloud-modules/saas/saas-server/pom.xml new file mode 100644 index 0000000..56f77f3 --- /dev/null +++ b/cloud-modules/saas/saas-server/pom.xml @@ -0,0 +1,117 @@ + + + 4.0.0 + + com.muyu + saas + 3.6.3 + + + com.muyu.server + saas-server + + + 17 + 17 + UTF-8 + + + + saas-server业务系统模块 + + + + + + com.muyu.common + saas-common + 3.6.3 + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + com.mysql + mysql-connector-j + + + + + com.muyu + cloud-common-datasource + + + + + com.muyu + cloud-common-datascope + + + + + com.muyu + cloud-common-log + + + + + com.muyu + cloud-common-api-doc + + + + + com.muyu + cloud-common-xxl + + + org.apache.iotdb + service-rpc + 1.3.2 + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + + diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/SaasApplication.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/SaasApplication.java new file mode 100644 index 0000000..ce9a6ac --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/SaasApplication.java @@ -0,0 +1,19 @@ +package com.muyu.server; + +import com.muyu.common.security.annotation.EnableMyFeignClients; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @Author YuPing + * @Description saas模块启动类 + * @Version 1.0 + * @Data 2024-09-28 17:34:31 + */ +@SpringBootApplication +@EnableMyFeignClients +public class SaasApplication { + public static void main(String[] args) { + SpringApplication.run(SaasApplication.class, args); + } +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/CarTypeController.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/CarTypeController.java new file mode 100644 index 0000000..1902420 --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/CarTypeController.java @@ -0,0 +1,33 @@ +package com.muyu.server.controller; + +import com.muyu.common.core.domain.Result; +import com.muyu.server.service.CarTypeService; +import org.springframework.beans.factory.annotation.Autowired; +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 java.util.List; + + +@RestController +@RequestMapping("/carType") +public class CarTypeController { + @Autowired + private CarTypeService carTypeService; + + + @GetMapping("/selectCarTypeList") + public Result selectCarTypeList(){ + return Result.success(carTypeService.selectCarTypeList()); + } + + + @GetMapping("/selectCarTypeRespList/{id}") + public Result selectCarTypeRespList(@PathVariable("id") Long id) { + return Result.success(carTypeService.selectCarTypeRespList(id)); + } + + +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/DataTypeController.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/DataTypeController.java new file mode 100644 index 0000000..220d0b9 --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/DataTypeController.java @@ -0,0 +1,47 @@ +package com.muyu.server.controller; + +import com.muyu.common.core.domain.Result; +import com.muyu.common.domain.DataType; +import com.muyu.server.service.DataTypeService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + + +/** + * @Author:liuxinyue + * @Package:com.sheep.controller + * @Project:cloud-server-c + * @name:DataTypeController + * @Date:2024/9/20 9:15 + */ +@RestController +@RequestMapping("/dataType") +@AllArgsConstructor +@Tag(name = "数据类型管理",description = "数据类型管理") +@Log4j2 +public class DataTypeController { + + + @Autowired + private DataTypeService dataTypeService; + + + /** + * 数据类型列表 + * @return + */ + @PostMapping("/dataTypeList") + @Operation(summary = "数据类型列表",description = "数据类型列表") + public Result> dataTypeList() { + return Result.success(dataTypeService.list()); + } + +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/ElectronicFenceController.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/ElectronicFenceController.java new file mode 100644 index 0000000..d607d8e --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/ElectronicFenceController.java @@ -0,0 +1,138 @@ +package com.muyu.server.controller; + +import com.muyu.common.core.domain.Result; +import com.muyu.common.domain.database.ElectronicFence; +import com.muyu.common.domain.req.ElectroicFenceAddReq; +import com.muyu.common.domain.req.ElectroicFenceListReq; +import com.muyu.common.domain.req.ElectroicFenceUpdReq; +import com.muyu.common.domain.req.FenceWayReq; +import com.muyu.common.domain.resp.ElectronicFenceResp; +import com.muyu.server.service.ElectronicFenceService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * @Author:yuping + * @Package:com.muyu.fence.controller + * @Project:cloud-server + * @name:FenceEtlController + * @Date:2024/9/17 16:28 + */ +@Log4j2 +@Tag(name = "电子围栏") +@RestController +@RequestMapping("/fence") +@RequiredArgsConstructor +public class ElectronicFenceController { + + @Autowired + private ElectronicFenceService electronicFenceService; + + + @PostMapping("/fenceArray") + @Operation(description = "查询所有可用的围栏") + public Result> fenceArray() { + System.out.println("=====>" + "hgfvhgjy"); + return Result.success(electronicFenceService.fenceArray()); + } + + @PostMapping("/fenceselectList") + @Operation(description = "列表") + public Result> fenceselectList(@RequestBody ElectroicFenceListReq electroicFenceListReq) { + System.out.println("=====>" + "hgfvhgjy"); + return Result.success(electronicFenceService.fenceselectList(electroicFenceListReq)); + } + + @PostMapping("/add") + @Operation(description = "添加") + public Result AddFence(@RequestBody ElectroicFenceAddReq electroicFenceAddReq) { + + electronicFenceService.unquireFence(electroicFenceAddReq.getName()); + electronicFenceService.AddFence(electroicFenceAddReq); + + return Result.success(); + } + + @PostMapping("/upd/{id}") + @Operation(description = "修改") + public Result UpdFence(@PathVariable("id") Long id, @RequestBody ElectroicFenceUpdReq electroicFenceUpdReq) { + + electronicFenceService.updateById(ElectronicFence.buildByElectronicUpd(electroicFenceUpdReq, () -> id)); + + return Result.success(); + } + + @PostMapping("/findElectronicByid/{id}") + @Operation(description = "通过id回显围栏信息") + public Result findElectronicByid(@PathVariable("id") Long id) { + + ElectronicFence electronicFence= electronicFenceService.findElectronicByid(id); + return Result.success(electronicFence); + } + + +// @PostMapping("/delElectronById/{id}") +// @Operation(description = "通过id删除围栏") +// public Result delElectronById(@PathVariable("id") Long id) { +// +// electronicFenceService.delElectronById(id); +// +// return Result.success(); +// } + + /** + * 电子围栏表信息表删除 + * @param electronicFenceId 请求对象 + * @return 返回结果 + */ + @DeleteMapping("/{electronicFenceId}") + @Operation(summary = "电子围栏表信息表删除", + description = "通过ID删除电子围栏表信息") + public Result remove(@PathVariable("electronicFenceId") Long electronicFenceId) + { + boolean b = electronicFenceService.removeById(electronicFenceId); + return Result.success(null, "操作成功"); + } + + @PostMapping("/open/{id}") + @Operation(description = "开启围栏") + public Result openFence(@PathVariable("id") Long id) { + + electronicFenceService.openFence(id); + + return Result.success(); + } + + @PostMapping("/close/{id}") + @Operation(description = "关闭围栏") + public Result closeFence(@PathVariable("id") Long id) { + + electronicFenceService.closeFence(id); + + return Result.success(); + } + + @PostMapping("/setFenceWay") + @Operation(description = "设置电子围栏的位置") + public Result setFenceWay(@RequestBody FenceWayReq fenceWayReq) { + + Long id = fenceWayReq.getId(); + + String longitudeLatitude = fenceWayReq.getLongitudeLatitude(); + + log.info("接收到的数据,:{}" + id + "====" + longitudeLatitude); + + electronicFenceService.setFenceWay(id, longitudeLatitude); + + return Result.success(); + + } + + +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/ElectronicFenceGroupController.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/ElectronicFenceGroupController.java new file mode 100644 index 0000000..297050a --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/ElectronicFenceGroupController.java @@ -0,0 +1,95 @@ +package com.muyu.server.controller; + +import com.muyu.common.core.domain.Result; +import com.muyu.common.domain.database.ElectronicFenceGroup; +import com.muyu.common.domain.req.ElectronicFenceGroupAddReq; +import com.muyu.common.domain.req.ElectronicFenceGroupListReq; +import com.muyu.common.domain.req.ElectronicFenceGroupUpdReq; +import com.muyu.common.domain.resp.ElectronicFenceGroupResp; +import com.muyu.common.domain.resp.GroupFenceListresp; +import com.muyu.server.service.ElectronicFenceGroupService; +import com.muyu.server.service.FenceGroupMidService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * @Author:yuping + * @Package:com.muyu.fence.controller + * @Project:cloud-server + * @name:FenceGroupController + * @Date:2024/9/18 15:15 + */ + +@Log4j2 +@RestController +@RequestMapping("/group") +@Tag(name = "围栏组") +public class ElectronicFenceGroupController { + + @Autowired + private ElectronicFenceGroupService electronicFenceGroupService; + + @Autowired + private FenceGroupMidService fenceGroupMidService; + + + @PostMapping("/selectGroupList") + public Result> selectGroupList(@RequestBody ElectronicFenceGroupListReq req) { +//查询所有的围栏组 + List fenceListList = electronicFenceGroupService.selectGroupList(req); + + List list = fenceListList.stream().map(ElectronicFenceGroup::buildGroupFence).toList(); + + return Result.success(list); + } + + @Transactional + @PostMapping("/addGroup") + public Result addGroup(@RequestBody ElectronicFenceGroupAddReq addReq) { + + ElectronicFenceGroup electronicFenceGroup = ElectronicFenceGroup.buildByAdd(addReq); + //添加围栏组返回添加后的主键自增id + electronicFenceGroupService.save(electronicFenceGroup); + Long id = electronicFenceGroup.getId(); +//添加中间表 + fenceGroupMidService.addGroupAndFenceMid(id, addReq.getElectronicFenceRespList()); + + + return Result.success(); + } + + @PostMapping("/findGroupByid/{id}") + @Operation(description = "通过id回显围栏组信息") + public Result findGroupByid(@PathVariable("id") Long id) { + + ElectronicFenceGroupResp fenceGroupResp = electronicFenceGroupService.findGroupByid(id); + return Result.success(fenceGroupResp); + + } + + + @Transactional + @PostMapping("/updGroup/{id}") + @Operation(description = "修改") + public Result UpdFence(@PathVariable("id") Long id, @RequestBody ElectronicFenceGroupUpdReq req) { + + electronicFenceGroupService.updateById(ElectronicFenceGroup.buildByUpd(req, () -> id)); + + if (!CollectionUtils.isEmpty(req.getElectronicFenceRespList())) { +//删除中间表 + fenceGroupMidService.deliteMid(id); + //增加中间表 + fenceGroupMidService.addGroupAndFenceMid(id, req.getElectronicFenceRespList()); + } + return Result.success(); + } + + +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/EnterpriseController.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/EnterpriseController.java new file mode 100644 index 0000000..5a417e1 --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/EnterpriseController.java @@ -0,0 +1,113 @@ +package com.muyu.server.controller; + +import cn.hutool.core.bean.BeanUtil; +import com.muyu.common.core.domain.Result; +import com.muyu.common.domain.Enterprise; +import com.muyu.common.util.PageUtils; +import com.muyu.server.controller.form.DeleteEnterpriseByIds; +import com.muyu.server.controller.form.InsertEnterprise; +import com.muyu.server.controller.form.SearchEnterpriseName; +import com.muyu.server.controller.form.UpdateEnterprise; +import com.muyu.server.service.EnterpriseService; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.Map; + +/** + * @Author YuPing + * @Description 企业运营控制层 + * @Version 1.0 + * @Data 2024-09-26 20:24:05 + */ +@RestController +public class EnterpriseController { + + @Autowired + private EnterpriseService enterpriseService; + + + /** + * 查询分页企业信息 + * @param form + * @return + */ + @PostMapping("/selectEnterprise") + public Result selectEnterprise(@RequestBody @Valid SearchEnterpriseName form) { + Integer page = form.getPage(); + Integer length = form.getLength(); + int start = (page - 1) * length; + Map param = BeanUtil.beanToMap(form); + param.put("start", start); + PageUtils pageUtils = enterpriseService.selectEnterprise(param); + + System.out.println(pageUtils); + + return Result.success(pageUtils); + } + + + /** + * 新增企业信息 + * @param form + * @return + */ + @PostMapping("/insert") + public Result insert(@RequestBody @Valid InsertEnterprise form){ + Enterprise enterprise = new Enterprise(); + + enterprise.setEnterpriseName(form.getEnterpriseName()); + enterprise.setEnterpriseCarCount(form.getEnterpriseCarCount()); + enterprise.setEnterpriseFenceCount(form.getEnterpriseFenceCount()); + + int rows = enterpriseService.insert(enterprise); + return Result.success(rows); + } + + + /** + * 根据编号查询企业信息 + * @param enterpriseId + * @return + */ + @GetMapping("/searchById") + public Result searchById(@RequestParam("enterpriseId") Integer enterpriseId){ + HashMap map = enterpriseService.searchById(enterpriseId); + return Result.success(map); + } + + + /** + * 修改企业信息 + * @param form + * @return + */ + @PostMapping("/updateEnterEnterprise") + public Result updateEnterprise(@RequestBody @Valid UpdateEnterprise form){ + Enterprise enterprise = new Enterprise(); + + enterprise.setEnterpriseId(form.getEnterpriseId()); + enterprise.setEnterpriseName(form.getEnterpriseName()); + enterprise.setEnterpriseCarCount(form.getEnterpriseCarCount()); + enterprise.setEnterpriseFenceCount(form.getEnterpriseFenceCount()); + + int rows = enterpriseService.updateEnterprise(enterprise); + return Result.success(rows); + } + + + /** + * 删除企业信息 + * @param form + * @return + */ + @PostMapping("/deleteByIds") + public Result deleteByIds(@RequestBody @Valid DeleteEnterpriseByIds form){ + int rows = enterpriseService.deleteByIds(form.getIds()); + return Result.success(rows); + } + + +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/MessageTemplateTypeController.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/MessageTemplateTypeController.java new file mode 100644 index 0000000..6afc5bf --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/MessageTemplateTypeController.java @@ -0,0 +1,102 @@ +package com.muyu.server.controller; + +import com.muyu.common.core.domain.Result; +import com.muyu.common.domain.MessageTemplateType; +import com.muyu.server.service.MessageTemplateTypeService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * @Author:liuxinyue + * @Package:com.sheep.message.controller + * @Project:cloud-server-c + * @name:MessageTemplateTypeController + * @Date:2024/9/19 14:33 + */ +@RestController +@RequestMapping("/messageTemplateType") +@AllArgsConstructor +@Tag(name = "报文模版管理",description = "报文模版管理") +@Log4j2 +public class MessageTemplateTypeController { + + @Autowired + private MessageTemplateTypeService messageTemplateTypeService; + + + /** + * 列表 + * @return + */ + @PostMapping("/messageTemplateTypeList") + @Operation(summary = "报文模版类型列表",description = "报文模版类型列表") + public Result> messageTemplateTypeList() { + List list = messageTemplateTypeService.list(); + return Result.success(list,"查询成功"); + } + + /** + * 添加 + * @param messageTemplateType + * @return + */ + @PostMapping("/addMessageType") + @Operation(summary = "报文模版类型添加",description = "报文模版类型添加") + public Result addMessageType(@RequestBody MessageTemplateType messageTemplateType) { + return Result.success(messageTemplateTypeService.addMessageType(messageTemplateType)); + } + + /** + * 基础类型 + * @param templatedId + * @param code + * @return + */ + @PostMapping("/findvehicleFoundationData") + @Operation(summary = "基础类型",description = "基础类型") + public Result> findvehicleFoundationData(@RequestParam("templatedId") Integer templatedId, @RequestParam("code") String code) { + return Result.success(messageTemplateTypeService.findvehicleFoundationData(templatedId,code)); + } + + + /** + * 数据类型 + * @param templatedId + * @param code + * @return + */ + @PostMapping("/findvehicleData") + @Operation(summary = "数据类型",description = "数据类型") + public Result> findvehicleData(@RequestParam("templatedId") Integer templatedId,@RequestParam("code") String code) { + return Result.success(messageTemplateTypeService.findvehicleData(templatedId,code)); + } + + /** + * 设备状态 + * @param templatedId + * @param code + * @return + */ + @PostMapping("/finddeviceStatusData") + @Operation(summary = "设备状态",description = "设备状态") + public Result> finddeviceStatusData(@RequestParam("templatedId") Integer templatedId,@RequestParam("code") String code) { + return Result.success(messageTemplateTypeService.finddeviceStatusData(templatedId,code)); + } + + /** + * 根据车辆类型查出所对应的模版 + * @param templatedId + * @return + */ + @PostMapping("/findMessageByTemplateName") + @Operation(summary = "根据车辆类型查出所对应的模版",description = "根据车辆类型查出所对应的模版") + public Result> findMessageByTemplateName(@RequestParam("templatedId") Integer templatedId) { + return Result.success(messageTemplateTypeService.findMessageByTemplateName(templatedId)); + } +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/SysCarController.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/SysCarController.java new file mode 100644 index 0000000..9d82059 --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/SysCarController.java @@ -0,0 +1,58 @@ +package com.muyu.server.controller; + +import com.muyu.common.core.domain.Result; +import com.muyu.common.domain.SysCar; +import com.muyu.common.domain.req.SysCarReq; +import com.muyu.common.domain.resp.SysCarFaultLogVo; +import com.muyu.server.service.SysCarService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/sysCar") +public class SysCarController { + @Autowired + private SysCarService sysCarService; + + @PostMapping("/selectSysCarVoList") + public Result selectSysCarVoList(@RequestBody SysCarReq sysCarReq){ + return Result.success(sysCarService.selectSysCarVoList(sysCarReq)); + } + + + @GetMapping("/selectSysCarVoById/{id}") + public Result selectSysCarVoById(@PathVariable("id") Long id){ + return Result.success(sysCarService.selectSysCarVoById(id)); + } + + + @PostMapping("/addSysCar") + public Result addSysCar(@RequestBody SysCar sysCar){ + return Result.success(sysCarService.addSysCar(sysCar)); + } + + @PostMapping("/updateSysCar") + public Result updateSysCar(@RequestBody SysCar sysCar){ + return Result.success(sysCarService.updateSysCar(sysCar)); + } + + + @DeleteMapping("/deleteSysCarById/{id}") + public Result deleteSysCarById(@PathVariable("id") Long id){ + int i = sysCarService.deleteSysCarById(id); + return i>0?Result.success():Result.error(); + } + + /** + * 根据车辆的VIN码查询该车的故障记录 + * @param carVin + * @return + */ + @PostMapping("/findFenceByCarVin/{carVin}") + public Result> findFenceByCarVin(@PathVariable("carVin") String carVin){ + return Result.success(sysCarService.findFenceByCarVin(carVin)); + } + +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/SysCarFaultController.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/SysCarFaultController.java new file mode 100644 index 0000000..fcc437c --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/SysCarFaultController.java @@ -0,0 +1,137 @@ +package com.muyu.server.controller; + +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.utils.poi.ExcelUtil; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.web.page.TableDataInfo; +import com.muyu.common.domain.SysCarFault; +import com.muyu.common.security.annotation.RequiresPermissions; +import com.muyu.common.security.utils.SecurityUtils; +import com.muyu.server.service.SysCarFaultService; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + +/** + * 故障码 接口控制层 + * @author YuanZiLong + * @package: com.muyu.breakdown.controller + * @name: SysCarFaultController + * @date: 2024/9/20 11:00 + */ +@RestController +@RequestMapping("/breakdown") +public class SysCarFaultController extends BaseController +{ + @Resource + private SysCarFaultService sysCarFaultService; + + /** + * 查询车辆故障管理列表 + */ + @RequiresPermissions("breakdown:breakdown:list") + @GetMapping("/list") + public Result> list(SysCarFault sysCarFault) + { + startPage(); + List list = sysCarFaultService.selectSysCarFaultList(sysCarFault); + return getDataTable(list); + } + + + + /** + * 导出车辆故障管理列表 + */ + @RequiresPermissions("breakdown:breakdown:export") + @PostMapping("/export") + public void export(HttpServletResponse response, SysCarFault sysCarFault) + { + List list = sysCarFaultService.selectSysCarFaultList(sysCarFault); + ExcelUtil util = new ExcelUtil(SysCarFault.class); + util.exportExcel(response, list, "车辆故障管理数据"); + } + + /** + * 获取车辆故障管理详细信息 + */ + @RequiresPermissions("breakdown:breakdown:query") + @GetMapping(value = "/{id}") + public Result> getInfo(@PathVariable("id") Long id) + { + return success(sysCarFaultService.selectSysCarFaultById(id)); + } + + /** + * 新增车辆故障管理 + */ + @RequiresPermissions("breakdown:breakdown:add") + @PostMapping + public Result add( + @Validated @RequestBody SysCarFault sysCarFault) + { + //判断故障码是否重复 + SysCarFault selectFaultByFaultCode = sysCarFaultService.selectFaultByFaultCode(sysCarFault.getFaultCode()); + if (selectFaultByFaultCode!=null){ + return error("新增车辆故障 ,故障码已存在"); + } + + + sysCarFault.setCreateBy(SecurityUtils.getUsername()); + return toAjax(sysCarFaultService.save(sysCarFault)); + } + + + /** + * 修改车辆故障管理 + */ + @RequiresPermissions("breakdown:breakdown:edit") + @PutMapping + public Result edit( + @Validated @RequestBody SysCarFault sysCarFault) + { + + sysCarFault.setUpdateBy(SecurityUtils.getUsername()); + return toAjax(sysCarFaultService.updateById(sysCarFault)); + } + + /** + * 删除车辆故障管理 + */ + @RequiresPermissions("breakdown:breakdown:remove") + @DeleteMapping("/{ids}") + public Result remove(@PathVariable("ids") Long[] ids) + { + sysCarFaultService.removeBatchByIds(Arrays.asList(ids)); + return success(); + } + + /** + * 启用告警 + * @param id + */ + @PutMapping("/enableWarningsById/{id}") + public void enableWarningsById(@PathVariable("id")Long id){ + UpdateWrapper wrapper = new UpdateWrapper<>(); + wrapper.eq("id",id); + wrapper.set("warn_status",0); + sysCarFaultService.update(wrapper); + } + /** + * 禁用告警 + * @param id + */ + @PutMapping("/disableWarningsById/{id}") + public void disableWarningsById(@PathVariable("id")Long id){ + UpdateWrapper wrapper = new UpdateWrapper<>(); + wrapper.eq("id",id); + wrapper.set("warn_status",1); + sysCarFaultService.update(wrapper); + } + +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/SysCarFaultLogController.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/SysCarFaultLogController.java new file mode 100644 index 0000000..3c4063d --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/SysCarFaultLogController.java @@ -0,0 +1,91 @@ +package com.muyu.server.controller; + +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.utils.poi.ExcelUtil; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.web.page.TableDataInfo; +import com.muyu.common.domain.SysCarFaultLog; +import com.muyu.server.service.SysCarFaultLogService; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + +/** + * 车辆故障记录 接口控制层 + * @author YuanZiLong + * @package: com.muyu.breakdown.controller + * @name: SysCarFaultLogController + * @date: 2024/9/22 21:08 + */ +@RestController +@RequestMapping("/log") +public class SysCarFaultLogController extends BaseController { + @Autowired + private SysCarFaultLogService service; + + @PostMapping("/list") + public Result> list(@RequestBody SysCarFaultLog sysCarFaultLog){ + startPage(); + List list = service.selectSysCarFaultLogList(sysCarFaultLog); + return getDataTable(list); + } + + @PostMapping("/listStatusIgnore") + public Result>listStatusIgnore(@RequestBody SysCarFaultLog sysCarFaultLog){ + startPage(); + List list = service.listStatusIgnore(sysCarFaultLog); + return getDataTable(list); + } + + + + + @PostMapping("/listStatusSolve") + public Result>listStatusSolve(@RequestBody SysCarFaultLog sysCarFaultLog){ + startPage(); + List list = service.listStatusSolve(sysCarFaultLog); + return getDataTable(list); + } + + @PostMapping("/listStatusProcess") + public Result>listStatusProcess(@RequestBody SysCarFaultLog sysCarFaultLog){ + startPage(); + List list = service.listStatusProcess(sysCarFaultLog); + return getDataTable(list); + } + + @PostMapping("/add") + public Result add(@RequestBody SysCarFaultLog sysCarFaultLog){ + + return toAjax(service.save(sysCarFaultLog)); + } + + @PostMapping("/export") + public void export(HttpServletResponse response, SysCarFaultLog sysCarFaultLog){ + List list = service.selectSysCarFaultLogList(sysCarFaultLog); + ExcelUtil util = new ExcelUtil(SysCarFaultLog.class); + util.exportExcel(response,list,"车辆故障记录数据"); + } + + /** + * 修改为忽略 + * @param idsStr + * @return + */ + @PutMapping("/updateStatusById/{ids}") + public Result updateStatusById(@PathVariable("ids")String idsStr){ + Long[] ids = Arrays.stream(idsStr.split(",")) + .map(Long::valueOf) + .toArray(Long[]::new); + UpdateWrapper wrapper = new UpdateWrapper<>(); + wrapper.in("id",ids).set("status",3); + boolean update = service.update(null, wrapper); + return Result.success(update); + } + + +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/SysCarFaultMessageController.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/SysCarFaultMessageController.java new file mode 100644 index 0000000..6446fb1 --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/SysCarFaultMessageController.java @@ -0,0 +1,74 @@ +package com.muyu.server.controller; + +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.muyu.common.core.domain.Result; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.web.page.TableDataInfo; +import com.muyu.common.domain.SysCarFaultMessage; +import com.muyu.server.service.SysCarFaultMessageService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + + +/** + * 站内信 接口控制层 + * @author YuanZiLong + * @package: com.muyu.breakdown.controller + * @name: SysCarFaultMessageController + * @date: 2024/9/22 14:39 + */ +@RestController +@RequestMapping("/message") +public class SysCarFaultMessageController extends BaseController { + @Autowired + private SysCarFaultMessageService service; + + /** + * 查询所有故障信息 + * @return + */ + @GetMapping("/list") + public Result> list(){ + startPage(); + List list = service.list(); + return getDataTable(list); + } + + /** + * 查询状态为1的故障信息 + * @return + */ + + @GetMapping("/listStatusOne") + public Result>listStatusOne(){ + startPage(); + List list = service.listStatusOnt(); + return getDataTable(list); + } + + /** + * 查询状态为2的故障信息 + * @return + */ + @GetMapping("/listStatusTwo") + public Result>listStatusTwo(){ + startPage(); + List list = service.listStatusTwo(); + return getDataTable(list); + } + /** + * 修改未读站内信息为已读 + * @param id + * @return + */ + @PutMapping("/updateStatusById/{id}") + public Result updateStatusById(@PathVariable("id")Long id){ + UpdateWrapper wrapper = new UpdateWrapper<>(); + wrapper.eq("id",id); + wrapper.set("status",1); + boolean update = service.update(wrapper); + return Result.success(update); + } +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/SysCarLogController.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/SysCarLogController.java new file mode 100644 index 0000000..0d4c1de --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/SysCarLogController.java @@ -0,0 +1,31 @@ +package com.muyu.server.controller; + +import com.muyu.common.core.domain.Result; +import com.muyu.server.service.SysCarLogService; +import org.springframework.beans.factory.annotation.Autowired; +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 java.util.List; + +@RestController +@RequestMapping("/sysCarLog") +public class SysCarLogController { + @Autowired + private SysCarLogService sysCarLogService; + + + @GetMapping("/selectList") + public Result selectList(){ + return Result.success(sysCarLogService.selectList()); + } + + @GetMapping("/selectList/{id}") + public Result selectById(@PathVariable("id") Long id){ + return Result.success(sysCarLogService.selectById(id)); + } + + +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/TemplateController.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/TemplateController.java new file mode 100644 index 0000000..36f5980 --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/TemplateController.java @@ -0,0 +1,63 @@ +package com.muyu.server.controller; + +import com.muyu.common.core.domain.Result; +import com.muyu.common.domain.Template; +import com.muyu.server.service.TemplateService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import org.apache.iotdb.rpc.IoTDBConnectionException; +import org.apache.iotdb.rpc.StatementExecutionException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.sql.SQLException; +import java.util.List; +import java.util.concurrent.ExecutionException; + +/** + * @Author:liuxinyue + * @Package:com.template.controller + * @Project:cloud-server-c + * @name:TemplateController + * @Date:2024/9/20 12:12 + */ +@RestController +@RequestMapping("/template") +@AllArgsConstructor +@Tag(name = "报文模版管理",description = "报文模版管理") +public class TemplateController { + + @Autowired + private TemplateService templateService; + + + /** + * 报文模版列表 + * @return + */ + @PostMapping("/templateList") + @Operation(summary = "报文模版列表",description = "报文模版列表") + public Result> templateList() { + return Result.success(templateService.list()); + } + + + /** + * 解析报文 + * @param templateMessage + * @return + */ + @PostMapping("/messageParsing") + @Operation(summary = "报文解析",description = "报文解析") + public Result messageParsing(@RequestParam("templateMessage") String templateMessage) throws SQLException, IoTDBConnectionException, ClassNotFoundException, StatementExecutionException, ExecutionException, InterruptedException { + templateService.messageParsing(templateMessage); + return Result.success(); + } + + + +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/TemplateNeedController.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/TemplateNeedController.java new file mode 100644 index 0000000..98cbee6 --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/TemplateNeedController.java @@ -0,0 +1,23 @@ +package com.muyu.server.controller; + + +import com.muyu.common.core.domain.Result; +import com.muyu.server.service.TemplateNeedService; +import org.springframework.beans.factory.annotation.Autowired; +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; + +@RestController +@RequestMapping("/templateNeed") +public class TemplateNeedController { + @Autowired + private TemplateNeedService templateNeedService; + + + @GetMapping("/selectByTemplateId/{templateId}") + public Result selectByTemplateId(@PathVariable("templateId") Long templateId){ + return Result.success(templateNeedService.selectByTemplateId(templateId)); + } +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/TemplateService.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/TemplateService.java new file mode 100644 index 0000000..74bf79a --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/TemplateService.java @@ -0,0 +1,41 @@ +package com.muyu.server.controller; + +import com.alibaba.fastjson.JSONObject; + +/** + * @Author:liuxinyue + * @Package:com.muyu.controller + * @Project:cloud-server + * @name:TemplateService + * @Date:2024/9/22 22:02 + */ +public class TemplateService { + + public void messageParsing(String templateMessage){ + + //创建一个JSON对象 + JSONObject jsonObject = new JSONObject(); + if(templateMessage.length()<18){ + throw new RuntimeException("错误VIN码,不存在此车"); + } + + //将报文进行切割 + String[] split = templateMessage.split(" "); + StringBuilder stringBuilder = new StringBuilder(); + for (String s : split) { + + int i = Integer.parseInt(s, 16); + stringBuilder.append((char)i); + + } + //取出车辆的VIN码值 + + + } + + public static void main(String[] args) { + + } + + +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/WarnLogsController.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/WarnLogsController.java new file mode 100644 index 0000000..db9e4cf --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/WarnLogsController.java @@ -0,0 +1,56 @@ +package com.muyu.server.controller; + +import com.muyu.common.domain.WarnLogs; +import com.muyu.server.service.WarnLogsService; +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.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.domain.Result; + +/** + * 预警日志Controller + * + * @author sx + * @date 2024-09-20 + */ +@RestController +@RequestMapping("/logs") +public class WarnLogsController extends BaseController +{ + @Autowired + private WarnLogsService warnLogsService; + + /** + * 查询预警日志列表 + */ + @GetMapping("/selectWarnLogsList") + public Result selectWarnLogsList(){ + return Result.success(warnLogsService.selectWarnLogsList()); + } + + + /** + * 获取预警日志详细信息 + */ + @GetMapping(value = "selectWarnLogsById/{id}") + public Result selectWarnLogsById(@PathVariable("id") Long id) + { + return success(warnLogsService.selectWarnLogsById(id)); + } + + /** + * 新增预警日志 + */ + @PostMapping("/addWarnLogs") + public Result addWarnLogs(@RequestBody WarnLogs warnLogs) + { + int i = warnLogsService.addWarnLogs(warnLogs); + return i>0?Result.success():Result.error(); + } + +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/WarnRuleController.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/WarnRuleController.java new file mode 100644 index 0000000..8400742 --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/WarnRuleController.java @@ -0,0 +1,90 @@ +package com.muyu.server.controller; + +import java.util.Arrays; +import java.util.List; +import javax.annotation.Resource; + +import com.muyu.common.domain.WarnRule; +import com.muyu.server.service.WarnRuleService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +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.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.domain.Result; + +/** + * 预警规则Controller + * + * @author sx + * @date 2024-09-20 + */ +@RestController +@RequestMapping("/rule") +public class WarnRuleController extends BaseController +{ + @Resource + private WarnRuleService warnRuleService; + + /** + * 查询预警规则列表 + */ + @GetMapping("/selectWarnRuleRespList") + public Result selectWarnRuleRespList() + { + return Result.success(warnRuleService.selectWarnRuleRespList()); + } + + /** + * 获取预警规则详细信息 + */ + + @GetMapping(value = "selectById/{id}") + public Result> selectById(@PathVariable("id") Long id) + { + return success(warnRuleService.selectWarnRuleById(id)); + } + + /** + * 新增预警规则 + */ + + @PostMapping("/addWarnRule") + public Result addWarnRule(@RequestBody WarnRule warnRule) + { + int i = warnRuleService.addWarnRule(warnRule); + return i>0?Result.success():Result.error(); + } + + /** + * 修改预警规则 + */ + + @PostMapping("/updWarnRule") + public Result updWarnRule(@RequestBody WarnRule warnRule) + { + int i = warnRuleService.updWarnRule(warnRule); + return i>0?Result.success():Result.error(); + } + + /** + * 删除预警规则 + */ + @DeleteMapping("/{ids}") + public Result remove(@PathVariable("ids") Long[] ids) + { + warnRuleService.removeBatchByIds(Arrays.asList(ids)); + return success(); + } + + + + //根据策略ID查规则 + @GetMapping("/selectListByStrategyId/{strategyId}") + public Result selectListByStrategyId(@PathVariable("strategyId") Long strategyId){ + return Result.success(warnRuleService.selectListByStrategyId(strategyId)); + } +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/WarnStrategyController.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/WarnStrategyController.java new file mode 100644 index 0000000..8c0a9ee --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/WarnStrategyController.java @@ -0,0 +1,89 @@ +package com.muyu.server.controller; + +import javax.annotation.Resource; + +import com.muyu.common.domain.WarnStrategy; +import com.muyu.common.domain.req.WarnStrategyReq; +import com.muyu.server.service.WarnStrategyService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +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.muyu.common.core.web.controller.BaseController; +import com.muyu.common.core.domain.Result; + +/** + * 预警策略Controller + * + * @author sx + * @date 2024-09-20 + */ +@RestController +@RequestMapping("/strategy") +public class WarnStrategyController extends BaseController +{ + @Resource + private WarnStrategyService warnStrategyService; + + /** + * 查询预警策略列表 + */ + @PostMapping("/selectWarnStrategyList") + public Result selectWarnStrategyList(@RequestBody WarnStrategyReq warnStrategyReq) + { + return Result.success(warnStrategyService.selectWarnStrategyList(warnStrategyReq)); + } + + + /** + * 获取预警策略详细信息 + */ + @GetMapping( "selectById/{id}") + public Result selectById(@PathVariable("id") Long id) + { + return success(warnStrategyService.selectWarnStrategyById(id)); + } + + /** + * 新增预警策略 + */ + @PostMapping("/addWarnStrategy") + public Result addWarnStrategy(@RequestBody WarnStrategy warnStrategy) + { + Integer i = warnStrategyService.addWarnStrategy(warnStrategy); + return i>0?Result.success():Result.error(); + } + + /** + * 修改预警策略 + */ + @PostMapping("/updWarnStrategy") + public Result updWarnStrategy(@RequestBody WarnStrategy warnStrategy) + { + Integer i = warnStrategyService.updWarnStrategy(warnStrategy); + return i>0?Result.success():Result.error(); + } + + /** + * 删除预警策略 + */ + @DeleteMapping("deleteWarnStrategy/{id}") + public Result deleteWarnStrategy(@PathVariable("id") Long id) + { + Integer i = warnStrategyService.deleteWarnStrategy(id); + return i>0?Result.success():Result.error(); + } + + + + /** + * 根据车辆类型ID查询策略 + */ + @GetMapping("/selectListByCarType/{carTypeId}") + public Result selectListByCarType(@PathVariable("carTypeId") Long carTypeId) { + return Result.success(warnStrategyService.selectListByCarType(carTypeId)); + } +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/form/DeleteEnterpriseByIds.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/form/DeleteEnterpriseByIds.java new file mode 100644 index 0000000..761cb1c --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/form/DeleteEnterpriseByIds.java @@ -0,0 +1,17 @@ +package com.muyu.server.controller.form; + +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +/** + * @Author YuPing + * @Description 删除企业信息 + * @Version 1.0 + * @Data 2024-09-26 22:05:47 + */ +@Data +public class DeleteEnterpriseByIds { + + @NotEmpty(message = "ids不能为空") + private Integer[] ids; +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/form/InsertEnterprise.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/form/InsertEnterprise.java new file mode 100644 index 0000000..cb5e206 --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/form/InsertEnterprise.java @@ -0,0 +1,24 @@ +package com.muyu.server.controller.form; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * @Author YuPing + * @Description 新增企业信息表单 + * @Version 1.0 + * @Data 2024-09-26 21:16:06 + */ +@Data +public class InsertEnterprise { + + @NotBlank(message = "enterprise企业名称不能为空") + private String enterpriseName; + + @NotNull(message = "enterpriseCarCount企业车辆数量不能为空") + private Integer enterpriseCarCount; + + @NotNull(message = "enterpriseFenceCount企业围栏组不能为空") + private Integer enterpriseFenceCount; +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/form/SearchEnterpriseName.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/form/SearchEnterpriseName.java new file mode 100644 index 0000000..b859940 --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/form/SearchEnterpriseName.java @@ -0,0 +1,28 @@ +package com.muyu.server.controller.form; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.hibernate.validator.constraints.Range; + +/** + * @Author YuPing + * @Description 企业名称查询表单 + * @Version 1.0 + * @Data 2024-09-26 20:48:33 + */ +@Data +public class SearchEnterpriseName { + +// @Pattern(regexp = "^[0-9a-zA-Z\\u4e00-\\u9fa5]{1,10}$", message = "enterpriseName内容不正确") + private String enterpriseName; + + @NotNull(message = "page不能为空") + @Min(value = 1, message = "page不能小于1") + private Integer page; + + @NotNull(message = "length不能为空") + @Range(min = 10, max = 50, message = "length必须在10~50之间") + private Integer length; + +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/form/UpdateEnterprise.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/form/UpdateEnterprise.java new file mode 100644 index 0000000..823b08c --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/controller/form/UpdateEnterprise.java @@ -0,0 +1,30 @@ +package com.muyu.server.controller.form; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import lombok.Data; + +/** + * @Author YuPing + * @Description 修改企业信息 + * @Version 1.0 + * @Data 2024-09-26 21:22:05 + */ +@Data +public class UpdateEnterprise { + @NotNull(message = "enterpriseId企业编号不能为空") + @Min(value = 1,message = "enterpriseId不能小于1") + private Integer enterpriseId; + + @NotBlank(message = "enterpriseName企业名称不能为空") + @Pattern(regexp = "^[0-9a-zA-Z\\u4e00-\\u9fa5]{1,10}$", message = "enterpriseName企业名称不能为空") + private String enterpriseName; + + @NotNull(message = "enterpriseCarCount企业车辆数量不能为空") + private Integer enterpriseCarCount; + + @NotNull(message = "enterpriseFenceCount企业围栏组不能为空") + private Integer enterpriseFenceCount; +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/CarTypeMapper.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/CarTypeMapper.java new file mode 100644 index 0000000..6d44e75 --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/CarTypeMapper.java @@ -0,0 +1,12 @@ +package com.muyu.server.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.common.domain.CarType; +import com.muyu.common.domain.resp.CarTypeResp; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +@Mapper +public interface CarTypeMapper extends BaseMapper { + CarTypeResp selectCarTypeRespList(@Param("id")Long id); +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/DataTypeMapper.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/DataTypeMapper.java new file mode 100644 index 0000000..3cc55bf --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/DataTypeMapper.java @@ -0,0 +1,17 @@ +package com.muyu.server.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.common.domain.DataType; +import org.apache.ibatis.annotations.Mapper; + +/** + * @Author:liuxinyue + * @Package:com.sheep.mapper + * @Project:cloud-server-c + * @name:DataTypeMapper + * @Date:2024/9/20 9:16 + */ +@Mapper +public interface DataTypeMapper extends BaseMapper { + +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/ElectronicFenceGroupMapper.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/ElectronicFenceGroupMapper.java new file mode 100644 index 0000000..b5604d1 --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/ElectronicFenceGroupMapper.java @@ -0,0 +1,16 @@ +package com.muyu.server.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.common.domain.database.ElectronicFenceGroup; +import org.apache.ibatis.annotations.Mapper; + +/** + * @Author:yuping + * @Package:com.muyu.fence.mapper + * @Project:cloud-server + * @name:FenceGroupMapper + * @Date:2024/9/18 15:19 + */ +@Mapper +public interface ElectronicFenceGroupMapper extends BaseMapper { +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/ElectronicFenceMapper.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/ElectronicFenceMapper.java new file mode 100644 index 0000000..0a230db --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/ElectronicFenceMapper.java @@ -0,0 +1,17 @@ +package com.muyu.server.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.common.domain.database.ElectronicFence; +import org.apache.ibatis.annotations.Mapper; + +/** + * @Author:yuping + * @Package:com.muyu.fence.mapper + * @Project:cloud-server + * @name:ElectronicFenceMapper + * @Date:2024/9/17 19:29 + */ +@Mapper +public interface ElectronicFenceMapper extends BaseMapper { + +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/EnterpriseDao.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/EnterpriseDao.java new file mode 100644 index 0000000..7e64554 --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/EnterpriseDao.java @@ -0,0 +1,30 @@ +package com.muyu.server.mapper; + +import com.muyu.common.domain.Enterprise; +import org.apache.ibatis.annotations.Mapper; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +@Mapper +public interface EnterpriseDao { + // 查询分页信息 + public ArrayList selectEnterprise(Map param); + //查询企业记录总数 + public long selectEnterpriseCount(); + + + //新增企业信息 + public int insert(Enterprise enterprise); + + + //根据编号查询企业信息 + public HashMap searchById(int enterpriseId); + //修改企业信息 + public int updateEnterprise(Enterprise enterprise); + + + //删除企业信息 + public int deleteByIds(Integer[] ids); +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/FenceGroupMidMapper.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/FenceGroupMidMapper.java new file mode 100644 index 0000000..7f57792 --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/FenceGroupMidMapper.java @@ -0,0 +1,16 @@ +package com.muyu.server.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.common.domain.database.FenceGroupMid; +import org.apache.ibatis.annotations.Mapper; + +/** + * @Author:yuping + * @Package:com.muyu.fence.mapper + * @Project:cloud-server + * @name:FenceGroupMidMapper + * @Date:2024/9/20 11:35 + */ +@Mapper +public interface FenceGroupMidMapper extends BaseMapper { +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/MessageTemplateTypeMapper.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/MessageTemplateTypeMapper.java new file mode 100644 index 0000000..5fcd085 --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/MessageTemplateTypeMapper.java @@ -0,0 +1,19 @@ +package com.muyu.server.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.common.domain.MessageTemplateType; +import org.apache.ibatis.annotations.Mapper; + +/** + * @Author:liuxinyue + * @Package:com.sheep.message.mapper + * @Project:cloud-server-c + * @name:MessageTemplateTypeMapper + * @Date:2024/9/19 14:35 + */ +@Mapper +public interface MessageTemplateTypeMapper extends BaseMapper { + + + +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/SysCarFaultLogMapper.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/SysCarFaultLogMapper.java new file mode 100644 index 0000000..3f41996 --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/SysCarFaultLogMapper.java @@ -0,0 +1,34 @@ +package com.muyu.server.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.common.domain.SysCarFaultLog; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 故障记录日志 管理层 + * @author YuanZiLong + * @package com.muyu.breakdown.mapper + * @name: SysCarFaultLogMapper + * @date: 2024/9/22 21:06 + */ +@Mapper +public interface SysCarFaultLogMapper extends BaseMapper { + + + + + + + public ListselectSysCarFaultLogList(SysCarFaultLog SysCarFaultLog); + + + public ListlistStatusSolve(SysCarFaultLog sysCarFaultLog); + + + public ListlistStatusProcess(SysCarFaultLog sysCarFaultLog); + + + public ListlistStatusIgnore(SysCarFaultLog sysCarFaultLog); +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/SysCarFaultMapper.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/SysCarFaultMapper.java new file mode 100644 index 0000000..5cebf68 --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/SysCarFaultMapper.java @@ -0,0 +1,34 @@ +package com.muyu.server.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.common.domain.SysCarFault; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + * 车辆故障码 管理层 + * @author YuanZiLong + * @Package: com.muyu.breakdown.mapper + * @Name: SysCarFaultMapper + * @Date: 2024/9/20 10:57 + */ +@Mapper +public interface SysCarFaultMapper extends BaseMapper { + + //根据添加的故障码进行查询 + @Select("select fault_code from sys_car_fault where fault_code=#{faultCode}") + SysCarFault selectFaultByFaultCode(String faultCode); + + /** + * 查询故障码信息 + * @param ids 故障码信息主键 + * @return 故障码信息 + */ + public List selectSysCarFaultIds(String[] ids); + + + + +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/SysCarFaultMessageMapper.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/SysCarFaultMessageMapper.java new file mode 100644 index 0000000..e02c29b --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/SysCarFaultMessageMapper.java @@ -0,0 +1,29 @@ +package com.muyu.server.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.common.domain.SysCarFaultMessage; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + * 站内信 管理层 + * @author YuanZiLong + * @packer: com.muyu.breakdown.mapper + * @name: SysCarFaultMessageMapper + * @date: 2024/9/22 11:59 + */ +@Mapper +public interface SysCarFaultMessageMapper extends BaseMapper { + + // 查询状态等于1的记录 + @Select("select * from sys_car_fault_message where status=1") + public ListlistStatusOnt( ); + //查询状态等于2的记录 + @Select("select * from sys_car_fault_message where status=2") + public ListlistStatusTwo( ); + + + +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/SysCarLogMapper.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/SysCarLogMapper.java new file mode 100644 index 0000000..69cf4b1 --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/SysCarLogMapper.java @@ -0,0 +1,9 @@ +package com.muyu.server.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.common.domain.SysCarLog; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface SysCarLogMapper extends BaseMapper { +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/SysCarMapper.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/SysCarMapper.java new file mode 100644 index 0000000..5310c28 --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/SysCarMapper.java @@ -0,0 +1,27 @@ +package com.muyu.server.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.common.domain.SysCar; +import com.muyu.common.domain.req.SysCarReq; +import com.muyu.common.domain.resp.SysCarFaultLogVo; +import com.muyu.common.domain.resp.SysCarVo; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface SysCarMapper extends BaseMapper { + List selectSysCarVoList(SysCarReq sysCarReq); + + SysCarVo selectSysCarVoById(@Param("id") Long id); + + List findFenceByCarVin(@Param("carVin") String carVin); + + //修改车辆 + Integer updSysCarById(SysCar sysCar); + + //添加车辆信息 + Integer addSysCar(SysCar sysCar); + +} diff --git a/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/TemplateMapper.java b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/TemplateMapper.java new file mode 100644 index 0000000..b732762 --- /dev/null +++ b/cloud-modules/saas/saas-server/src/main/java/com/muyu/server/mapper/TemplateMapper.java @@ -0,0 +1,23 @@ +package com.muyu.server.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.muyu.common.domain.MessageTemplateType; +import com.muyu.common.domain.Template; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * @Author:liuxinyue + * @Package:com.template.mapper + * @Project:cloud-server-c + * @name:TemplateMapper + * @Date:2024/9/20 12:13 + */ +@Mapper +public interface TemplateMapper extends BaseMapper