This commit is contained in:
2026-04-17 10:32:57 +08:00
parent 572ad1205f
commit 17c52c978c
691 changed files with 72342 additions and 0 deletions

39
.gitignore vendored Normal file
View File

@@ -0,0 +1,39 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
.kotlin
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

33
.idea/encodings.xml generated Normal file
View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/maibu-admin/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/maibu-admin/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/maibu-common/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/maibu-common/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/maibu-external/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/maibu-external/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/maibu-framework/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/maibu-framework/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/maibu-framwork/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/maibu-framwork/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/maibu-iotDA/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/maibu-iotDA/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/maibu-netty-server/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/maibu-netty-server/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/maibu-service/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/maibu-service/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/maibu-web-middleware/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/maibu-web-middleware/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/maibu-webrtc-server/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/maibu-webrtc-server/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
<file url="file://$APPLICATION_HOME_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$APPLICATION_HOME_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

814
.idea/workspace.xml generated Normal file
View File

@@ -0,0 +1,814 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="84ac1b15-3cfa-4375-9957-45b7b581d04e" name="Changes" comment="#init">
<change afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/encodings.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/vcs.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/pom.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/ali/AliSmsController.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/ali/AliSmsService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/ali/AliSmsUtil.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/aspect/WebLogAspect.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/aspectj/DataScopeAspect.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/aspectj/DataSourceAspect.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/aspectj/LogAspect.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/aspectj/RateLimiterAspect.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/config/ApplicationConfig.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/config/CacheConfig.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/config/CaptchaConfig.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/config/DataSourceConfig.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/config/DruidConfig.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/config/FastJson2JsonRedisSerializer.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/config/FilterConfig.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/config/KaptchaTextCreator.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/config/MyBatisConfig.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/config/MybatisPlusMetaObjectHandler.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/config/MybatisPlusPageConfigurer.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/config/OtaThreadPoolConfig.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/config/RedisConfig.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/config/RedissonConfig.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/config/ResourcesConfig.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/config/SecurityConfig.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/config/ServerConfig.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/config/SqlFilterArgumentResolver.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/config/ThreadPoolConfig.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/config/properties/DruidProperties.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/config/properties/PermitAllUrlProperties.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/config/properties/RedissonProperties.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/config/sharding/ShardingAlgorithmTool.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/config/sharding/ShardingTablesLoadRunner.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/config/sharding/TimeShardingAlgorithm.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/config/sharding/enums/ShardingTableCacheEnum.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/datasource/DynamicDataSource.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/datasource/DynamicDataSourceContextHolder.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/handler/KeyPrefixHandler.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/interceptor/LanguageInterceptor.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/interceptor/RepeatSubmitInterceptor.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/interceptor/impl/SameUrlDataInterceptor.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/manager/AsyncManager.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/manager/CaffeineCacheDecorator.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/manager/DeleteKeyGenerator.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/manager/ShutdownManager.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/manager/SpringCacheManager.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/manager/factory/AsyncFactory.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/mybatis/LambdaQueryWrapperX.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/mybatis/QueryWrapperX.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/mybatis/mapper/BaseMapperX.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/mybatis/utils/MyBatisUtils.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/security/context/AuthenticationContextHolder.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/security/context/PermissionContextHolder.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/security/filter/JwtAuthenticationTokenFilter.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/security/handle/AuthenticationEntryPointImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/security/handle/LogoutSuccessHandlerImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/utils/CacheUtils.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/utils/QueueUtils.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/utils/RedisUtils.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/web/domain/Server.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/web/domain/dto/LoginResultDto.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/web/domain/server/Cpu.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/web/domain/server/Jvm.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/web/domain/server/Mem.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/web/domain/server/Sys.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/web/domain/server/SysFile.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/web/exception/GlobalExceptionHandler.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/web/service/PermissionService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/web/service/SysLoginService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/web/service/SysPasswordService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/web/service/SysPermissionService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/web/service/SysRegisterService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/web/service/TokenService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-framework/src/main/java/com/maibu/web/service/UserDetailsServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-iotDA/pom.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-iotDA/src/main/java/com/maibu/controller/IoTDADeviceController.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-iotDA/src/main/java/com/maibu/dto/IoTDACommandDTO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-iotDA/src/main/java/com/maibu/dto/IoTDARuleDTO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-iotDA/src/main/java/com/maibu/entity/IoTDADeviceRunningStatus.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-iotDA/src/main/java/com/maibu/entity/IoTDADeviceSecret.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-iotDA/src/main/java/com/maibu/init/InitIotResource.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-iotDA/src/main/java/com/maibu/mapper/IoTDADeviceSecretMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-iotDA/src/main/java/com/maibu/memory/IotGlobalMemory.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-iotDA/src/main/java/com/maibu/service/IoTDADeviceService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-iotDA/src/main/java/com/maibu/utils/IoTDAAMQClient.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-iotDA/src/main/java/com/maibu/utils/IoTDAMqttClient.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-iotDA/src/main/resources/ca.jks" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-netty-server/pom.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-netty-server/src/main/java/com/maibu/Main.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/pom.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/cache/IDeviceCache.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/cache/IFirmwareCache.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/cache/IModbusConfigCache.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/cache/IOtaTaskCache.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/cache/ITSLCache.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/cache/ITSLValueCache.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/cache/SceneModelTagCache.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/cache/impl/DeviceCacheImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/cache/impl/FirmwareCacheImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/cache/impl/ModbusConfigCacheImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/cache/impl/OtaTaskCacheImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/cache/impl/SceneModelTagCacheImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/cache/impl/TSLCacheImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/cache/impl/TSLValueCacheImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/convert/DeviceRecordConvert.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/convert/FirmwareTaskConvert.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/convert/ProductModbusJobConvert.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/convert/ProductSubGatewayConvert.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/Alert.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/AlertLog.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/AlertNotifyTemplate.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/AlertScene.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/Bridge.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/Category.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/CommandPreferences.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/Device.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/DeviceAlertUser.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/DeviceGroup.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/DeviceJob.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/DeviceLog.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/DevicePlan.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/DevicePlanTask.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/DeviceRecord.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/DeviceRunStatistics.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/DeviceRunningStatusHistory.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/DeviceShare.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/DeviceStatusRecordDTO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/DeviceTaskPlanRule.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/DeviceUser.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/EventLog.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/Firmware.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/FirmwareTask.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/FirmwareTaskDetail.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/FunctionLog.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/GoviewProject.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/GoviewProjectData.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/Group.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/HttpClient.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/IoTCommonDevice.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/IoTCommonDeviceAction.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/IoTCommonProduct.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/IoTCommonThingsModel.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/ModbusConfig.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/ModbusJob.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/ModbusParams.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/MqttClient.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/MultipleDataSource.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/News.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/NewsCategory.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/OrderControl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/Product.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/ProductAuthorize.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/ProductModbusJob.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/ProductSubGateway.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/Protocol.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/Scene.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/SceneDevice.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/SceneModel.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/SceneModelData.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/SceneModelDevice.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/SceneModelTag.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/SceneScript.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/SceneTagPoints.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/Script.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/SimulateLog.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/SipRelation.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/SocialPlatform.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/SocialUser.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/SubGateway.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/ThingsModel.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/ThingsModelJsonTemplate.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/ThingsModelTemplate.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/UserSocialProfile.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/domain/ViewConfig.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/enums/ConnectType.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/enums/DeviceTaskPlanRepeatType.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/enums/DeviceTaskStaus.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/enums/DeviceType.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/AlertLogMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/AlertMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/BridgeMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/CategoryMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/CommandPreferencesMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/DeviceAlertUserMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/DeviceJobMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/DeviceLogMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/DeviceMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/DevicePlanMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/DevicePlanTaskMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/DeviceRecordMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/DeviceRunStatisticsMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/DeviceShareMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/DeviceStatusHistoryMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/DeviceUserMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/EventLogMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/FirmwareMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/FirmwareTaskDetailMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/FirmwareTaskMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/FunctionLogMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/GoviewProjectDataMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/GoviewProjectMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/GroupMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/IoTCommonDevicesMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/ModbusConfigMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/ModbusJobMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/ModbusParamsMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/MqttClientMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/NewsCategoryMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/NewsMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/OrderControlMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/ProductAuthorizeMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/ProductMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/ProductModbusJobMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/ProductSubGatewayMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/ProtocolMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/SceneDeviceMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/SceneMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/SceneModelDataMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/SceneModelDeviceMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/SceneModelMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/SceneModelTagMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/SceneScriptMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/SceneTagPointsMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/ScriptMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/SipRelationMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/SocialPlatformMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/SocialUserMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/SpeakerMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/SubGatewayMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/ThingsModelMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/ThingsModelTemplateMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/ViewConfigMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/mapper/WorkRecordMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/memory/CommonMemory.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/Action.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/AlertCountVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/AlertSceneSendVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/AuthenticateInputModel.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/CategoryNews.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ChangeProductStatusModel.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/DataResult.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/DeviceAlertCount.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/DeviceAlertUserVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/DeviceAllShortOutput.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/DeviceAssignmentVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/DeviceGroupInput.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/DeviceHistoryParam.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/DeviceImportVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/DeviceMqttConnectVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/DeviceMqttVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/DeviceNumberAndProductId.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/DeviceNumberStatus.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/DeviceParamZhValue.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/DeviceRecordVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/DeviceRelateAlertLogVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/DeviceRelateUserInput.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/DeviceReport.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/DeviceRunningStatus.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/DeviceShortOutput.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/DeviceStatistic.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/DeviceStatusVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/EmqxApiConfig.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/FirmwareTaskDetailInput.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/FirmwareTaskDetailOutput.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/FirmwareTaskDeviceStatistic.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/FirmwareTaskInput.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/HistoryModel.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/IdAndName.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/IdOutput.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ImportThingsModelInput.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ModbusJobBo.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ModbusPollBo.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/MonitorModel.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/MqttAuthenticationModel.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/MqttClientConfig.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/MqttClientConnectModel.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/MqttInfoModel.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/NtpModel.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ProductAuthenticateModel.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ProductAuthorizeVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ProductCode.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/RegisterUserInput.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/RegisterUserOutput.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/SceneDeviceBindVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/SceneTerminalUserVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ScriptCondition.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ScriptTemplate.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/SerialNumberVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/Specs.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ThingsModelItem/ArrayModelOutput.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ThingsModelItem/BoolModelOutput.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ThingsModelItem/Datatype.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ThingsModelItem/DecimalModelOutput.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ThingsModelItem/EnumItem.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ThingsModelItem/EnumItemOutput.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ThingsModelItem/EnumModelOutput.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ThingsModelItem/IntegerModelOutput.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ThingsModelItem/ReadOnlyModelOutput.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ThingsModelItem/StringModelOutput.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ThingsModelItem/ThingsModel.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ThingsModelItem/ThingsModelItemBase.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ThingsModelLogCountVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ThingsModelPerm.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ThingsModelSimVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ThingsModels/EventDto.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ThingsModels/FunctionDto.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ThingsModels/IdentifierVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ThingsModels/PropertyDto.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ThingsModels/ThingsItems.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ThingsModels/ThingsModelShadow.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ThingsModels/ThingsModelValueItem.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ThingsModels/ThingsModelValueItemDto.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ThingsModels/ThingsModelValuesOutput.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ThingsModels/ThingsModelsDto.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/ThingsModels/ValueItem.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/TriggerParameter.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/UserAndTenant.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/UserClient.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/UserIdDeviceIdModel.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/VariableReadVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/WorkRecord.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/dashBoard/DashMqttMetrics.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/dashBoard/DashMqttStat.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/dto/DeviceRtDto.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/dto/ThingsModelDTO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/gateWay/GateSubDeviceVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/gateWay/ProductSubGatewayAddVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/gateWay/ProductSubGatewayVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/gateWay/SubDeviceAddVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/gateWay/SubDeviceListVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/goview/GoviewProjectVo.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/login/AuthRequestWrap.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/login/BindIdValue.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/login/LoginIdValue.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/login/WeChatLoginQrRes.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/modbus/ModbusAndThingsVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/modbus/ModbusConfigVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/modbus/ModbusDataImport.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/modbus/ModbusIoImport.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/modbus/ModbusJobVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/modbus/ModbusPollJob.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/modbus/ProductModbusJobVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/param/DataCenterParam.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/scenemodel/CusDeviceVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/scenemodel/SceneDeviceThingsModelVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/scenemodel/SceneHistoryParam.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/scenemodel/SceneModelDataDTO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/scenemodel/SceneModelDeviceVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/scenemodel/SceneModelTagCacheVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/scenemodel/SceneModelTagCycleVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/speaker/DuerosReportVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/speaker/OauthAccessTokenReportVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/speaker/OauthClientDetailsReportVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/varTemp/DeviceSlavePoint.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/varTemp/DeviceTemp.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/varTemp/EnumClass.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/varTemp/SlaveIdAndId.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/model/varTemp/SyncModel.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/ruleEngine/DataSourceService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/ruleEngine/HttpBridge.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/ruleEngine/MessageArrivedHandler.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/ruleEngine/MqttBridge.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/ruleEngine/MqttClientFactory.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/ruleEngine/MsgContext.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/ruleEngine/MsgContextService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/ruleEngine/RuleProcess.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/DataCenterService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IAlertLogService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IAlertService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IAuthRequestFactory.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IBridgeService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/ICategoryService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/ICommandPreferencesService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IDeviceAlertUserService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IDeviceJobService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IDeviceLogService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IDeviceRecordService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IDeviceRuntimeService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IDeviceService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IDeviceShareService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IDeviceUserService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IEventLogService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IFirmwareService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IFunctionLogService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IGoviewProjectDataService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IGoviewProjectService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IGroupService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IHttpClientService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IModbusConfigService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IModbusJobService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IModbusParamsService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IMqttClientService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/INewsCategoryService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/INewsService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IOrderControlService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IProductAuthorizeService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IProductModbusJobService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IProductService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IProductSubGatewayService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IProtocolService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/ISceneDeviceService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/ISceneModelDataService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/ISceneModelDeviceService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/ISceneModelService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/ISceneModelTagService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/ISceneScriptService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/ISceneService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/ISceneTagPointsService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IScriptService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/ISipRelationService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/ISocialLoginService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/ISocialPlatformService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/ISocialUserService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/ISubGatewayService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IThingsModelService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IThingsModelTemplateService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IToolService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IUserSocialProfileService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IViewConfigService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IWebRtcService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/IWorkRecordService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/SpeakerService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/AlertLogServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/AlertServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/AuthRequestFactoryImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/AuthStateRedisCache.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/BridgeServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/CategoryServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/CommandPreferencesServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/DataCenterServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/DeviceAlertUserServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/DeviceLogServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/DeviceRecordServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/DeviceRuntimeServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/DeviceServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/DeviceShareServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/DeviceUserServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/EventLogServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/FirmwareServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/FunctionLogServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/GoviewProjectDataServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/GoviewProjectServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/GroupServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/HttpClientServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/ModbusConfigServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/ModbusJobServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/ModbusParamsServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/MqttClientServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/NewsCategoryServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/NewsServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/OrderControlServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/ProductAuthorizeServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/ProductModbusJobServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/ProductServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/ProductSubGatewayServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/ProtocolServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/SceneDeviceServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/SceneModelDataServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/SceneModelDeviceServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/SceneModelServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/SceneModelTagServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/SceneScriptServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/SceneServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/SceneTagPointsServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/ScriptServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/SipRelationServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/SocialLoginServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/SocialPlatformServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/SocialUserServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/SpeakerServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/SubGatewayServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/ThingsModelServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/ThingsModelTemplateServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/ToolServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/UserSocialProfileServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/ViewConfigServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/service/impl/WorkRecordServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/tdengine/config/TDengineConfig.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/tdengine/dao/TDDeviceLogDAO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/tdengine/init/ApplicationStarted.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/tdengine/service/ILogService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/tdengine/service/factory/LogServiceFactory.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/tdengine/service/impl/MySqlLogServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/tdengine/service/impl/TdengineLogServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/tdengine/service/model/TdLogDto.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/util/AESUtils.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/util/JobCronUtils.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/util/MqttMessageUtils.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/util/SnowflakeIdWorker.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/util/VelocityInitializer.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/util/VelocityUtils.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/wechat/WeChatCallbackService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/wechat/WeChatService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/wechat/impl/WeChatCallbackServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/wechat/impl/WeChatServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/wechat/vo/WxBindReqVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/java/com/maibu/wechat/vo/WxCancelBindReqVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/AlertLogMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/AlertMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/BridgeMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/CategoryMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/CommandPreferencesMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/DeviceAlertUserMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/DeviceJobMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/DeviceLogMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/DeviceMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/DeviceRecordMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/DeviceShareMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/DeviceUserMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/EventLogMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/FirmwareMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/FirmwareTaskDetailMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/FirmwareTaskMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/FunctionLogMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/GoviewProjectDataMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/GoviewProjectMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/GroupMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/ModbusConfigMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/ModbusJobMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/ModbusParamsMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/NewsCategoryMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/NewsMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/OrderControlMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/ProductAuthorizeMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/ProductMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/ProductModbusJobMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/ProductSubGatewayMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/ProtocolMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/SceneDeviceMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/SceneMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/SceneModelDataMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/SceneModelDeviceMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/SceneModelMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/SceneModelTagMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/SceneScriptMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/SceneTagPointsMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/ScriptMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/SipRelationMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/SocialPlatformMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/SocialUserMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/SpeakerMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/SubGatewayMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/ThingsModelMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/ThingsModelTemplateMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/ViewConfigMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/iot/WorkRecordMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-iot-service/src/main/resources/mapper/tdengine/TDDeviceLogMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/pom.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/domain/AppLanguage.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/domain/AppPreferences.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/domain/SysCache.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/domain/SysClient.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/domain/SysConfig.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/domain/SysLoginConfig.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/domain/SysLogininfor.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/domain/SysNotice.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/domain/SysOperLog.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/domain/SysPost.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/domain/SysRoleDept.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/domain/SysRoleMenu.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/domain/SysUserOnline.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/domain/SysUserPost.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/domain/SysUserRole.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/domain/vo/MetaVo.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/domain/vo/RouterVo.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/domain/vo/SysDeptTypeVO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/enums/Language.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/mapper/AppLanguageMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/mapper/AppPreferencesMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/mapper/SysClientMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/mapper/SysConfigMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/mapper/SysDeptMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/mapper/SysDictDataMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/mapper/SysDictTypeMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/mapper/SysLoginConfigMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/mapper/SysLogininforMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/mapper/SysMenuMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/mapper/SysNoticeMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/mapper/SysOperLogMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/mapper/SysPostMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/mapper/SysRoleDeptMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/mapper/SysRoleMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/mapper/SysRoleMenuMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/mapper/SysTranslateMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/mapper/SysUserClientMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/mapper/SysUserMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/mapper/SysUserPostMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/mapper/SysUserRoleMapper.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/IAppLanguageService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/IAppPreferencesService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/ISysClientService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/ISysConfigService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/ISysDeptService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/ISysDictDataService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/ISysDictTypeService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/ISysLogininforService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/ISysMenuService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/ISysNoticeService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/ISysOperLogService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/ISysPostService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/ISysRoleService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/ISysTranslateService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/ISysUserClientService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/ISysUserOnlineService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/ISysUserService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/impl/AppLanguageServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/impl/AppPreferencesServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/impl/SysClientServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/impl/SysConfigServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/impl/SysDeptServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/impl/SysDictDataServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/impl/SysDictTypeServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/impl/SysLogininforServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/impl/SysMenuServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/impl/SysNoticeServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/impl/SysOperLogServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/impl/SysPostServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/impl/SysRoleServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/impl/SysTranslateServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/impl/SysUserClientServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/impl/SysUserOnlineServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/java/com/maibu/service/impl/SysUserServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/resources/system/AppLanguageMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/resources/system/AppPreferencesMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/resources/system/SysClientMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/resources/system/SysConfigMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/resources/system/SysDeptMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/resources/system/SysDictDataMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/resources/system/SysDictTypeMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/resources/system/SysLogininforMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/resources/system/SysMenuMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/resources/system/SysNoticeMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/resources/system/SysOperLogMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/resources/system/SysPostMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/resources/system/SysRoleDeptMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/resources/system/SysRoleMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/resources/system/SysRoleMenuMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/resources/system/SysTranslateMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/resources/system/SysUserClientMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/resources/system/SysUserMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/resources/system/SysUserPostMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/maibu-system-service/src/main/resources/system/SysUserRoleMapper.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-service/pom.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/pom.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/common/MiddleCommandConstant.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/common/MiddleConstant.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/config/RestTemplateConfig.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/control/DiffSteer.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/controller/WebMiddlewareController.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/dto/DeviceRunningStatus.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/dto/DeviceStatusDetail.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/dto/LawnMoverProtocolEntity.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/dto/MesType.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/dto/NettySwitchDeviceDTO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/dto/ObstacleMessageDTO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/dto/RemoteData.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/dto/ResultDTO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/dto/WebAuthResponseDTO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/dto/WebHasLoginDTO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/dto/WebMessageBaseDTO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/dto/WebOlineStatusMessageDTO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/dto/WebStatusMessageDTO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/dto/WebSwitchControlResponseDTO.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/memory/MiddleGlobalMemory.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/memory/SystemMonitor.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/netty/InitResource.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/netty/NettyClient.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/netty/handler/ClientHandler.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/netty/handler/MiddleHeaderFooterDecoder.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/netty/handler/MiddleHexDecoder.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/netty/handler/MiddleHexEncoder.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/utils/ControlUtils.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/utils/HexUtils.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/utils/HttpService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-web-middleware/src/main/java/com/maibu/websocket/WebsocketHandler.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-webrtc-server/pom.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-webrtc-server/src/main/java/com/maibu/config/RestTemplateConfig.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-webrtc-server/src/main/java/com/maibu/config/SrsRedisKeyEventListener.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-webrtc-server/src/main/java/com/maibu/controller/SrsHookController.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-webrtc-server/src/main/java/com/maibu/dto/SrsHookRequest.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-webrtc-server/src/main/java/com/maibu/service/PlaySessionService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-webrtc-server/src/main/java/com/maibu/service/impl/PlaySessionServiceImpl.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-webrtc-server/src/main/java/com/maibu/util/Constants.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-webrtc-server/src/main/java/com/maibu/util/TokenUtil.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-webrtc-server/src/main/resources/log4j2.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/maibu-webrtc-server/src/main/resources/logback-spring.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/pom.xml" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="MavenImportPreferences">
<option name="generalSettings">
<MavenGeneralSettings>
<option name="customMavenHome" value="D:\develop\apache-maven-3.9.11" />
<option name="mavenHomeTypeForPersistence" value="CUSTOM" />
<option name="userSettingsFile" value="D:\develop\apache-maven-3.9.11\conf\settings.xml" />
</MavenGeneralSettings>
</option>
</component>
<component name="McpProjectServerCommands">
<commands />
<urls />
</component>
<component name="ProjectColorInfo">{
&quot;associatedIndex&quot;: 1
}</component>
<component name="ProjectId" id="3CQbi1mIMWJBbYCCmIj5tsS3lct" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"ASKED_SHARE_PROJECT_CONFIGURATION_FILES": "true",
"Maven.MiddlePlatform [clean].executor": "Run",
"Maven.MiddlePlatform [install].executor": "Run",
"ModuleVcsDetector.initialDetectionPerformed": "true",
"RunOnceActivity.MCP Project settings loaded": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
"RunOnceActivity.typescript.service.memoryLimit.init": "true",
"git-widget-placeholder": "main",
"kotlin-language-version-configured": "true",
"last_opened_file_path": "D:/code/MiddlePlatform/maibu-service/maibu-system-service/src/main/java/com/maibu",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.tslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
"onboarding.tips.debug.path": "D:/code/MiddlePlatform/maibu-service/maibu-system-service/src/main/java/com/maibu/Main.java",
"settings.editor.selected.configurable": "project.propVCSSupport.CommitDialog",
"vue.rearranger.settings.migration": "true"
}
}]]></component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="D:\code\MiddlePlatform\maibu-service\maibu-system-service\src\main\java\com\maibu" />
<recent name="D:\code\MiddlePlatform\maibu-service\maibu-system-service\src\main\resources" />
<recent name="D:\code\MiddlePlatform\maibu-service\maibu-iot-service\src\main\java\com\maibu" />
<recent name="D:\code\MiddlePlatform\maibu-service\maibu-iot-service\src\main\resources" />
<recent name="D:\code\MiddlePlatform\maibu-service" />
</key>
<key name="CopyClassDialog.RECENTS_KEY">
<recent name="com.maibu" />
</key>
</component>
<component name="SharedIndexes">
<attachedChunks>
<set>
<option value="bundled-jdk-30f59d01ecdd-26cb7f24e5b0-intellij.indexing.shared.core-IU-253.29346.138" />
<option value="bundled-js-predefined-d6986cc7102b-9b0f141eb926-JavaScript-IU-253.29346.138" />
</set>
</attachedChunks>
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="84ac1b15-3cfa-4375-9957-45b7b581d04e" name="Changes" comment="" />
<created>1776320869599</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1776320869599</updated>
<workItem from="1776320873919" duration="15557000" />
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
<component name="VcsManagerConfiguration">
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="false" />
<option name="CHECK_NEW_TODO" value="false" />
<MESSAGE value="#init" />
<option name="LAST_COMMIT_MESSAGE" value="#init" />
<option name="NON_MODAL_COMMIT_POSTPONE_SLOW_CHECKS" value="false" />
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
<breakpoints>
<line-breakpoint enabled="true" type="java-line">
<url>file://$PROJECT_DIR$/src/main/java/com/maibu/Main.java</url>
<line>13</line>
<option name="timeStamp" value="1" />
</line-breakpoint>
<line-breakpoint enabled="true" type="java-line">
<url>file://$PROJECT_DIR$/maibu-netty-server/src/main/java/com/maibu/Main.java</url>
<line>13</line>
<option name="timeStamp" value="7" />
</line-breakpoint>
<line-breakpoint enabled="true" type="java-line">
<url>file://$PROJECT_DIR$/maibu-service/src/main/java/com/maibu/Main.java</url>
<line>13</line>
<option name="timeStamp" value="11" />
</line-breakpoint>
</breakpoints>
</breakpoint-manager>
</component>
</project>

145
maibu-framework/pom.xml Normal file
View File

@@ -0,0 +1,145 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.maibu</groupId>
<artifactId>MiddlePlatform</artifactId>
<version>4.0.0</version>
</parent>
<artifactId>maibu-framework</artifactId>
<dependencies>
<!-- SpringBoot Web容器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- SpringBoot 拦截器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- 系统模块-->
<dependency>
<groupId>com.maibu</groupId>
<artifactId>maibu-system-service</artifactId>
<version>${maibu.version}</version>
</dependency>
<!-- 阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>${shardingsphere.version}</version>
</dependency>
<!-- ShardingJDBC 5.1.0使用druid连接池需要加dbcp依赖 -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-dbcp</artifactId>
<version>10.0.16</version>
</dependency>
<!-- 动态数据源 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${dynamic-datasource.version}</version>
</dependency>
<!-- 验证码 -->
<dependency>
<groupId>pro.fessional</groupId>
<artifactId>kaptcha</artifactId>
<exclusions>
<exclusion>
<artifactId>javax.servlet-api</artifactId>
<groupId>javax.servlet</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- 获取系统信息 -->
<dependency>
<groupId>com.github.oshi</groupId>
<artifactId>oshi-core</artifactId>
</dependency>
<!--redisson-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>${redisson.version}</version>
<exclusions>
<exclusion>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-data-31</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-data-25</artifactId>
<version>${redisson.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>lock4j-redisson-spring-boot-starter</artifactId>
<version>${lock4j.version}</version>
<exclusions>
<exclusion>
<artifactId>redisson-spring-boot-starter</artifactId>
<groupId>org.redisson</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dysmsapi20170525</artifactId>
<version>2.0.24</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

View File

@@ -0,0 +1,88 @@
package com.maibu.ali;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.fastbee.framework.web.service.TokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("external/sms")
public class AliSmsController {
@Autowired
private TokenService tokenService;
@Autowired
private AliSmsService aliSmsService;
/**
* 发送短信验证码
*/
@PostMapping("/sendAliSmsCode")
@Log(title = "短信验证", businessType = BusinessType.OTHER)
public AjaxResult sendSmsCode(@RequestBody SendSmsRequest request) {
String phoneNumber = request.getPhoneNumbers();
// 验证手机号格式
if (!isValidPhoneNumber(phoneNumber)) {
return AjaxResult.error("手机号格式不正确");
}
// 发送短信验证码
boolean result = aliSmsService.sendPhoneSms(phoneNumber);
if (result) {
return AjaxResult.success("验证码已发送");
} else {
return AjaxResult.error("验证码发送失败,请稍后重试");
}
}
// /**
// * 短信验证码登录
// */
// @PostMapping("/sms-login")
// public AjaxResult smsLogin(@RequestBody SendSmsRequest request) {
// String phoneNumber = request.getPhoneNumbers();
//// String code = request.getCode();
//
// // 验证手机号格式
// if (!isValidPhoneNumber(phoneNumber)) {
// return AjaxResult.error("手机号格式不正确");
// }
//
// // 验证验证码
// if (!smsUtil.verifySmsCode(phoneNumber, code)) {
// return AjaxResult.error("验证码错误或已过期");
// }
//
// // 查询用户信息
// SysUser user = userService.selectUserByPhoneNumber(phoneNumber);
// if (user == null) {
// // 如果用户不存在,可以选择自动注册
// user = registerUserByPhone(phoneNumber);
// }
//
// // 生成登录令牌
// LoginUser loginUser = new LoginUser();
// loginUser.setUser(user);
// String token = tokenService.createToken(loginUser);
//
// // 返回令牌和用户信息
// Map<String, Object> result = new HashMap<>();
// result.put("token", token);
// result.put("user", user);
// return AjaxResult.success(result);
// }
/**
* 验证手机号格式
*/
private boolean isValidPhoneNumber(String phoneNumber) {
// 简单的手机号格式验证
return phoneNumber != null && phoneNumber.matches("^1[3-9]\\d{9}$");
}
}

View File

@@ -0,0 +1,90 @@
package com.maibu.ali;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.fastbee.common.core.redis.RedisCache;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
@Service
@Slf4j
public class AliSmsService {
@Value("${ali.sms.access-key-id:LTAI5tFxmTZAeFBNa7sqcHUj}")
private String accessKeyId;
@Value("${ali.sms.access-key-secret:vtHYhEWrh585jleku55TbHhtYscmGU}")
private String accessKeySecret;
@Value("${ali.sms.endpoint:dysmsapi.aliyuncs.com}")
private String endpoint;
@Value("${ali.sms.sign-name:飒沓机器人}")
private String signName;
@Value("${ali.sms.template-code:SMS_498590140}")
private String templateCode;
@Value("${ali.sms.expired-time:1}")
private int expiredTime;
private static final String redisKey = "sms:code:";
@Autowired
private RedisCache redisCache;
public boolean sendPhoneSms(String phoneNumber) {
String code = String.valueOf(ThreadLocalRandom.current().nextInt(100000, 1000000));
SendSmsResponse result = AliSmsUtil.sendSmsCode(accessKeyId, accessKeySecret, phoneNumber,
signName, templateCode, endpoint, code, expiredTime);
if (result != null) {
log.info("send phoneNumber:{}", phoneNumber);
log.info("send code:{}", code);
if (result.getBody().getCode().equalsIgnoreCase("OK")) {
String key = redisKey + phoneNumber;
log.info("send put:{}", key);
log.info("send put value :{}", code);
redisCache.setCacheObject(key, code, expiredTime, TimeUnit.MINUTES);
return true;
}
}
return false;
}
/**
* 验证短信验证码
*/
public boolean verifySmsCode(String phoneNumber, String code) {
try {
log.info("phoneNumber:{}", phoneNumber);
log.info("code:{}", code);
if (StringUtils.isEmpty(phoneNumber) || StringUtils.isEmpty(code)) {
return false;
}
String key = redisKey + phoneNumber;
String storedCode = redisCache.getCacheObject(key);
log.info("key:{}", key);
log.info("storedCode:{}", storedCode);
if (StringUtils.isEmpty(storedCode)) {
return false;
} else {
if (code.equals(storedCode)) {
// 验证成功后删除验证码
redisCache.deleteObject(key);
return true;
}
return false;
}
} catch (Exception e) {
log.error("验证短信验证码异常,手机号: {}", phoneNumber, e);
return false;
}
}
}

View File

@@ -0,0 +1,64 @@
package com.maibu.ali;
import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.aliyun.teaopenapi.models.Config;
import com.fastbee.common.utils.json.JsonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class AliSmsUtil {
private static final Logger logger = LoggerFactory.getLogger(AliSmsUtil.class);
/**
* 发送短信验证码
*/
public static SendSmsResponse sendSmsCode(String accessKeyId, String accessKeySecret, String phoneNumber, String signName,
String templateCode, String endpoint, String code, int time) {
try {
// 构造请求对象
SendSmsRequest sendSmsRequest = new SendSmsRequest()
.setPhoneNumbers(phoneNumber)
.setSignName(signName)
.setTemplateCode(templateCode)
// // TemplateParam 为序列化后的 JSON 字符串。其中\"表示转义后的双引号。
.setTemplateParam("{\"code\":\"" + code + "\",\"time\":\"" + time + "\"}");
Config config = new Config()
// 配置 AccessKey ID,请确保代码运行环境配置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID。
.setAccessKeyId(accessKeyId)
// 配置 AccessKey Secret,请确保代码运行环境配置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。
.setAccessKeySecret(accessKeySecret);
// System.getenv()方法表示获取系统环境变量,不要直接在getenv()中填入AccessKey信息。
// 配置 Endpoint。
config.endpoint = endpoint;
Client client = new Client(config);
// 发送 API 请求
SendSmsResponse sendSmsResponse = client.sendSms(sendSmsRequest);
logger.info("发送短信反馈信息: {}", JsonUtils.toJsonString(sendSmsResponse));
if (sendSmsResponse != null) {
logger.info("发送短信反馈信息body: {}", JsonUtils.toJsonString(sendSmsResponse.getBody()));
if (sendSmsResponse.statusCode == 200) {
// submit success
return sendSmsResponse;
} else {
// submit success
return null;
}
} else {
// fail
return null;
}
} catch (Exception e) {
logger.error("发送短信异常,手机号: {}", phoneNumber, e);
return null;
}
}
}

View File

@@ -0,0 +1,70 @@
package com.maibu.aspect;
import com.google.gson.Gson;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
//@Aspect
//@Component
public class WebLogAspect {
// private final static Logger logger = LoggerFactory.getLogger(WebLogAspect.class);
// /** 以 controller 包下定义的所有请求为切入点 */
// @Pointcut("execution(public * com.fastbee.data.controller..*.*(..))")
// public void webLog() {}
// /**
// * 在切点之前织入
// * @param joinPoint
// * @throws Throwable
// */
// @Before("webLog()")
// public void doBefore(JoinPoint joinPoint) throws Throwable {
// // 开始打印请求日志
// ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
// HttpServletRequest request = attributes.getRequest();
// // 打印请求相关参数
// logger.info("========================================== Start ==========================================");
// // 打印请求 url
// logger.info("\nURL : {} \nHTTP Method : {} \nClass Method : {}.{} \nIP : {} \nRequest Args : {}", request.getRequestURL().toString(), request.getMethod(), joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(), request.getRemoteAddr(), new Gson().toJson(joinPoint.getArgs()));
//// // 打印 Http method
//// logger.info("HTTP Method : {}", request.getMethod());
//// // 打印调用 controller 的全路径以及执行方法
//// logger.info("Class Method : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
//// // 打印请求的 IP
//// logger.info("IP : {}", request.getRemoteAddr());
//// // 打印请求入参
//// logger.info("Request Args : {}", new Gson().toJson(joinPoint.getArgs()));
// }
// /**
// * 在切点之后织入
// * @throws Throwable
// */
// @After("webLog()")
// public void doAfter() throws Throwable {
// logger.info("=========================================== End ===========================================");
// 每个请求之间空一行
// logger.info("");
// }
// /**
// * 环绕
// * @param proceedingJoinPoint
// * @return
// * @throws Throwable
// */
// @Around("webLog()")
// public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
// long startTime = System.currentTimeMillis();
// Object result = proceedingJoinPoint.proceed();
// // 打印出参
// logger.info("\nResponse Args : {} \nTime-Consuming : {} ms", new Gson().toJson(result), System.currentTimeMillis() - startTime);
// // 执行耗时
//// logger.info("\nTime-Consuming : {} ms", System.currentTimeMillis() - startTime);
// return result;
// }
}

View File

@@ -0,0 +1,167 @@
package com.maibu.aspectj;
import java.util.ArrayList;
import java.util.List;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import com.fastbee.common.annotation.DataScope;
import com.fastbee.common.core.domain.BaseEntity;
import com.fastbee.common.core.domain.entity.SysRole;
import com.fastbee.common.core.domain.entity.SysUser;
import com.fastbee.common.core.domain.model.LoginUser;
import com.fastbee.common.core.text.Convert;
import com.fastbee.common.utils.SecurityUtils;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.framework.security.context.PermissionContextHolder;
/**
* 数据过滤处理
*
* @author ruoyi
*/
@Aspect
@Component
public class DataScopeAspect
{
/**
* 全部数据权限
*/
public static final String DATA_SCOPE_ALL = "1";
/**
* 自定数据权限
*/
public static final String DATA_SCOPE_CUSTOM = "2";
/**
* 部门数据权限
*/
public static final String DATA_SCOPE_DEPT = "3";
/**
* 部门及以下数据权限
*/
public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";
/**
* 仅本人数据权限
*/
public static final String DATA_SCOPE_SELF = "5";
/**
* 数据权限过滤关键字
*/
public static final String DATA_SCOPE = "dataScope";
@Before("@annotation(controllerDataScope)")
public void doBefore(JoinPoint point, DataScope controllerDataScope) throws Throwable
{
clearDataScope(point);
handleDataScope(point, controllerDataScope);
}
protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope)
{
// 获取当前的用户
LoginUser loginUser = SecurityUtils.getLoginUser();
if (StringUtils.isNotNull(loginUser))
{
SysUser currentUser = loginUser.getUser();
// 如果是超级管理员,则不过滤数据
if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin())
{
String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), PermissionContextHolder.getContext());
dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
controllerDataScope.userAlias(), permission);
}
}
}
/**
* 数据范围过滤
*
* @param joinPoint 切点
* @param user 用户
* @param deptAlias 部门别名
* @param userAlias 用户别名
* @param permission 权限字符
*/
public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, String permission)
{
StringBuilder sqlString = new StringBuilder();
List<String> conditions = new ArrayList<String>();
for (SysRole role : user.getRoles())
{
String dataScope = role.getDataScope();
if (!DATA_SCOPE_CUSTOM.equals(dataScope) && conditions.contains(dataScope))
{
continue;
}
if (StringUtils.isNotEmpty(permission) && StringUtils.isNotEmpty(role.getPermissions())
&& !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
{
continue;
}
if (DATA_SCOPE_ALL.equals(dataScope))
{
sqlString = new StringBuilder();
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);
}
if (StringUtils.isNotBlank(sqlString.toString()))
{
Object params = joinPoint.getArgs()[0];
if (StringUtils.isNotNull(params) && params instanceof BaseEntity)
{
BaseEntity baseEntity = (BaseEntity) params;
baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");
}
}
}
/**
* 拼接权限sql前先清空params.dataScope参数防止注入
*/
private void clearDataScope(final JoinPoint joinPoint)
{
Object params = joinPoint.getArgs()[0];
if (StringUtils.isNotNull(params) && params instanceof BaseEntity)
{
BaseEntity baseEntity = (BaseEntity) params;
baseEntity.getParams().put(DATA_SCOPE, "");
}
}
}

View File

@@ -0,0 +1,73 @@
package com.maibu.aspectj;
import java.util.Objects;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import com.fastbee.common.annotation.DataSource;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.framework.datasource.DynamicDataSourceContextHolder;
/**
* 多数据源处理
*
* @author ruoyi
*/
@Aspect
@Order(1)
@Component
public class DataSourceAspect
{
protected Logger logger = LoggerFactory.getLogger(getClass());
@Pointcut("@annotation(com.fastbee.common.annotation.DataSource)"
+ "|| @within(com.fastbee.common.annotation.DataSource)")
public void dsPointCut()
{
}
@Around("dsPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable
{
DataSource dataSource = getDataSource(point);
if (StringUtils.isNotNull(dataSource))
{
DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());
}
try
{
return point.proceed();
}
finally
{
// 销毁数据源 在执行方法之后
DynamicDataSourceContextHolder.clearDataSourceType();
}
}
/**
* 获取需要切换的数据源
*/
public DataSource getDataSource(ProceedingJoinPoint point)
{
MethodSignature signature = (MethodSignature) point.getSignature();
DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);
if (Objects.nonNull(dataSource))
{
return dataSource;
}
return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
}
}

View File

@@ -0,0 +1,229 @@
package com.maibu.aspectj;
import java.util.Collection;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import com.alibaba.fastjson2.JSON;
import com.fastbee.common.annotation.Log;
import com.fastbee.common.core.domain.model.LoginUser;
import com.fastbee.common.enums.BusinessStatus;
import com.fastbee.common.enums.HttpMethod;
import com.fastbee.common.filter.PropertyPreExcludeFilter;
import com.fastbee.common.utils.SecurityUtils;
import com.fastbee.common.utils.ServletUtils;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.common.utils.ip.IpUtils;
import com.fastbee.framework.manager.AsyncManager;
import com.fastbee.framework.manager.factory.AsyncFactory;
import com.fastbee.system.domain.SysOperLog;
/**
* 操作日志记录处理
*
* @author ruoyi
*/
@Aspect
@Component
public class LogAspect
{
private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
/** 排除敏感属性字段 */
public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" };
/**
* 处理完请求后执行
*
* @param joinPoint 切点
*/
@AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult)
{
handleLog(joinPoint, controllerLog, null, jsonResult);
}
/**
* 拦截异常操作
*
* @param joinPoint 切点
* @param e 异常
*/
@AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e)
{
handleLog(joinPoint, controllerLog, e, null);
}
protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult)
{
try
{
// 获取当前的用户
LoginUser loginUser = SecurityUtils.getLoginUser();
// *========数据库日志=========*//
SysOperLog operLog = new SysOperLog();
operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
// 请求的地址
String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
operLog.setOperIp(ip);
operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));
if (loginUser != null)
{
operLog.setOperName(loginUser.getUsername());
}
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);
// 保存数据库
AsyncManager.me().execute(AsyncFactory.recordOper(operLog));
}
catch (Exception exp)
{
// 记录本地异常日志
log.error("异常信息:{}", exp.getMessage());
exp.printStackTrace();
}
}
/**
* 获取注解中对方法的描述信息 用于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);
}
// 是否需要保存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) throws Exception
{
String requestMethod = operLog.getRequestMethod();
if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod))
{
String params = argsArrayToString(joinPoint.getArgs());
operLog.setOperParam(StringUtils.substring(params, 0, 2000));
}
else
{
Map<?, ?> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());
operLog.setOperParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter()), 0, 2000));
}
}
/**
* 参数拼装
*/
private String argsArrayToString(Object[] paramsArray)
{
String params = "";
if (paramsArray != null && paramsArray.length > 0)
{
for (Object o : paramsArray)
{
if (StringUtils.isNotNull(o) && !isFilterObject(o))
{
try
{
String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter());
params += jsonObj.toString() + " ";
}
catch (Exception e)
{
}
}
}
}
return params.trim();
}
/**
* 忽略敏感属性
*/
public PropertyPreExcludeFilter excludePropertyPreFilter()
{
return new PropertyPreExcludeFilter().addExcludes(EXCLUDE_PROPERTIES);
}
/**
* 判断是否需要过滤的对象。
*
* @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;
}
}

View File

@@ -0,0 +1,91 @@
package com.maibu.aspectj;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;
import com.fastbee.common.annotation.RateLimiter;
import com.fastbee.common.enums.LimitType;
import com.fastbee.common.exception.ServiceException;
import com.fastbee.common.utils.ServletUtils;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.common.utils.ip.IpUtils;
/**
* 限流处理
*
* @author ruoyi
*/
@Aspect
@Component
public class RateLimiterAspect
{
private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class);
private RedisTemplate<Object, Object> redisTemplate;
private RedisScript<Long> limitScript;
@Autowired
public void setRedisTemplate1(RedisTemplate<Object, Object> redisTemplate)
{
this.redisTemplate = redisTemplate;
}
@Autowired
public void setLimitScript(RedisScript<Long> limitScript)
{
this.limitScript = limitScript;
}
@Before("@annotation(rateLimiter)")
public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable
{
int time = rateLimiter.time();
int count = rateLimiter.count();
String combineKey = getCombineKey(rateLimiter, point);
List<Object> keys = Collections.singletonList(combineKey);
try
{
Long number = redisTemplate.execute(limitScript, keys, count, time);
if (StringUtils.isNull(number) || number.intValue() > count)
{
throw new ServiceException("访问过于频繁,请稍候再试");
}
log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), combineKey);
}
catch (ServiceException e)
{
throw e;
}
catch (Exception e)
{
throw new RuntimeException("服务器限流异常,请稍候再试");
}
}
public String getCombineKey(RateLimiter rateLimiter, JoinPoint point)
{
StringBuffer stringBuffer = new StringBuffer(rateLimiter.key());
if (rateLimiter.limitType() == LimitType.IP)
{
stringBuffer.append(IpUtils.getIpAddr(ServletUtils.getRequest())).append("-");
}
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
Class<?> targetClass = method.getDeclaringClass();
stringBuffer.append(targetClass.getName()).append("-").append(method.getName());
return stringBuffer.toString();
}
}

View File

@@ -0,0 +1,30 @@
package com.maibu.config;
import java.util.TimeZone;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
* 程序注解配置
*
* @author ruoyi
*/
@Configuration
// 表示通过aop框架暴露该代理对象,AopContext能够访问
@EnableAspectJAutoProxy(exposeProxy = true)
// 指定要扫描的Mapper类的包的路径
@MapperScan("com.fastbee.**.mapper")
public class ApplicationConfig
{
/**
* 时区配置
*/
@Bean
public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization()
{
return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault());
}
}

View File

@@ -0,0 +1,45 @@
package com.maibu.config;
import com.fastbee.framework.manager.SpringCacheManager;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.TimeUnit;
/**
* 缓存配置
*/
@Configuration
@EnableCaching
@ConditionalOnProperty(name = "spring.cache.enable", havingValue = "true")
public class CacheConfig {
@Value("${spring.cache.ttl}")
private Long ttl;
@Bean
public Cache<Object, Object> caffeine() {
return Caffeine.newBuilder()
// 设置最后一次写入或访问后经过固定时间过期
.expireAfterWrite(60, TimeUnit.SECONDS)
// 初始的缓存空间大小
.initialCapacity(100)
// 缓存的最大条数
.maximumSize(10000)
.build();
}
/**
* 自定义缓存管理器 整合spring-cache
*/
@Bean
public CacheManager cacheManager() {
return new SpringCacheManager(ttl);
}
}

View File

@@ -0,0 +1,83 @@
package com.maibu.config;
import java.util.Properties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import static com.google.code.kaptcha.Constants.*;
/**
* 验证码配置
*
* @author ruoyi
*/
@Configuration
public class CaptchaConfig
{
@Bean(name = "captchaProducer")
public DefaultKaptcha getKaptchaBean()
{
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
Properties properties = new Properties();
// 是否有边框 默认为true 我们可以自己设置yes,no
properties.setProperty(KAPTCHA_BORDER, "yes");
// 验证码文本字符颜色 默认为Color.BLACK
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black");
// 验证码图片宽度 默认为200
properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
// 验证码图片高度 默认为50
properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
// 验证码文本字符大小 默认为40
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38");
// KAPTCHA_SESSION_KEY
properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode");
// 验证码文本字符长度 默认为5
properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
// 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
// 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
Config config = new Config(properties);
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
@Bean(name = "captchaProducerMath")
public DefaultKaptcha getKaptchaBeanMath()
{
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
Properties properties = new Properties();
// 是否有边框 默认为true 我们可以自己设置yes,no
properties.setProperty(KAPTCHA_BORDER, "yes");
// 边框颜色 默认为Color.BLACK
properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90");
// 验证码文本字符颜色 默认为Color.BLACK
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue");
// 验证码图片宽度 默认为200
properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
// 验证码图片高度 默认为50
properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
// 验证码文本字符大小 默认为40
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35");
// KAPTCHA_SESSION_KEY
properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath");
// 验证码文本生成器
properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.fastbee.framework.config.KaptchaTextCreator");
// 验证码文本字符间距 默认为2
properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3");
// 验证码文本字符长度 默认为5
properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6");
// 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
// 验证码噪点颜色 默认为Color.BLACK
properties.setProperty(KAPTCHA_NOISE_COLOR, "white");
// 干扰实现类
properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise");
// 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
Config config = new Config(properties);
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
}

View File

@@ -0,0 +1,72 @@
package com.maibu.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration;
import com.fastbee.framework.datasource.DynamicDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
import java.io.IOException;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
@Configuration
@AutoConfigureBefore({DynamicDataSourceAutoConfiguration.class, SpringBootConfiguration.class})
public class DataSourceConfig {
@Bean(name = "masterDataSource")
@ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.master")
public DruidDataSource masterDataSource() {
return new DruidDataSource();
}
// @Bean(name = "slaveDataSource")
// @ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.slave")
// public DruidDataSource slaveDataSource() {
// return new DruidDataSource();
// }
@Bean
@Primary
public DynamicDataSource dataSource(@Qualifier("masterDataSource") DataSource master) throws SQLException, IOException {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("master", master);
//targetDataSources.put("slave", slave);
return new DynamicDataSource(master,targetDataSources);
}
@Bean
public DataSourceTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
// /**
// * 将动态数据源设置为首选数据源
// */
// @Primary
// @Bean
// public DynamicDataSource dataSource(DynamicDataSourceProvider dynamicDataSourceProvider) {
//// DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
//// dataSource.setPrimary(properties.getPrimary());
//// dataSource.setStrict(properties.getStrict());
//// dataSource.setStrategy(properties.getStrategy());
//// dataSource.setProvider(dynamicDataSourceProvider);
//// dataSource.setP6spy(properties.getP6spy());
//// dataSource.setSeata(properties.getSeata());
//// return dataSource;
// Map<String, DataSource> targetDataSources = dynamicDataSourceProvider.loadDataSources();
// Map<Object, Object> objectObjectMap = new HashMap<>(targetDataSources);
// DataSource masterDataSource = targetDataSources.get(properties.getPrimary());
// return new DynamicDataSource(masterDataSource,objectObjectMap);
// }
}

View File

@@ -0,0 +1,74 @@
package com.maibu.config;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.spring.boot.autoconfigure.stat.DruidFilterConfiguration;
import com.alibaba.druid.spring.boot.autoconfigure.stat.DruidSpringAopConfiguration;
import com.alibaba.druid.spring.boot.autoconfigure.stat.DruidStatViewServletConfiguration;
import com.alibaba.druid.spring.boot.autoconfigure.stat.DruidWebStatFilterConfiguration;
import com.alibaba.druid.util.Utils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import javax.servlet.*;
import java.io.IOException;
@Configuration
@ConditionalOnClass(value = DruidDataSourceAutoConfigure.class)
@EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class})
@Import({DruidSpringAopConfiguration.class, DruidStatViewServletConfiguration.class, DruidWebStatFilterConfiguration.class, DruidFilterConfiguration.class})
public class DruidConfig {
public DruidConfig() {
}
/**
* 去除监控页面底部的广告
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
@Bean
@ConditionalOnProperty(name = "spring.datasource.druid.stat-view-servlet.enabled", havingValue = "true")
public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties)
{
// 获取web监控页面的参数
DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();
// 提取common.js的配置路径
String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*";
String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");
final String filePath = "support/http/resources/js/common.js";
// 创建filter进行过滤
Filter filter = new Filter()
{
@Override
public void init(javax.servlet.FilterConfig filterConfig) throws ServletException
{
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException
{
chain.doFilter(request, response);
// 重置缓冲区,响应头不会被重置
response.resetBuffer();
// 获取common.js
String text = Utils.readFromResource(filePath);
// 正则替换banner, 除去底部的广告信息
text = text.replaceAll("<a.*?banner\"></a><br/>", "");
text = text.replaceAll("powered.*?shrek.wang</a>", "");
response.getWriter().write(text);
}
@Override
public void destroy()
{
}
};
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter);
registrationBean.addUrlPatterns(commonJsPattern);
return registrationBean;
}
}

View File

@@ -0,0 +1,48 @@
package com.maibu.config;
import java.nio.charset.Charset;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.JSONWriter;
/**
* Redis使用FastJson序列化
*
* @author ruoyi
*/
public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T>
{
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private Class<T> clazz;
public FastJson2JsonRedisSerializer(Class<T> clazz)
{
super();
this.clazz = clazz;
}
@Override
public byte[] serialize(T t) throws SerializationException
{
if (t == null)
{
return new byte[0];
}
return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET);
}
@Override
public T deserialize(byte[] bytes) throws SerializationException
{
if (bytes == null || bytes.length <= 0)
{
return null;
}
String str = new String(bytes, DEFAULT_CHARSET);
return JSON.parseObject(str, clazz, JSONReader.Feature.SupportAutoType);
}
}

View File

@@ -0,0 +1,58 @@
package com.maibu.config;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.DispatcherType;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.fastbee.common.filter.RepeatableFilter;
import com.fastbee.common.filter.XssFilter;
import com.fastbee.common.utils.StringUtils;
/**
* Filter配置
*
* @author ruoyi
*/
@Configuration
public class FilterConfig
{
@Value("${xss.excludes}")
private String excludes;
@Value("${xss.urlPatterns}")
private String urlPatterns;
@SuppressWarnings({ "rawtypes", "unchecked" })
@Bean
@ConditionalOnProperty(value = "xss.enabled", havingValue = "true")
public FilterRegistrationBean xssFilterRegistration()
{
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setDispatcherTypes(DispatcherType.REQUEST);
registration.setFilter(new XssFilter());
registration.addUrlPatterns(StringUtils.split(urlPatterns, ","));
registration.setName("xssFilter");
registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
Map<String, String> initParameters = new HashMap<String, String>();
initParameters.put("excludes", excludes);
registration.setInitParameters(initParameters);
return registration;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Bean
public FilterRegistrationBean someFilterRegistration()
{
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new RepeatableFilter());
registration.addUrlPatterns("/*");
registration.setName("repeatableFilter");
registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE);
return registration;
}
}

View File

@@ -0,0 +1,68 @@
package com.maibu.config;
import java.util.Random;
import com.google.code.kaptcha.text.impl.DefaultTextCreator;
/**
* 验证码文本生成器
*
* @author ruoyi
*/
public class KaptchaTextCreator extends DefaultTextCreator
{
private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(",");
@Override
public String getText()
{
Integer result = 0;
Random random = new Random();
int x = random.nextInt(10);
int y = random.nextInt(10);
StringBuilder suChinese = new StringBuilder();
int randomoperands = random.nextInt(3);
if (randomoperands == 0)
{
result = x * y;
suChinese.append(CNUMBERS[x]);
suChinese.append("*");
suChinese.append(CNUMBERS[y]);
}
else if (randomoperands == 1)
{
if ((x != 0) && y % x == 0)
{
result = y / x;
suChinese.append(CNUMBERS[y]);
suChinese.append("/");
suChinese.append(CNUMBERS[x]);
}
else
{
result = x + y;
suChinese.append(CNUMBERS[x]);
suChinese.append("+");
suChinese.append(CNUMBERS[y]);
}
}
else
{
if (x >= y)
{
result = x - y;
suChinese.append(CNUMBERS[x]);
suChinese.append("-");
suChinese.append(CNUMBERS[y]);
}
else
{
result = y - x;
suChinese.append(CNUMBERS[y]);
suChinese.append("-");
suChinese.append(CNUMBERS[x]);
}
}
suChinese.append("=?@" + result);
return suChinese.toString();
}
}

View File

@@ -0,0 +1,167 @@
package com.maibu.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.SpringBootVFS;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.framework.datasource.DynamicDataSource;
import org.apache.ibatis.io.VFS;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
/**
* Mybatis支持*匹配扫描包
*
* @author ruoyi
*/
@Configuration
public class MyBatisConfig
{
@Autowired
private Environment env;
static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
public static String setTypeAliasesPackage(String typeAliasesPackage)
{
ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver();
MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);
List<String> allResult = new ArrayList<String>();
try
{
for (String aliasesPackage : typeAliasesPackage.split(","))
{
List<String> result = new ArrayList<String>();
aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
+ ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN;
Resource[] resources = resolver.getResources(aliasesPackage);
if (resources != null && resources.length > 0)
{
MetadataReader metadataReader = null;
for (Resource resource : resources)
{
if (resource.isReadable())
{
metadataReader = metadataReaderFactory.getMetadataReader(resource);
try
{
result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName());
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
}
}
}
if (result.size() > 0)
{
HashSet<String> hashResult = new HashSet<String>(result);
allResult.addAll(hashResult);
}
}
if (allResult.size() > 0)
{
typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0]));
}
else
{
throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包");
}
}
catch (IOException e)
{
e.printStackTrace();
}
return typeAliasesPackage;
}
public Resource[] resolveMapperLocations(String[] mapperLocations)
{
ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
List<Resource> resources = new ArrayList<Resource>();
if (mapperLocations != null)
{
for (String mapperLocation : mapperLocations)
{
try
{
Resource[] mappers = resourceResolver.getResources(mapperLocation);
resources.addAll(Arrays.asList(mappers));
}
catch (IOException e)
{
// ignore
}
}
}
return resources.toArray(new Resource[resources.size()]);
}
/**
* mybatis-plus 配置:把 SqlSessionFactoryBean 换成 MybatisSqlSessionFactoryBean就行
* @param dataSource 数据源
* @return
*/
@Bean(name = "mysqlSessionFactory")
@Primary
public SqlSessionFactory sqlSessionFactory(DynamicDataSource dataSource) throws Exception
{
String typeAliasesPackage = env.getProperty("mybatis-plus.typeAliasesPackage");
String mapperLocations = env.getProperty("mybatis-plus.mapperLocations");
String configLocation = env.getProperty("mybatis-plus.configLocation");
typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);
VFS.addImplClass(SpringBootVFS.class);
final MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ",")));
sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
// 添加分页插件
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
sessionFactory.setPlugins(interceptor);
return sessionFactory.getObject();
}
/**
* 元对象字段填充控制器
*/
@Bean
public MetaObjectHandler metaObjectHandler() {
return new MybatisPlusMetaObjectHandler();
}
@Bean(name = "mysqlSqlSessionTemplate")
@Primary
public SqlSessionTemplate mysqlSqlSessionTemplate(@Qualifier("mysqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}

View File

@@ -0,0 +1,75 @@
package com.maibu.config;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.fastbee.common.core.domain.model.LoginUser;
import com.fastbee.common.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.util.ClassUtils;
import java.nio.charset.Charset;
import java.util.Date;
/**
* MybatisPlus 自动填充配置
*
* @author admin
*/
@Slf4j
public class MybatisPlusMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.debug("mybatisPlus插入填充--------------");
Date date = new Date();
fillValIfNullByName("createTime", date, metaObject, false);
fillValIfNullByName("updateTime", date, metaObject, false);
fillValIfNullByName("createBy", getUserName(), metaObject, false);
fillValIfNullByName("updateBy", getUserName(), metaObject, false);
fillValIfNullByName("deleteFlag", "1", metaObject, false);
}
@Override
public void updateFill(MetaObject metaObject) {
log.debug("mybatisPlus更新填充--------------");
fillValIfNullByName("updateTime", new Date(), metaObject, true);
fillValIfNullByName("updateBy", getUserName(), metaObject, true);
}
/**
* 填充值,先判断是否有手动设置,优先手动设置的值,例如:job必须手动设置
* @param fieldName 属性名
* @param fieldVal 属性值
* @param metaObject MetaObject
* @param isCover 是否覆盖原有值,避免更新操作手动入参
*/
private static void fillValIfNullByName(String fieldName, Object fieldVal, MetaObject metaObject, boolean isCover) {
// 1. 没有 get 方法
if (!metaObject.hasSetter(fieldName)) {
return;
}
// 2. 如果用户有手动设置的值
Object userSetValue = metaObject.getValue(fieldName);
String setValueStr = StrUtil.str(userSetValue, Charset.defaultCharset());
if (StrUtil.isNotBlank(setValueStr) && !isCover) {
return;
}
// 3. field 类型相同时设置
Class<?> getterType = metaObject.getGetterType(fieldName);
if (ClassUtils.isAssignableValue(getterType, fieldVal)) {
metaObject.setValue(fieldName, fieldVal);
}
}
/**
* 获取 spring security 当前的用户名
* @return 当前用户名
*/
private String getUserName() {
LoginUser loginUser = SecurityUtils.getLoginUser();
return loginUser.getUsername();
}
}

View File

@@ -0,0 +1,47 @@
package com.maibu.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
/**
* @author admin
*/
@Configuration(proxyBeanMethods = false)
public class MybatisPlusPageConfigurer implements WebMvcConfigurer {
/**
* SQL 过滤器避免SQL 注入
* @param argumentResolvers
*/
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new SqlFilterArgumentResolver());
}
/**
* 分页插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
/**
* 审计字段自动填充
* @return {@link MybatisPlusMetaObjectHandler}
*/
@Bean
public MybatisPlusMetaObjectHandler mybatisPlusMetaObjectHandler() {
return new MybatisPlusMetaObjectHandler();
}
}

View File

@@ -0,0 +1,39 @@
package com.maibu.config;
import com.fastbee.common.constant.FastBeeConstant;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
public class OtaThreadPoolConfig
{
// 核心线程池大小
private int corePoolSize = 10;
// 最大可创建的线程数
private int maxPoolSize = 20;
// 队列最大长度
private int queueCapacity = 10000;
// 线程池维护线程所允许的空闲时间
private int keepAliveSeconds = 30;
// 配置线程池的前缀
private static final String threadNamePrefix = "ota-";
@Bean(name = FastBeeConstant.TASK.OTA_THREAD_POOL)
public ThreadPoolTaskExecutor threadPoolTaskExecutor()
{
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(maxPoolSize);
executor.setCorePoolSize(corePoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setKeepAliveSeconds(keepAliveSeconds);
executor.setThreadNamePrefix(threadNamePrefix);
// 线程池对拒绝任务(无线程可用)的处理策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 进行加载
executor.initialize();
return executor;
}
}

View File

@@ -0,0 +1,67 @@
package com.maibu.config;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* redis配置
*
* @author ruoyi
*/
@Configuration
public class RedisConfig extends CachingConfigurerSupport
{
@Bean
@SuppressWarnings(value = { "unchecked", "rawtypes" })
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
{
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
// 使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
// Hash的key也采用StringRedisSerializer的序列化方式
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
@Bean
public DefaultRedisScript<Long> limitScript()
{
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(limitScriptText());
redisScript.setResultType(Long.class);
return redisScript;
}
/**
* 限流脚本
*/
private String limitScriptText()
{
return "local key = KEYS[1]\n" +
"local count = tonumber(ARGV[1])\n" +
"local time = tonumber(ARGV[2])\n" +
"local current = redis.call('get', key);\n" +
"if current and tonumber(current) > count then\n" +
" return tonumber(current);\n" +
"end\n" +
"current = redis.call('incr', key)\n" +
"if tonumber(current) == 1 then\n" +
" redis.call('expire', key, time)\n" +
"end\n" +
"return tonumber(current);";
}
}

View File

@@ -0,0 +1,93 @@
package com.maibu.config;
import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import lombok.extern.slf4j.Slf4j;
import com.fastbee.framework.config.properties.RedissonProperties;
import org.redisson.client.codec.StringCodec;
import org.redisson.codec.CompositeCodec;
import org.redisson.codec.TypedJsonJacksonCodec;
import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.fastbee.framework.handler.KeyPrefixHandler;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.TimeZone;
/**
* redis配置
*/
@Slf4j
@Configuration
@EnableConfigurationProperties(RedissonProperties.class)
public class RedissonConfig {
@Autowired
private RedissonProperties redissonProperties;
@Bean
public RedissonAutoConfigurationCustomizer redissonCustomizer() {
return config -> {
JavaTimeModule javaTimeModule = new JavaTimeModule();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter));
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter));
ObjectMapper om = new ObjectMapper();
om.registerModule(javaTimeModule);
om.setTimeZone(TimeZone.getDefault());
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型,类必须是非final修饰的。序列化时将对象全类名一起保存下来
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
TypedJsonJacksonCodec jsonCodec = new TypedJsonJacksonCodec(Object.class, om);
// 组合序列化 key 使用 String 内容使用通用 json 格式
CompositeCodec codec = new CompositeCodec(StringCodec.INSTANCE, jsonCodec, jsonCodec);
config.setThreads(redissonProperties.getThreads())
.setNettyThreads(redissonProperties.getNettyThreads())
// 缓存 Lua 脚本 减少网络传输(redisson 大部分的功能都是基于 Lua 脚本实现)
.setUseScriptCache(true)
.setCodec(codec);
RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig();
if (ObjectUtil.isNotNull(singleServerConfig)) {
// 使用单机模式
config.useSingleServer()
//设置redis key前缀
.setNameMapper(new KeyPrefixHandler(redissonProperties.getKeyPrefix()))
.setTimeout(singleServerConfig.getTimeout())
.setClientName(singleServerConfig.getClientName())
.setIdleConnectionTimeout(singleServerConfig.getIdleConnectionTimeout())
.setSubscriptionConnectionPoolSize(singleServerConfig.getSubscriptionConnectionPoolSize())
.setConnectionMinimumIdleSize(singleServerConfig.getConnectionMinimumIdleSize())
.setConnectionPoolSize(singleServerConfig.getConnectionPoolSize());
}
// 集群配置方式 参考下方注释
RedissonProperties.ClusterServersConfig clusterServersConfig = redissonProperties.getClusterServersConfig();
if (ObjectUtil.isNotNull(clusterServersConfig)) {
config.useClusterServers()
//设置redis key前缀
.setNameMapper(new KeyPrefixHandler(redissonProperties.getKeyPrefix()))
.setTimeout(clusterServersConfig.getTimeout())
.setClientName(clusterServersConfig.getClientName())
.setIdleConnectionTimeout(clusterServersConfig.getIdleConnectionTimeout())
.setSubscriptionConnectionPoolSize(clusterServersConfig.getSubscriptionConnectionPoolSize())
.setMasterConnectionMinimumIdleSize(clusterServersConfig.getMasterConnectionMinimumIdleSize())
.setMasterConnectionPoolSize(clusterServersConfig.getMasterConnectionPoolSize())
.setSlaveConnectionMinimumIdleSize(clusterServersConfig.getSlaveConnectionMinimumIdleSize())
.setSlaveConnectionPoolSize(clusterServersConfig.getSlaveConnectionPoolSize())
.setReadMode(clusterServersConfig.getReadMode())
.setSubscriptionMode(clusterServersConfig.getSubscriptionMode());
}
log.info("初始化 redis 配置");
};
}
}

View File

@@ -0,0 +1,82 @@
package com.maibu.config;
import java.util.concurrent.TimeUnit;
import com.fastbee.framework.interceptor.LanguageInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.CacheControl;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.fastbee.common.config.RuoYiConfig;
import com.fastbee.common.constant.Constants;
import com.fastbee.framework.interceptor.RepeatSubmitInterceptor;
import javax.annotation.Resource;
/**
* 通用配置
*
* @author ruoyi
*/
@Configuration
public class ResourcesConfig implements WebMvcConfigurer
{
@Autowired
private RepeatSubmitInterceptor repeatSubmitInterceptor;
@Resource
private LanguageInterceptor languageInterceptor;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
/** 本地文件上传路径 */
registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**")
.addResourceLocations("file:" + RuoYiConfig.getProfile() + "/");
/** swagger配置 */
registry.addResourceHandler("/swagger-ui/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/")
.setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic());;
}
/**
* 自定义拦截规则
*/
@Override
public void addInterceptors(InterceptorRegistry registry)
{
registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");
//这里配置国际化拦截器的白名单
registry.addInterceptor(languageInterceptor).addPathPatterns("/**").excludePathPatterns("/v2/api-docs",
"/tool/gen/**");
}
/**
* 跨域配置
*/
@Bean
public CorsFilter corsFilter()
{
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
// 设置访问源地址
config.addAllowedOriginPattern("*");
// 设置访问源请求头
config.addAllowedHeader("*");
// 设置访问源请求方法
config.addAllowedMethod("*");
// 有效期 1800秒
config.setMaxAge(1800L);
// 添加映射路径,拦截一切请求
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
// 返回新的CorsFilter
return new CorsFilter(source);
}
}

View File

@@ -0,0 +1,221 @@
package com.maibu.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import com.fastbee.framework.config.properties.PermitAllUrlProperties;
import com.fastbee.framework.security.filter.JwtAuthenticationTokenFilter;
import com.fastbee.framework.security.handle.AuthenticationEntryPointImpl;
import com.fastbee.framework.security.handle.LogoutSuccessHandlerImpl;
import java.util.Arrays;
/**
* spring security配置
*
* @author ruoyi
*/
//@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 自定义用户认证逻辑
*/
@Autowired
private UserDetailsService userDetailsService;
/**
* 认证失败处理类
*/
@Autowired
private AuthenticationEntryPointImpl unauthorizedHandler;
/**
* 退出处理类
*/
@Autowired
private LogoutSuccessHandlerImpl logoutSuccessHandler;
/**
* token认证过滤器
*/
@Autowired
private JwtAuthenticationTokenFilter authenticationTokenFilter;
/**
* 跨域过滤器
*/
@Autowired
private CorsFilter corsFilter;
/**
* 允许匿名访问的地址
*/
@Autowired
private PermitAllUrlProperties permitAllUrl;
/**
* 解决 无法直接注入 AuthenticationManager
*
* @return
* @throws Exception
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* anyRequest | 匹配所有请求路径
* access | SpringEl表达式结果为true时可以访问
* anonymous | 匿名可以访问
* denyAll | 用户不能访问
* fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录)
* hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问
* hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问
* hasAuthority | 如果有参数,参数表示权限,则其权限可以访问
* hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
* hasRole | 如果有参数,参数表示角色,则其角色可以访问
* permitAll | 用户可以任意访问
* rememberMe | 允许通过remember-me登录的用户访问
* authenticated | 用户登录后可访问
*/
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
// 注解标记允许匿名访问的url
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests();
permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll());
httpSecurity
// CSRF禁用,因为不使用session
.csrf().disable()
.cors().configurationSource(corsConfigurationSource()).and()
// 禁用HTTP响应标头
.headers().cacheControl().disable().and()
// 认证失败处理类
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
// 基于token,所以不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.sessionManagement().sessionFixation().none().and()
// 过滤请求
.authorizeRequests()
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
.antMatchers("/api/thermal/**","/iotDA/device/**", "/websocket/allMemory", "/iot/UAV/**", "/iot/api/**", "/iot/device/userDevice", "/external/sms/sendAliSmsCode", "/login", "/register", "/captchaImage", "/iot/tool/register", "/iot/tool/ntp",
"/iot/tool/mqtt/auth", "/iot/workRecord/**", "/iot/tool/mqtt/authv5", "/iot/tool/mqtt/webhook", "/iot/tool/mqtt/webhookv5", "/auth/**/**",
"/wechat/mobileLogin", "/wechat/miniLogin", "/wechat/wxBind/callback").permitAll()
.antMatchers("/zlmhook/**").permitAll()
.antMatchers("/srs/**").permitAll() //srs服务
//.antMatchers("/sip/player/getBigScreenUrl/**").permitAll()
.antMatchers("/ruleengine/rulemanager/**").permitAll()
.antMatchers("/goview/sys/login", "/goview/project/getData").permitAll()
.antMatchers("/notify/smsLoginCaptcha", "/notify/emailRegisterCaptcha", "/auth/sms/login", "/notify/weComVerifyUrl"
, "/wechat/publicAccount/callback", "/notify/smsRegisterCaptcha").permitAll()
.antMatchers("/app/language/list").permitAll()
.antMatchers("/iot/news/bannerList", "/iot/news/topList", "/iot/news/getDetail").permitAll()
// 静态资源,可匿名访问
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
.antMatchers("/druid/**").permitAll()
.antMatchers("/oauth2/**").permitAll()
.antMatchers("/swagger**", "/dev-api/swagger**", "/dev-api/swagger-ui/**", "/dev-api/v3/api-docs/**").permitAll()
.antMatchers("/**/device/**/**").permitAll()
// // oauth
// .antMatchers("/oauth/css/**","/oauth/fonts/**","/oauth/js/**").permitAll()
// dueros
.antMatchers("/dueros").permitAll()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated()
// // oauth
// .and()
// .formLogin()
// .loginPage("/oauth/login")
// .permitAll()
// .and()
// .logout().logoutUrl("/oauth/logout")
// .permitAll()
.and()
.headers().frameOptions().disable();
// 添加Logout filter
httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
// 添加JWT filter
httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
// 添加CORS filter
httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);
httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class);
}
/**
* 强散列哈希加密实现
*/
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 身份认证接口
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
// CorsConfiguration config = new CorsConfiguration();
// config.setAllowCredentials(true);
// config.addAllowedOriginPattern("*"); // 通配支持
// config.addAllowedHeader("*");
// config.addAllowedMethod("*");
// config.setMaxAge(1800L);
//
// UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// source.registerCorsConfiguration("/**", config);
// return source;
CorsConfiguration config = new CorsConfiguration();
// 关键:设置允许的前端源(替换为你的实际前端域名,如https://ri.satabot.com)
// 填* 会有问题
config.setAllowedOrigins(Arrays.asList("https://ri.satabot.com", "https://ai.satabot.com",
"https://serviceai.satabot.com", "https://serviceri.satabot.com", "https://pvs.satabot.com",
"https://servicepvs.satabot.com", "https://servicepathplan.satabot.com", "https://solar.satabot.com",
"https://servicestream.satabot.com", "http://192.168.2.58", "http://192.168.2.58"));
// config.addAllowedOriginPattern("*");
// 允许的请求方法(必须包含OPTIONS,支持业务请求的GET/POST等)
config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
// 允许的请求头(*表示所有,若有自定义头可单独指定,如Token)
config.setAllowedHeaders(Arrays.asList("*"));
// 允许携带Cookie/Token(开启后,allowedOrigins不能用*,需指定具体域名)
config.setAllowCredentials(true);
// 预检请求缓存时间(单位:秒),减少OPTIONS请求次数
config.setMaxAge(3600L);
// 将跨域配置绑定到所有接口路径
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
}

View File

@@ -0,0 +1,32 @@
package com.maibu.config;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
import com.fastbee.common.utils.ServletUtils;
/**
* 服务相关配置
*
* @author ruoyi
*/
@Component
public class ServerConfig
{
/**
* 获取完整的请求路径,包括:域名,端口,上下文访问路径
*
* @return 服务地址
*/
public String getUrl()
{
HttpServletRequest request = ServletUtils.getRequest();
return getDomain(request);
}
public static String getDomain(HttpServletRequest request)
{
StringBuffer url = request.getRequestURL();
String contextPath = request.getServletContext().getContextPath();
return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString();
}
}

View File

@@ -0,0 +1,92 @@
package com.maibu.config;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* 解决Mybatis Plus Order By SQL注入问题
* @author admin
*/
@Slf4j
public class SqlFilterArgumentResolver implements HandlerMethodArgumentResolver {
private final static String[] KEYWORDS = { "master", "truncate", "insert", "select", "delete", "update", "declare",
"alter", "drop", "sleep" };
/**
* 判断Controller是否包含page 参数
* @param parameter 参数
* @return 是否过滤
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().equals(Page.class);
}
/**
* @param parameter 入参集合
* @param mavContainer model 和 view
* @param webRequest web相关
* @param binderFactory 入参解析
* @return 检查后新的page对象
* <p>
* page 只支持查询 GET .如需解析POST获取请求报文体处理
*/
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
String[] ascs = request.getParameterValues("ascs");
String[] descs = request.getParameterValues("descs");
String current = request.getParameter("current");
String size = request.getParameter("size");
Page<?> page = new Page<>();
if (StrUtil.isNotBlank(current)) {
page.setCurrent(Long.parseLong(current));
}
if (StrUtil.isNotBlank(size)) {
page.setSize(Long.parseLong(size));
}
List<OrderItem> orderItemList = new ArrayList<>();
Optional.ofNullable(ascs).ifPresent(s -> orderItemList.addAll(
Arrays.stream(s).filter(sqlInjectPredicate()).map(OrderItem::asc).collect(Collectors.toList())));
Optional.ofNullable(descs).ifPresent(s -> orderItemList.addAll(
Arrays.stream(s).filter(sqlInjectPredicate()).map(OrderItem::desc).collect(Collectors.toList())));
page.addOrder(orderItemList);
return page;
}
/**
* 判断用户输入里面有没有关键字
* @return Predicate
*/
private Predicate<String> sqlInjectPredicate() {
return sql -> {
for (String keyword : KEYWORDS) {
if (StrUtil.containsIgnoreCase(sql, keyword)) {
return false;
}
}
return true;
};
}
}

View File

@@ -0,0 +1,63 @@
package com.maibu.config;
import com.fastbee.common.utils.Threads;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 线程池配置
*
* @author ruoyi
**/
@Configuration
public class ThreadPoolConfig
{
// 核心线程池大小
private int corePoolSize = 50;
// 最大可创建的线程数
private int maxPoolSize = 200;
// 队列最大长度
private int queueCapacity = 1000;
// 线程池维护线程所允许的空闲时间
private int keepAliveSeconds = 300;
@Bean(name = "threadPoolTaskExecutor")
public ThreadPoolTaskExecutor threadPoolTaskExecutor()
{
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(maxPoolSize);
executor.setCorePoolSize(corePoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setKeepAliveSeconds(keepAliveSeconds);
// 线程池对拒绝任务(无线程可用)的处理策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
/**
* 执行周期性或定时任务
*/
@Bean(name = "scheduledExecutorService")
protected ScheduledExecutorService scheduledExecutorService()
{
return new ScheduledThreadPoolExecutor(corePoolSize,
new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(),
new ThreadPoolExecutor.CallerRunsPolicy())
{
@Override
protected void afterExecute(Runnable r, Throwable t)
{
super.afterExecute(r, t);
Threads.printException(r, t);
}
};
}
}

View File

@@ -0,0 +1,75 @@
package com.maibu.config.properties;
import org.springframework.context.annotation.Configuration;
/**
* druid 配置属性
*
* @author ruoyi
*/
@Configuration
public class DruidProperties
{
// @Value("${spring.datasource.druid.initialSize}")
// private int initialSize;
//
// @Value("${spring.datasource.druid.minIdle}")
// private int minIdle;
//
// @Value("${spring.datasource.druid.maxActive}")
// private int maxActive;
//
// @Value("${spring.datasource.druid.maxWait}")
// private int maxWait;
//
// @Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}")
// private int timeBetweenEvictionRunsMillis;
//
// @Value("${spring.datasource.druid.minEvictableIdleTimeMillis}")
// private int minEvictableIdleTimeMillis;
//
// @Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}")
// private int maxEvictableIdleTimeMillis;
//
// @Value("${spring.datasource.druid.validationQuery}")
// private String validationQuery;
//
// @Value("${spring.datasource.druid.testWhileIdle}")
// private boolean testWhileIdle;
//
// @Value("${spring.datasource.druid.testOnBorrow}")
// private boolean testOnBorrow;
//
// @Value("${spring.datasource.druid.testOnReturn}")
// private boolean testOnReturn;
// public DruidDataSource dataSource(DruidDataSource datasource)
// {
// /** 配置初始化大小、最小、最大 */
// datasource.setInitialSize(initialSize);
// datasource.setMaxActive(maxActive);
// datasource.setMinIdle(minIdle);
//
// /** 配置获取连接等待超时的时间 */
// datasource.setMaxWait(maxWait);
//
// /** 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 */
// datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
//
// /** 配置一个连接在池中最小、最大生存的时间,单位是毫秒 */
// datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
// datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);
//
// /**
// * 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
// */
// datasource.setValidationQuery(validationQuery);
// /** 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 */
// datasource.setTestWhileIdle(testWhileIdle);
// /** 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */
// datasource.setTestOnBorrow(testOnBorrow);
// /** 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */
// datasource.setTestOnReturn(testOnReturn);
// return datasource;
// }
}

View File

@@ -0,0 +1,72 @@
package com.maibu.config.properties;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;
import org.apache.commons.lang3.RegExUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import com.fastbee.common.annotation.Anonymous;
/**
* 设置Anonymous注解允许匿名访问的url
*
* @author ruoyi
*/
@Configuration
public class PermitAllUrlProperties implements InitializingBean, ApplicationContextAware
{
private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}");
private ApplicationContext applicationContext;
private List<String> urls = new ArrayList<>();
public String ASTERISK = "*";
@Override
public void afterPropertiesSet()
{
RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
map.keySet().forEach(info -> {
HandlerMethod handlerMethod = map.get(info);
// 获取方法上边的注解 替代path variable 为 *
Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class);
Optional.ofNullable(method).ifPresent(anonymous -> info.getPatternsCondition().getPatterns()
.forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK))));
// 获取类上边的注解, 替代path variable 为 *
Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class);
Optional.ofNullable(controller).ifPresent(anonymous -> info.getPatternsCondition().getPatterns()
.forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK))));
});
}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException
{
this.applicationContext = context;
}
public List<String> getUrls()
{
return urls;
}
public void setUrls(List<String> urls)
{
this.urls = urls;
}
}

View File

@@ -0,0 +1,134 @@
package com.maibu.config.properties;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.redisson.config.ReadMode;
import org.redisson.config.SubscriptionMode;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Redisson 配置属性
*
*/
@Data
@ConfigurationProperties(prefix = "redisson")
public class RedissonProperties {
/**
* redis缓存key前缀
*/
private String keyPrefix;
/**
* 线程池数量,默认值 = 当前处理核数量 * 2
*/
private int threads;
/**
* Netty线程池数量,默认值 = 当前处理核数量 * 2
*/
private int nettyThreads;
/**
* 单机服务配置
*/
private SingleServerConfig singleServerConfig;
/**
* 集群服务配置
*/
private ClusterServersConfig clusterServersConfig;
@Data
@NoArgsConstructor
public static class SingleServerConfig {
/**
* 客户端名称
*/
private String clientName;
/**
* 最小空闲连接数
*/
private int connectionMinimumIdleSize;
/**
* 连接池大小
*/
private int connectionPoolSize;
/**
* 连接空闲超时,单位:毫秒
*/
private int idleConnectionTimeout;
/**
* 命令等待超时,单位:毫秒
*/
private int timeout;
/**
* 发布和订阅连接池大小
*/
private int subscriptionConnectionPoolSize;
}
@Data
@NoArgsConstructor
public static class ClusterServersConfig {
/**
* 客户端名称
*/
private String clientName;
/**
* master最小空闲连接数
*/
private int masterConnectionMinimumIdleSize;
/**
* master连接池大小
*/
private int masterConnectionPoolSize;
/**
* slave最小空闲连接数
*/
private int slaveConnectionMinimumIdleSize;
/**
* slave连接池大小
*/
private int slaveConnectionPoolSize;
/**
* 连接空闲超时,单位:毫秒
*/
private int idleConnectionTimeout;
/**
* 命令等待超时,单位:毫秒
*/
private int timeout;
/**
* 发布和订阅连接池大小
*/
private int subscriptionConnectionPoolSize;
/**
* 读取模式
*/
private ReadMode readMode;
/**
* 订阅模式
*/
private SubscriptionMode subscriptionMode;
}
}

View File

@@ -0,0 +1,262 @@
package com.maibu.config.sharding;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.druid.util.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.fastbee.framework.config.sharding.enums.ShardingTableCacheEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.driver.jdbc.core.datasource.ShardingSphereDataSource;
import org.apache.shardingsphere.infra.config.RuleConfiguration;
import org.apache.shardingsphere.mode.manager.ContextManager;
import org.apache.shardingsphere.sharding.algorithm.config.AlgorithmProvidedShardingRuleConfiguration;
import org.apache.shardingsphere.sharding.api.config.rule.ShardingTableRuleConfiguration;
import org.springframework.core.env.Environment;
import java.sql.*;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
/**
* <p> @Title ShardingAlgorithmTool
* <p> @Description 按月分片算法工具
*
*/
@Slf4j
public class ShardingAlgorithmTool {
/** 表分片符号,例:siot_device_log_202201 中,分片符号为 "_" */
private static final String TABLE_SPLIT_SYMBOL = "_";
/** 数据库配置 */
private static final Environment ENV = SpringUtil.getApplicationContext().getEnvironment();
private static final String DATASOURCE_URL = ENV.getProperty("spring.shardingsphere.datasource.ds0.url");
private static final String DATASOURCE_USERNAME = ENV.getProperty("spring.shardingsphere.datasource.ds0.username");
private static final String DATASOURCE_PASSWORD = ENV.getProperty("spring.shardingsphere.datasource.ds0.password");
/**
* 检查分表获取的表名是否存在,不存在则自动建表
* @param logicTable 逻辑表
* @param resultTableNames 真实表名,例:iot_device_log_202201
* @return 存在于数据库中的真实表名集合
*/
public static Set<String> getShardingTablesAndCreate(ShardingTableCacheEnum logicTable, Collection<String> resultTableNames) {
return resultTableNames.stream().map(o -> getShardingTableAndCreate(logicTable, o)).collect(Collectors.toSet());
}
/**
* 检查分表获取的表名是否存在,不存在则自动建表
* @param logicTable 逻辑表
* @param resultTableName 真实表名,例:iot_device_log_202201
* @return 确认存在于数据库中的真实表名
*/
public static String getShardingTableAndCreate(ShardingTableCacheEnum logicTable, String resultTableName) {
// 缓存中有此表则返回,没有则判断创建
if (logicTable.resultTableNamesCache().contains(resultTableName)) {
return resultTableName;
} else {
// 未创建的表返回逻辑空表
boolean isSuccess = createShardingTable(logicTable, resultTableName);
return isSuccess ? resultTableName : logicTable.logicTableName();
}
}
/**
* 重载全部缓存
*/
public static void tableNameCacheReloadAll() {
Arrays.stream(ShardingTableCacheEnum.values()).forEach(ShardingAlgorithmTool::tableNameCacheReload);
}
/**
* 重载指定分表缓存
* @param logicTable 逻辑表
*/
public static void tableNameCacheReload(ShardingTableCacheEnum logicTable) {
// 读取数据库中所有表名
List<String> tableNameList = getAllTableNameBySchema(logicTable);
// 更新缓存、配置(原子操作)
logicTable.atomicUpdateCacheAndActualDataNodes(tableNameList);
// 删除旧的缓存(如果存在)
logicTable.resultTableNamesCache().clear();
// 写入新的缓存
logicTable.resultTableNamesCache().addAll(tableNameList);
// 动态更新配置 actualDataNodes
actualDataNodesRefresh(logicTable.logicTableName(), tableNameList);
}
/**
* 获取所有表名
* @return 表名集合
* @param logicTable 逻辑表
*/
public static List<String> getAllTableNameBySchema(ShardingTableCacheEnum logicTable) {
List<String> tableNames = new ArrayList<>();
if (StringUtils.isEmpty(DATASOURCE_URL) || StringUtils.isEmpty(DATASOURCE_USERNAME) || StringUtils.isEmpty(DATASOURCE_PASSWORD)) {
log.error(">>>>>>>>>> 【ERROR】数据库连接配置有误,请稍后重试,URL:{}, username:{}, password:{}", DATASOURCE_URL, DATASOURCE_USERNAME, DATASOURCE_PASSWORD);
throw new IllegalArgumentException("数据库连接配置有误,请稍后重试");
}
try (Connection conn = DriverManager.getConnection(DATASOURCE_URL, DATASOURCE_USERNAME, DATASOURCE_PASSWORD);
Statement st = conn.createStatement()) {
String logicTableName = logicTable.logicTableName();
try (ResultSet rs = st.executeQuery("show TABLES like '" + logicTableName + TABLE_SPLIT_SYMBOL + "%'")) {
log.info("查询数据库所有表:{}","show TABLES like '" + logicTableName + TABLE_SPLIT_SYMBOL + "%'");
while (rs.next()) {
String tableName = rs.getString(1);
log.info("分表格式:{}",String.format("^(%s\\d{6})$", logicTableName + TABLE_SPLIT_SYMBOL));
// 匹配分表格式 例:^(t\_contract_\d{6})$
if (org.apache.commons.lang3.StringUtils.isNotBlank(tableName) && tableName.matches(String.format("^(%s\\d{6})$", logicTableName + TABLE_SPLIT_SYMBOL))) {
tableNames.add(rs.getString(1));
}
}
}
} catch (SQLException e) {
log.error(">>>>>>>>>> 【ERROR】数据库连接失败,请稍后重试,原因:{}", e.getMessage(), e);
throw new IllegalArgumentException("数据库连接失败,请稍后重试");
}
return tableNames;
}
/**
* 动态更新配置 actualDataNodes
*
* @param logicTableName 逻辑表名
* @param tableNamesCache 真实表名集合
*/
public static void actualDataNodesRefresh(String logicTableName, List<String> tableNamesCache) {
try {
if (CollectionUtils.isEmpty(tableNamesCache)) {
return;
}
// 获取数据分片节点
String dbName = "ds0";
log.info(">>>>>>>>>> 【INFO】更新分表配置,logicTableName:{},tableNamesCache:{}", logicTableName, tableNamesCache);
// generate actualDataNodes
String newActualDataNodes = tableNamesCache.stream().map(o -> String.format("%s.%s", dbName, o)).collect(Collectors.joining(","));
ShardingSphereDataSource shardingSphereDataSource = SpringUtil.getBean(ShardingSphereDataSource.class);
updateShardRuleActualDataNodes(shardingSphereDataSource, logicTableName, newActualDataNodes);
}catch (Exception e){
log.error("初始化 动态表单失败,原因:{}", e.getMessage(), e);
}
}
// --------------------------------------------------------------------------------------------------------------
// 私有方法
// --------------------------------------------------------------------------------------------------------------
/**
* 刷新ActualDataNodes
*/
private static void updateShardRuleActualDataNodes(ShardingSphereDataSource dataSource, String logicTableName, String newActualDataNodes) {
// Context manager.
ContextManager contextManager = dataSource.getContextManager();
// Rule configuration.
String schemaName = "logic_db";
Collection<RuleConfiguration> newRuleConfigList = new LinkedList<>();
Collection<RuleConfiguration> oldRuleConfigList = dataSource.getContextManager()
.getMetaDataContexts()
.getMetaData(schemaName)
.getRuleMetaData()
.getConfigurations();
for (RuleConfiguration oldRuleConfig : oldRuleConfigList) {
if (oldRuleConfig instanceof AlgorithmProvidedShardingRuleConfiguration) {
// Algorithm provided sharding rule configuration
AlgorithmProvidedShardingRuleConfiguration oldAlgorithmConfig = (AlgorithmProvidedShardingRuleConfiguration) oldRuleConfig;
AlgorithmProvidedShardingRuleConfiguration newAlgorithmConfig = new AlgorithmProvidedShardingRuleConfiguration();
// Sharding table rule configuration Collection
Collection<ShardingTableRuleConfiguration> newTableRuleConfigList = new LinkedList<>();
Collection<ShardingTableRuleConfiguration> oldTableRuleConfigList = oldAlgorithmConfig.getTables();
oldTableRuleConfigList.forEach(oldTableRuleConfig -> {
if (logicTableName.equals(oldTableRuleConfig.getLogicTable())) {
ShardingTableRuleConfiguration newTableRuleConfig = new ShardingTableRuleConfiguration(oldTableRuleConfig.getLogicTable(), newActualDataNodes);
newTableRuleConfig.setTableShardingStrategy(oldTableRuleConfig.getTableShardingStrategy());
newTableRuleConfig.setDatabaseShardingStrategy(oldTableRuleConfig.getDatabaseShardingStrategy());
newTableRuleConfig.setKeyGenerateStrategy(oldTableRuleConfig.getKeyGenerateStrategy());
newTableRuleConfigList.add(newTableRuleConfig);
} else {
newTableRuleConfigList.add(oldTableRuleConfig);
}
});
newAlgorithmConfig.setTables(newTableRuleConfigList);
newAlgorithmConfig.setAutoTables(oldAlgorithmConfig.getAutoTables());
newAlgorithmConfig.setBindingTableGroups(oldAlgorithmConfig.getBindingTableGroups());
newAlgorithmConfig.setBroadcastTables(oldAlgorithmConfig.getBroadcastTables());
newAlgorithmConfig.setDefaultDatabaseShardingStrategy(oldAlgorithmConfig.getDefaultDatabaseShardingStrategy());
newAlgorithmConfig.setDefaultTableShardingStrategy(oldAlgorithmConfig.getDefaultTableShardingStrategy());
newAlgorithmConfig.setDefaultKeyGenerateStrategy(oldAlgorithmConfig.getDefaultKeyGenerateStrategy());
newAlgorithmConfig.setDefaultShardingColumn(oldAlgorithmConfig.getDefaultShardingColumn());
newAlgorithmConfig.setShardingAlgorithms(oldAlgorithmConfig.getShardingAlgorithms());
newAlgorithmConfig.setKeyGenerators(oldAlgorithmConfig.getKeyGenerators());
newRuleConfigList.add(newAlgorithmConfig);
}
}
// update context
contextManager.alterRuleConfiguration(schemaName, newRuleConfigList);
}
/**
* 创建分表
* @param logicTable 逻辑表
* @param resultTableName 真实表名,例:sys_user_behavior_202201
* @return 创建结果(true创建成功,false未创建)
*/
private static boolean createShardingTable(ShardingTableCacheEnum logicTable, String resultTableName) {
// 根据日期判断,当前月份之后分表不提前创建
String month = resultTableName.replace(logicTable.logicTableName() + TABLE_SPLIT_SYMBOL,"");
YearMonth shardingMonth = YearMonth.parse(month, DateTimeFormatter.ofPattern("yyyyMM"));
if (shardingMonth.isAfter(YearMonth.now())) {
return false;
}
synchronized (logicTable.logicTableName().intern()) {
// 缓存中有此表 返回
if (logicTable.resultTableNamesCache().contains(resultTableName)) {
return false;
}
// 缓存中无此表,则建表并添加缓存
executeSql(Collections.singletonList("CREATE TABLE IF NOT EXISTS `" + resultTableName + "` LIKE `" + logicTable.logicTableName() + "`;"));
// 缓存重载
tableNameCacheReload(logicTable);
}
return true;
}
/**
* 执行SQL
* @param sqlList SQL集合
*/
private static void executeSql(List<String> sqlList) {
if (StringUtils.isEmpty(DATASOURCE_URL) || StringUtils.isEmpty(DATASOURCE_USERNAME) || StringUtils.isEmpty(DATASOURCE_PASSWORD)) {
log.error(">>>>>>>>>> 【ERROR】数据库连接配置有误,请稍后重试,URL:{}, username:{}, password:{}", DATASOURCE_URL, DATASOURCE_USERNAME, DATASOURCE_PASSWORD);
throw new IllegalArgumentException("数据库连接配置有误,请稍后重试");
}
try (Connection conn = DriverManager.getConnection(DATASOURCE_URL, DATASOURCE_USERNAME, DATASOURCE_PASSWORD)) {
try (Statement st = conn.createStatement()) {
conn.setAutoCommit(false);
for (String sql : sqlList) {
st.execute(sql);
}
} catch (Exception e) {
conn.rollback();
log.error(">>>>>>>>>> 【ERROR】数据表创建执行失败,请稍后重试,原因:{}", e.getMessage(), e);
throw new IllegalArgumentException("数据表创建执行失败,请稍后重试");
}
} catch (SQLException e) {
log.error(">>>>>>>>>> 【ERROR】数据库连接失败,请稍后重试,原因:{}", e.getMessage(), e);
throw new IllegalArgumentException("数据库连接失败,请稍后重试");
}
}
}

View File

@@ -0,0 +1,21 @@
package com.maibu.config.sharding;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* <p> @Title ShardingTablesLoadRunner
* <p> @Description 项目启动后,读取已有分表,进行缓存
*
*/
@Order(value = 1) // 数字越小,越先执行
@Component
public class ShardingTablesLoadRunner implements CommandLineRunner {
@Override
public void run(String... args) {
// 读取已有分表,进行缓存
//ShardingAlgorithmTool.tableNameCacheReloadAll();
}
}

View File

@@ -0,0 +1,191 @@
package com.maibu.config.sharding;
import com.fastbee.framework.config.sharding.enums.ShardingTableCacheEnum;
import com.google.common.collect.Range;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;
import org.springframework.util.CollectionUtils;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
/**
* <p> @Title TimeShardingAlgorithm
* <p> @Description 分片算法,按月分片
*
*/
@Slf4j
public class TimeShardingAlgorithm implements StandardShardingAlgorithm<Date> {
/**
* Date类型的分片时间格式
*/
private static final SimpleDateFormat TABLE_SHARD_Date_FORMATTER = new SimpleDateFormat("yyyyMM");
/**
* 分片时间格式
*/
private static final DateTimeFormatter TABLE_SHARD_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMM");
/**
* 完整时间格式
*/
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd HH:mm:ss");
/**
* 完整时间格式
*/
private static final SimpleDateFormat DATE_TIME_FORMATTER_SPILE = new SimpleDateFormat("yyyy-MM-dd");
/**
* 表分片符号,例:t_user_202201 中,分片符号为 "_"
*/
private final String TABLE_SPLIT_SYMBOL = "_";
/**
* 精准分片
* @param tableNames 对应分片库中所有分片表的集合
* @param preciseShardingValue 分片键值,其中 logicTableName 为逻辑表,columnName 分片键,value 为从 SQL 中解析出来的分片键的值
* @return 表名
*/
@Override
public String doSharding(Collection<String> tableNames, PreciseShardingValue<Date> preciseShardingValue) {
String logicTableName = preciseShardingValue.getLogicTableName();
ShardingTableCacheEnum logicTable = ShardingTableCacheEnum.of(logicTableName);
createAllTable(logicTable, tableNames);
/// 打印分片信息
log.info(">>>>>>>>>> 【INFO】精确分片,节点配置表名:{},数据库缓存表名:{}", tableNames, logicTable.resultTableNamesCache());
Date date = preciseShardingValue.getValue();
Instant instant = date.toInstant();
LocalDateTime localDateTime = instant.atZone(ZoneId.systemDefault()).toLocalDateTime();
String resultTableName = logicTableName + "_" + TABLE_SHARD_TIME_FORMATTER.format(localDateTime);
// 检查分表获取的表名是否存在,不存在则自动建表
if (!tableNames.contains(resultTableName)){
tableNames.add(resultTableName);
}
return ShardingAlgorithmTool.getShardingTableAndCreate(logicTable, resultTableName);
}
/**
* 范围分片
* @param tableNames 对应分片库中所有分片表的集合
* @param rangeShardingValue 分片范围
* @return 表名集合
*/
@Override
public Collection<String> doSharding(Collection<String> tableNames, RangeShardingValue<Date> rangeShardingValue) {
log.info("开始分表查询开始:{}",System.currentTimeMillis());
String logicTableName = rangeShardingValue.getLogicTableName();
ShardingTableCacheEnum logicTable = ShardingTableCacheEnum.of(logicTableName);
createAllTable(logicTable, tableNames);
/// 打印分片信息
log.info(">>>>>>>>>> 【INFO】范围分片,节点配置表名:{},数据库缓存表名:{}", tableNames, logicTable.resultTableNamesCache());
// between and 的起始值
Range<Date> valueRange = rangeShardingValue.getValueRange();
boolean hasLowerBound = valueRange.hasLowerBound();
boolean hasUpperBound = valueRange.hasUpperBound();
// 获取最大值和最小值
Set<String> tableNameCache = logicTable.resultTableNamesCache();
String min = hasLowerBound ? String.valueOf(valueRange.lowerEndpoint()) : getLowerEndpoint(tableNameCache);
String max = hasUpperBound ? String.valueOf(valueRange.upperEndpoint()) : getUpperEndpoint(tableNameCache);
// 循环计算分表范围
Set<String> resultTableNames = new LinkedHashSet<>();
try {
Date minDate = DATE_TIME_FORMATTER_SPILE.parse(min);
Date maxDate = DATE_TIME_FORMATTER_SPILE.parse(max);
Calendar calendar = Calendar.getInstance();
while (minDate.before(maxDate) || minDate.equals(maxDate)) {
String tableName = logicTableName + TABLE_SPLIT_SYMBOL + TABLE_SHARD_Date_FORMATTER.format(minDate);
resultTableNames.add(tableName);
calendar.setTime(minDate); // 设置Calendar的时间为Date对象的时间
calendar.add(Calendar.DAY_OF_MONTH, 1); // 给日期加一天
minDate = calendar.getTime();
}
log.info("开始分表查询结束:{}",System.currentTimeMillis());
return ShardingAlgorithmTool.getShardingTablesAndCreate(logicTable, resultTableNames);
} catch (Exception e) {
return ShardingAlgorithmTool.getShardingTablesAndCreate(logicTable, logicTable.resultTableNamesCache());
}
}
@Override
public void init() {
}
@Override
public String getType() {
return null;
}
// --------------------------------------------------------------------------------------------------------------
// 私有方法
// --------------------------------------------------------------------------------------------------------------
/**
* 获取 最小分片值
* @param tableNames 表名集合
* @return 最小分片值
*/
private String getLowerEndpoint(Collection<String> tableNames) {
Optional<LocalDateTime> optional = tableNames.stream()
.map(o -> LocalDateTime.parse(o.replace(TABLE_SPLIT_SYMBOL, "") + "01 00:00:00", DATE_TIME_FORMATTER))
.min(Comparator.comparing(Function.identity()));
if (optional.isPresent()) {
ZonedDateTime zonedDateTime = optional.get().atZone(ZoneId.systemDefault());
Instant instant = zonedDateTime.toInstant();
return String.valueOf(Date.from(instant));
} else {
log.error(">>>>>>>>>> 【ERROR】获取数据最小分表失败,请稍后重试,tableName:{}", tableNames);
throw new IllegalArgumentException("获取数据最小分表失败,请稍后重试");
}
}
/**
* 获取 最大分片值
* @param tableNames 表名集合
* @return 最大分片值
*/
private String getUpperEndpoint(Collection<String> tableNames) {
Optional<LocalDateTime> optional = tableNames.stream()
.map(o -> LocalDateTime.parse(o.replace(TABLE_SPLIT_SYMBOL, "") + "01 00:00:00", DATE_TIME_FORMATTER))
.max(Comparator.comparing(Function.identity()));
if (optional.isPresent()) {
ZonedDateTime zonedDateTime = optional.get().atZone(ZoneId.systemDefault());
Instant instant = zonedDateTime.toInstant();
return String.valueOf(Date.from(instant));
} else {
log.error(">>>>>>>>>> 【ERROR】获取数据最大分表失败,请稍后重试,tableName:{}", tableNames);
throw new IllegalArgumentException("获取数据最大分表失败,请稍后重试");
}
}
/**
* 根据分片规则获取的表,创建所有的表
* @param logicTable
* @param tableNames
*/
private void createAllTable(ShardingTableCacheEnum logicTable, Collection<String> tableNames) {
if (!CollectionUtils.isEmpty(logicTable.resultTableNamesCache())) {
//如果缓存中有表了,则证明已经创建了表,无需再创建
return;
}
//根据分片规则创建表
ShardingAlgorithmTool.getShardingTablesAndCreate(logicTable,tableNames);
//刷新缓存
ShardingAlgorithmTool.tableNameCacheReload(logicTable);
}
}

View File

@@ -0,0 +1,83 @@
package com.maibu.config.sharding.enums;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import java.util.*;
import static com.fastbee.framework.config.sharding.ShardingAlgorithmTool.actualDataNodesRefresh;
/**
* <p> @Title ShardingTableCacheEnum
* <p> @Description 分片表缓存枚举
*
*/
public enum ShardingTableCacheEnum {
/**
* 用户埋点表
*/
DEVICE_LOG("iot_device_log", new HashSet<>());
/**
* 逻辑表名
*/
private final String logicTableName;
/**
* 实际表名
*/
private final Set<String> resultTableNamesCache;
private static Map<String, ShardingTableCacheEnum> valueMap = new HashMap<>();
static {
Arrays.stream(ShardingTableCacheEnum.values()).forEach(o -> valueMap.put(o.logicTableName, o));
}
ShardingTableCacheEnum(String logicTableName, Set<String> resultTableNamesCache) {
this.logicTableName = logicTableName;
this.resultTableNamesCache = resultTableNamesCache;
}
public static ShardingTableCacheEnum of(String value) {
return valueMap.get(value);
}
public String logicTableName() {
return logicTableName;
}
public Set<String> resultTableNamesCache() {
return resultTableNamesCache;
}
/**
* 更新缓存、配置(原子操作)
*
* @param tableNameList
*/
public void atomicUpdateCacheAndActualDataNodes(List<String> tableNameList) {
if (CollectionUtils.isEmpty(tableNameList)) {
return;
}
synchronized (resultTableNamesCache) {
// 删除缓存
resultTableNamesCache.clear();
// 写入新的缓存
resultTableNamesCache.addAll(tableNameList);
// 动态更新配置 actualDataNodes
actualDataNodesRefresh(logicTableName, tableNameList);
}
}
public static Set<String> logicTableNames() {
return valueMap.keySet();
}
@Override
public String toString() {
return "ShardingTableCacheEnum{" +
"logicTableName='" + logicTableName + '\'' +
", resultTableNamesCache=" + resultTableNamesCache +
'}';
}
}

View File

@@ -0,0 +1,30 @@
package com.maibu.datasource;
import java.util.Map;
import javax.sql.DataSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* 动态数据源
*
* @author ruoyi
*/
@Slf4j
public class DynamicDataSource extends AbstractRoutingDataSource
{
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources)
{
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
}
@Override
protected Object determineCurrentLookupKey()
{
//log.debug("数据源:" + DynamicDataSourceContextHolder.getDataSourceType());
return DynamicDataSourceContextHolder.getDataSourceType();
}
}

View File

@@ -0,0 +1,45 @@
package com.maibu.datasource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 数据源切换处理
*
* @author ruoyi
*/
public class DynamicDataSourceContextHolder
{
public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
/**
* 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
*/
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
/**
* 设置数据源的变量
*/
public static void setDataSourceType(String dsType)
{
log.info("切换到{}数据源", dsType);
CONTEXT_HOLDER.set(dsType);
}
/**
* 获得数据源的变量
*/
public static String getDataSourceType()
{
return CONTEXT_HOLDER.get();
}
/**
* 清空数据源变量
*/
public static void clearDataSourceType()
{
CONTEXT_HOLDER.remove();
}
}

View File

@@ -0,0 +1,50 @@
package com.maibu.handler;
import com.fastbee.common.utils.StringUtils;
import org.redisson.api.NameMapper;
/**
* redis缓存key前缀处理
*
* @author ye
* @date 2022/7/14 17:44
* @since 4.3.0
*/
public class KeyPrefixHandler implements NameMapper {
private final String keyPrefix;
public KeyPrefixHandler(String keyPrefix) {
//前缀为空 则返回空前缀
this.keyPrefix = StringUtils.isBlank(keyPrefix) ? "" : keyPrefix + ":";
}
/**
* 增加前缀
*/
@Override
public String map(String name) {
if (StringUtils.isBlank(name)) {
return null;
}
if (StringUtils.isNotBlank(keyPrefix) && !name.startsWith(keyPrefix)) {
return keyPrefix + name;
}
return name;
}
/**
* 去除前缀
*/
@Override
public String unmap(String name) {
if (StringUtils.isBlank(name)) {
return null;
}
if (StringUtils.isNotBlank(keyPrefix) && name.startsWith(keyPrefix)) {
return name.substring(keyPrefix.length());
}
return name;
}
}

View File

@@ -0,0 +1,58 @@
package com.maibu.interceptor;
import com.fastbee.common.utils.SecurityUtils;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.system.enums.Language;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;
import static com.fastbee.common.constant.Constants.LANGUAGE;
/**
* 国际化语言拦截器
*/
@Component
@Slf4j
public class LanguageInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String language = request.getHeader(LANGUAGE);
if (StringUtils.isEmpty(language) || language.equals("undefined")
|| language.equals("")|| language.equals("null")){
//针对有些接口没有增加语言字段,先去token获取
language = SecurityUtils.getLanguage();
}else if (!language.contains("-")){
language = Language.matches(language);
}
// 前端传递的language必须是zh-CN格式的,中间的-必须要完整,不能只传递zh或en
Locale locale = new Locale(language.split("-")[0],language.split("-")[1]);
// 这样赋值以后,MessageUtils.message方法就不用修改了
LocaleContextHolder.setLocale(locale);
return true;
}
/**
* 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
}
/**
* 在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
}
}

View File

@@ -0,0 +1,55 @@
package com.maibu.interceptor;
import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import com.alibaba.fastjson2.JSON;
import com.fastbee.common.annotation.RepeatSubmit;
import com.fastbee.common.core.domain.AjaxResult;
import com.fastbee.common.utils.ServletUtils;
/**
* 防止重复提交拦截器
*
* @author ruoyi
*/
@Component
public abstract class RepeatSubmitInterceptor implements HandlerInterceptor
{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
{
if (handler instanceof HandlerMethod)
{
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
if (annotation != null)
{
if (this.isRepeatSubmit(request, annotation))
{
AjaxResult ajaxResult = AjaxResult.error(annotation.message());
ServletUtils.renderString(response, JSON.toJSONString(ajaxResult));
return false;
}
}
return true;
}
else
{
return true;
}
}
/**
* 验证是否重复提交由子类实现具体的防重复提交的规则
*
* @param request
* @return
* @throws Exception
*/
public abstract boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation);
}

View File

@@ -0,0 +1,110 @@
package com.maibu.interceptor.impl;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson2.JSON;
import com.fastbee.common.annotation.RepeatSubmit;
import com.fastbee.common.constant.CacheConstants;
import com.fastbee.common.core.redis.RedisCache;
import com.fastbee.common.filter.RepeatedlyRequestWrapper;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.common.utils.http.HttpHelper;
import com.fastbee.framework.interceptor.RepeatSubmitInterceptor;
/**
* 判断请求url和数据是否和上一次相同,
* 如果和上次相同,则是重复提交表单。 有效时间为10秒内。
*
* @author ruoyi
*/
@Component
public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
{
public final String REPEAT_PARAMS = "repeatParams";
public final String REPEAT_TIME = "repeatTime";
// 令牌自定义标识
@Value("${token.header}")
private String header;
@Autowired
private RedisCache redisCache;
@SuppressWarnings("unchecked")
@Override
public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation)
{
String nowParams = "";
if (request instanceof RepeatedlyRequestWrapper)
{
RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request;
nowParams = HttpHelper.getBodyString(repeatedlyRequest);
}
// body参数为空,获取Parameter的数据
if (StringUtils.isEmpty(nowParams))
{
nowParams = JSON.toJSONString(request.getParameterMap());
}
Map<String, Object> nowDataMap = new HashMap<String, Object>();
nowDataMap.put(REPEAT_PARAMS, nowParams);
nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());
// 请求地址(作为存放cache的key值)
String url = request.getRequestURI();
// 唯一值(没有消息头则使用请求地址)
String submitKey = StringUtils.trimToEmpty(request.getHeader(header));
// 唯一标识(指定key + url + 消息头)
String cacheRepeatKey = CacheConstants.REPEAT_SUBMIT_KEY + url + submitKey;
Object sessionObj = redisCache.getCacheObject(cacheRepeatKey);
if (sessionObj != null)
{
Map<String, Object> sessionMap = (Map<String, Object>) sessionObj;
if (sessionMap.containsKey(url))
{
Map<String, Object> preDataMap = (Map<String, Object>) sessionMap.get(url);
if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, annotation.interval()))
{
return true;
}
}
}
Map<String, Object> cacheMap = new HashMap<String, Object>();
cacheMap.put(url, nowDataMap);
redisCache.setCacheObject(cacheRepeatKey, cacheMap, annotation.interval(), TimeUnit.MILLISECONDS);
return false;
}
/**
* 判断参数是否相同
*/
private boolean compareParams(Map<String, Object> nowMap, Map<String, Object> preMap)
{
String nowParams = (String) nowMap.get(REPEAT_PARAMS);
String preParams = (String) preMap.get(REPEAT_PARAMS);
return nowParams.equals(preParams);
}
/**
* 判断两次间隔时间
*/
private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap, int interval)
{
long time1 = (Long) nowMap.get(REPEAT_TIME);
long time2 = (Long) preMap.get(REPEAT_TIME);
if ((time1 - time2) < interval)
{
return true;
}
return false;
}
}

View File

@@ -0,0 +1,55 @@
package com.maibu.manager;
import java.util.TimerTask;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import com.fastbee.common.utils.Threads;
import com.fastbee.common.utils.spring.SpringUtils;
/**
* 异步任务管理器
*
* @author ruoyi
*/
public class AsyncManager
{
/**
* 操作延迟10毫秒
*/
private final int OPERATE_DELAY_TIME = 10;
/**
* 异步操作任务调度线程池
*/
private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService");
/**
* 单例模式
*/
private AsyncManager(){}
private static AsyncManager me = new AsyncManager();
public static AsyncManager me()
{
return me;
}
/**
* 执行任务
*
* @param task 任务
*/
public void execute(TimerTask task)
{
executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);
}
/**
* 停止任务线程池
*/
public void shutdown()
{
Threads.shutdownAndAwaitTermination(executor);
}
}

View File

@@ -0,0 +1,88 @@
package com.maibu.manager;
import org.springframework.cache.Cache;
import com.fastbee.common.utils.spring.SpringUtils;
import java.util.concurrent.Callable;
/**
* Cache 装饰器模式(用于扩展 Caffeine 一级缓存)
*
*/
public class CaffeineCacheDecorator implements Cache {
private static final com.github.benmanes.caffeine.cache.Cache<Object, Object>
CAFFEINE = SpringUtils.getBean("caffeine");
private final Cache cache;
public CaffeineCacheDecorator(Cache cache) {
this.cache = cache;
}
@Override
public String getName() {
return cache.getName();
}
@Override
public Object getNativeCache() {
return cache.getNativeCache();
}
public String getUniqueKey(Object key) {
return cache.getName() + ":" + key;
}
@Override
public ValueWrapper get(Object key) {
Object o = CAFFEINE.get(getUniqueKey(key), k -> cache.get(key));
return (ValueWrapper) o;
}
@SuppressWarnings("unchecked")
public <T> T get(Object key, Class<T> type) {
Object o = CAFFEINE.get(getUniqueKey(key), k -> cache.get(key, type));
return (T) o;
}
@Override
public void put(Object key, Object value) {
CAFFEINE.invalidate(getUniqueKey(key));
cache.put(key, value);
}
public ValueWrapper putIfAbsent(Object key, Object value) {
CAFFEINE.invalidate(getUniqueKey(key));
return cache.putIfAbsent(key, value);
}
@Override
public void evict(Object key) {
evictIfPresent(key);
}
public boolean evictIfPresent(Object key) {
boolean b = cache.evictIfPresent(key);
if (b) {
CAFFEINE.invalidate(getUniqueKey(key));
}
return b;
}
@Override
public void clear() {
cache.clear();
}
public boolean invalidate() {
return cache.invalidate();
}
@SuppressWarnings("unchecked")
@Override
public <T> T get(Object key, Callable<T> valueLoader) {
Object o = CAFFEINE.get(getUniqueKey(key), k -> cache.get(key, valueLoader));
return (T) o;
}
}

View File

@@ -0,0 +1,30 @@
package com.maibu.manager;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.*;
@Component
public class DeleteKeyGenerator implements KeyGenerator {
@Override
public Object generate(Object target, Method method, Object... params) {
if (params.length > 0) {
Object ids = params[0];
Collection<?> collection = Collections.singletonList(ids);
if (collection.stream().allMatch(item -> item instanceof Long)) {
// 将 Collection<Long> 转换为 String[]
String[] keys = collection.stream()
.map(Object::toString)
.toArray(String[]::new);
// 使用 keys
List<Object> keysList = new ArrayList<>(keys.length);
keysList.addAll(Arrays.asList(keys));
return keysList;
}
}
return new String[0];
}
}

View File

@@ -0,0 +1,39 @@
package com.maibu.manager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.annotation.PreDestroy;
/**
* 确保应用退出时能关闭后台线程
*
* @author ruoyi
*/
@Component
public class ShutdownManager
{
private static final Logger logger = LoggerFactory.getLogger("sys-user");
@PreDestroy
public void destroy()
{
shutdownAsyncManager();
}
/**
* 停止异步执行任务
*/
private void shutdownAsyncManager()
{
try
{
logger.info("====关闭后台任务任务线程池====");
AsyncManager.me().shutdown();
}
catch (Exception e)
{
logger.error(e.getMessage(), e);
}
}
}

View File

@@ -0,0 +1,186 @@
package com.maibu.manager;
import com.fastbee.framework.utils.RedisUtils;
import org.redisson.api.RMap;
import org.redisson.api.RMapCache;
import org.redisson.spring.cache.CacheConfig;
import org.redisson.spring.cache.RedissonCache;
import org.springframework.boot.convert.DurationStyle;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.transaction.TransactionAwareCacheDecorator;
import org.springframework.util.StringUtils;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* A {@link CacheManager} implementation
* backed by Redisson instance.
* <p>
* 修改 RedissonSpringCacheManager 源码
* 重写 cacheName 处理方法 支持多参数
*
*
*/
@SuppressWarnings("unchecked")
public class SpringCacheManager implements CacheManager {
private boolean dynamic = true;
private boolean allowNullValues = true;
private boolean transactionAware = true;
private Long ttl;
Map<String, CacheConfig> configMap = new ConcurrentHashMap<>();
ConcurrentMap<String, Cache> instanceMap = new ConcurrentHashMap<>();
/**
* Creates CacheManager supplied by Redisson instance
*/
public SpringCacheManager() {
}
public SpringCacheManager(Long ttl) {
this.ttl = ttl;
}
/**
* Defines possibility of storing {@code null} values.
* <p>
* Default is <code>true</code>
*
* @param allowNullValues stores if <code>true</code>
*/
public void setAllowNullValues(boolean allowNullValues) {
this.allowNullValues = allowNullValues;
}
/**
* Defines if cache aware of Spring-managed transactions.
* If {@code true} put/evict operations are executed only for successful transaction in after-commit phase.
* <p>
* Default is <code>false</code>
*
* @param transactionAware cache is transaction aware if <code>true</code>
*/
public void setTransactionAware(boolean transactionAware) {
this.transactionAware = transactionAware;
}
/**
* Defines 'fixed' cache names.
* A new cache instance will not be created in dynamic for non-defined names.
* <p>
* `null` parameter setups dynamic mode
*
* @param names of caches
*/
public void setCacheNames(Collection<String> names) {
if (names != null) {
for (String name : names) {
getCache(name);
}
dynamic = false;
} else {
dynamic = true;
}
}
/**
* Set cache config mapped by cache name
*
* @param config object
*/
public void setConfig(Map<String, ? extends CacheConfig> config) {
this.configMap = (Map<String, CacheConfig>) config;
}
protected CacheConfig createDefaultConfig() {
return new CacheConfig();
}
@Override
public Cache getCache(String name) {
// 重写 cacheName 支持多参数
String[] array = StringUtils.delimitedListToStringArray(name, "#");
name = array[0];
Cache cache = instanceMap.get(name);
if (cache != null) {
return cache;
}
if (!dynamic) {
return cache;
}
CacheConfig config = configMap.get(name);
if (config == null) {
config = createDefaultConfig();
configMap.put(name, config);
}
if (ttl > 0) {
config.setTTL(ttl * 1000);
}
if (array.length > 1) {
config.setTTL(DurationStyle.detectAndParse(array[1]).toMillis());
}
if (array.length > 2) {
config.setMaxIdleTime(DurationStyle.detectAndParse(array[2]).toMillis());
}
if (array.length > 3) {
config.setMaxSize(Integer.parseInt(array[3]));
}
if (config.getMaxIdleTime() == 0 && config.getTTL() == 0 && config.getMaxSize() == 0) {
return createMap(name, config);
}
return createMapCache(name, config);
}
private Cache createMap(String name, CacheConfig config) {
RMap<Object, Object> map = RedisUtils.getClient().getMap(name);
Cache cache = new CaffeineCacheDecorator(new RedissonCache(map, allowNullValues));
if (transactionAware) {
cache = new TransactionAwareCacheDecorator(cache);
}
Cache oldCache = instanceMap.putIfAbsent(name, cache);
if (oldCache != null) {
cache = oldCache;
}
return cache;
}
private Cache createMapCache(String name, CacheConfig config) {
RMapCache<Object, Object> map = RedisUtils.getClient().getMapCache(name);
Cache cache = new CaffeineCacheDecorator(new RedissonCache(map, config, allowNullValues));
if (transactionAware) {
cache = new TransactionAwareCacheDecorator(cache);
}
Cache oldCache = instanceMap.putIfAbsent(name, cache);
if (oldCache != null) {
cache = oldCache;
} else {
map.setMaxSize(config.getMaxSize());
}
return cache;
}
@Override
public Collection<String> getCacheNames() {
return Collections.unmodifiableSet(configMap.keySet());
}
}

View File

@@ -0,0 +1,102 @@
package com.maibu.manager.factory;
import java.util.TimerTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fastbee.common.constant.Constants;
import com.fastbee.common.utils.LogUtils;
import com.fastbee.common.utils.ServletUtils;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.common.utils.ip.AddressUtils;
import com.fastbee.common.utils.ip.IpUtils;
import com.fastbee.common.utils.spring.SpringUtils;
import com.fastbee.system.domain.SysLogininfor;
import com.fastbee.system.domain.SysOperLog;
import com.fastbee.system.service.ISysLogininforService;
import com.fastbee.system.service.ISysOperLogService;
import eu.bitwalker.useragentutils.UserAgent;
/**
* 异步工厂(产生任务用)
*
* @author ruoyi
*/
public class AsyncFactory
{
private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user");
/**
* 记录登录信息
*
* @param username 用户名
* @param status 状态
* @param message 消息
* @param args 列表
* @return 任务task
*/
public static TimerTask recordLogininfor(final String username, final String status, final String message,
final Object... args)
{
final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
final String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
return new TimerTask()
{
@Override
public void run()
{
String address = AddressUtils.getRealAddressByIP(ip);
StringBuilder s = new StringBuilder();
s.append(LogUtils.getBlock(ip));
s.append(address);
s.append(LogUtils.getBlock(username));
s.append(LogUtils.getBlock(status));
s.append(LogUtils.getBlock(message));
// 打印信息到日志
sys_user_logger.info(s.toString(), args);
// 获取客户端操作系统
String os = userAgent.getOperatingSystem().getName();
// 获取客户端浏览器
String browser = userAgent.getBrowser().getName();
// 封装对象
SysLogininfor logininfor = new SysLogininfor();
logininfor.setUserName(username);
logininfor.setIpaddr(ip);
logininfor.setLoginLocation(address);
logininfor.setBrowser(browser);
logininfor.setOs(os);
logininfor.setMsg(message);
// 日志状态
if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER))
{
logininfor.setStatus(Constants.SUCCESS);
}
else if (Constants.LOGIN_FAIL.equals(status))
{
logininfor.setStatus(Constants.FAIL);
}
// 插入数据
SpringUtils.getBean(ISysLogininforService.class).insertLogininfor(logininfor);
}
};
}
/**
* 操作日志记录
*
* @param operLog 操作日志信息
* @return 任务task
*/
public static TimerTask recordOper(final SysOperLog operLog)
{
return new TimerTask()
{
@Override
public void run()
{
// 远程查询操作地点
operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp()));
SpringUtils.getBean(ISysOperLogService.class).insertOperlog(operLog);
}
};
}
}

View File

@@ -0,0 +1,136 @@
package com.maibu.mybatis;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.util.StringUtils;
import java.util.Collection;
/**
* 拓展 MyBatis Plus QueryWrapper 类,主要增加如下功能:
* <p>
* 1. 拼接条件的方法,增加 xxxIfPresent 方法,用于判断值不存在的时候,不要拼接到条件中。
*
* @param <T> 数据类型
*/
public class LambdaQueryWrapperX<T> extends LambdaQueryWrapper<T> {
public LambdaQueryWrapperX<T> likeIfPresent(SFunction<T, ?> column, String val) {
if (StringUtils.hasText(val)) {
return (LambdaQueryWrapperX<T>) super.like(column, val);
}
return this;
}
public LambdaQueryWrapperX<T> inIfPresent(SFunction<T, ?> column, Collection<?> values) {
if (!CollectionUtils.isEmpty(values)) {
return (LambdaQueryWrapperX<T>) super.in(column, values);
}
return this;
}
public LambdaQueryWrapperX<T> inIfPresent(SFunction<T, ?> column, Object... values) {
if (!ArrayUtil.isEmpty(values)) {
return (LambdaQueryWrapperX<T>) super.in(column, values);
}
return this;
}
public LambdaQueryWrapperX<T> eqIfPresent(SFunction<T, ?> column, Object val) {
if (ObjectUtil.isNotEmpty(val)) {
return (LambdaQueryWrapperX<T>) super.eq(column, val);
}
return this;
}
public LambdaQueryWrapperX<T> neIfPresent(SFunction<T, ?> column, Object val) {
if (ObjectUtil.isNotEmpty(val)) {
return (LambdaQueryWrapperX<T>) super.ne(column, val);
}
return this;
}
public LambdaQueryWrapperX<T> gtIfPresent(SFunction<T, ?> column, Object val) {
if (val != null) {
return (LambdaQueryWrapperX<T>) super.gt(column, val);
}
return this;
}
public LambdaQueryWrapperX<T> geIfPresent(SFunction<T, ?> column, Object val) {
if (val != null) {
return (LambdaQueryWrapperX<T>) super.ge(column, val);
}
return this;
}
public LambdaQueryWrapperX<T> ltIfPresent(SFunction<T, ?> column, Object val) {
if (val != null) {
return (LambdaQueryWrapperX<T>) super.lt(column, val);
}
return this;
}
public LambdaQueryWrapperX<T> leIfPresent(SFunction<T, ?> column, Object val) {
if (val != null) {
return (LambdaQueryWrapperX<T>) super.le(column, val);
}
return this;
}
public LambdaQueryWrapperX<T> betweenIfPresent(SFunction<T, ?> column, Object val1, Object val2) {
if (val1 != null && val2 != null) {
return (LambdaQueryWrapperX<T>) super.between(column, val1, val2);
}
if (val1 != null) {
return (LambdaQueryWrapperX<T>) ge(column, val1);
}
if (val2 != null) {
return (LambdaQueryWrapperX<T>) le(column, val2);
}
return this;
}
public LambdaQueryWrapperX<T> betweenIfPresent(SFunction<T, ?> column, Object[] values) {
Object val1 = ArrayUtils.get(values, 0);
Object val2 = ArrayUtils.get(values, 1);
return betweenIfPresent(column, val1, val2);
}
// ========== 重写父类方法,方便链式调用 ==========
@Override
public LambdaQueryWrapperX<T> eq(boolean condition, SFunction<T, ?> column, Object val) {
super.eq(condition, column, val);
return this;
}
@Override
public LambdaQueryWrapperX<T> eq(SFunction<T, ?> column, Object val) {
super.eq(column, val);
return this;
}
@Override
public LambdaQueryWrapperX<T> orderByDesc(SFunction<T, ?> column) {
super.orderByDesc(true, column);
return this;
}
@Override
public LambdaQueryWrapperX<T> last(String lastSql) {
super.last(lastSql);
return this;
}
@Override
public LambdaQueryWrapperX<T> in(SFunction<T, ?> column, Collection<?> coll) {
super.in(column, coll);
return this;
}
}

View File

@@ -0,0 +1,140 @@
package com.maibu.mybatis;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ArrayUtils;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import org.springframework.util.StringUtils;
import java.util.Collection;
/**
* 拓展 MyBatis Plus QueryWrapper 类,主要增加如下功能:
*
* 1. 拼接条件的方法,增加 xxxIfPresent 方法,用于判断值不存在的时候,不要拼接到条件中。
*
* @param <T> 数据类型
*/
public class QueryWrapperX<T> extends QueryWrapper<T> {
public QueryWrapperX<T> likeIfPresent(String column, String val) {
if (StringUtils.hasText(val)) {
return (QueryWrapperX<T>) super.like(column, val);
}
return this;
}
public QueryWrapperX<T> inIfPresent(String column, Collection<?> values) {
if (!CollectionUtils.isEmpty(values)) {
return (QueryWrapperX<T>) super.in(column, values);
}
return this;
}
public QueryWrapperX<T> inIfPresent(String column, Object... values) {
if (!ArrayUtils.isEmpty(values)) {
return (QueryWrapperX<T>) super.in(column, values);
}
return this;
}
public QueryWrapperX<T> eqIfPresent(String column, Object val) {
if (val != null) {
return (QueryWrapperX<T>) super.eq(column, val);
}
return this;
}
public QueryWrapperX<T> neIfPresent(String column, Object val) {
if (val != null) {
return (QueryWrapperX<T>) super.ne(column, val);
}
return this;
}
public QueryWrapperX<T> gtIfPresent(String column, Object val) {
if (val != null) {
return (QueryWrapperX<T>) super.gt(column, val);
}
return this;
}
public QueryWrapperX<T> geIfPresent(String column, Object val) {
if (val != null) {
return (QueryWrapperX<T>) super.ge(column, val);
}
return this;
}
public QueryWrapperX<T> ltIfPresent(String column, Object val) {
if (val != null) {
return (QueryWrapperX<T>) super.lt(column, val);
}
return this;
}
public QueryWrapperX<T> leIfPresent(String column, Object val) {
if (val != null) {
return (QueryWrapperX<T>) super.le(column, val);
}
return this;
}
public QueryWrapperX<T> betweenIfPresent(String column, Object val1, Object val2) {
if (val1 != null && val2 != null) {
return (QueryWrapperX<T>) super.between(column, val1, val2);
}
if (val1 != null) {
return (QueryWrapperX<T>) ge(column, val1);
}
if (val2 != null) {
return (QueryWrapperX<T>) le(column, val2);
}
return this;
}
public QueryWrapperX<T> betweenIfPresent(String column, Object[] values) {
if (values!= null && values.length != 0 && values[0] != null && values[1] != null) {
return (QueryWrapperX<T>) super.between(column, values[0], values[1]);
}
if (values!= null && values.length != 0 && values[0] != null) {
return (QueryWrapperX<T>) ge(column, values[0]);
}
if (values!= null && values.length != 0 && values[1] != null) {
return (QueryWrapperX<T>) le(column, values[1]);
}
return this;
}
// ========== 重写父类方法,方便链式调用 ==========
@Override
public QueryWrapperX<T> eq(boolean condition, String column, Object val) {
super.eq(condition, column, val);
return this;
}
@Override
public QueryWrapperX<T> eq(String column, Object val) {
super.eq(column, val);
return this;
}
@Override
public QueryWrapperX<T> orderByDesc(String column) {
super.orderByDesc(true, column);
return this;
}
@Override
public QueryWrapperX<T> last(String lastSql) {
super.last(lastSql);
return this;
}
@Override
public QueryWrapperX<T> in(String column, Collection<?> coll) {
super.in(column, coll);
return this;
}
}

View File

@@ -0,0 +1,112 @@
package com.maibu.mybatis.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.fastbee.common.core.domain.PageParam;
import com.fastbee.common.core.domain.PageResult;
import com.fastbee.framework.mybatis.utils.MyBatisUtils;
import org.apache.ibatis.annotations.Param;
import java.util.Collection;
import java.util.List;
/**
* 在 MyBatis Plus 的 BaseMapper 的基础上拓展,提供更多的能力
* <p>
* 为什么继承 MPJBaseMapper 接口?支持 MyBatis Plus 多表 Join 的能力。
*/
public interface BaseMapperX<T> extends BaseMapper<T> {
default PageResult<T> selectPage(PageParam pageParam, @Param("ew") Wrapper<T> queryWrapper) {
// MyBatis Plus 查询
IPage<T> mpPage = MyBatisUtils.buildPage(pageParam);
selectPage(mpPage, queryWrapper);
// 转换返回
return new PageResult<>(mpPage.getRecords(), mpPage.getTotal());
}
default T selectOne(String field, Object value) {
return selectOne(new QueryWrapper<T>().eq(field, value));
}
default T selectOne(SFunction<T, ?> field, Object value) {
return selectOne(new LambdaQueryWrapper<T>().eq(field, value));
}
default T selectOne(String field1, Object value1, String field2, Object value2) {
return selectOne(new QueryWrapper<T>().eq(field1, value1).eq(field2, value2));
}
default T selectOne(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2) {
return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2));
}
default Long selectCount() {
return selectCount(new QueryWrapper<T>());
}
default Long selectCount(String field, Object value) {
return selectCount(new QueryWrapper<T>().eq(field, value));
}
default Long selectCount(SFunction<T, ?> field, Object value) {
return selectCount(new LambdaQueryWrapper<T>().eq(field, value));
}
default List<T> selectList() {
return selectList(new QueryWrapper<>());
}
default List<T> selectList(String field, Object value) {
return selectList(new QueryWrapper<T>().eq(field, value));
}
default List<T> selectList(SFunction<T, ?> field, Object value) {
return selectList(new LambdaQueryWrapper<T>().eq(field, value));
}
default List<T> selectList(String field, Collection<?> values) {
return selectList(new QueryWrapper<T>().in(field, values));
}
default List<T> selectList(SFunction<T, ?> field, Collection<?> values) {
return selectList(new LambdaQueryWrapper<T>().in(field, values));
}
default List<T> selectList(SFunction<T, ?> leField, SFunction<T, ?> geField, Object value) {
return selectList(new LambdaQueryWrapper<T>().le(leField, value).ge(geField, value));
}
/**
* 批量插入,适合大量数据插入
*
* @param entities 实体们
*/
default void insertBatch(Collection<T> entities) {
Db.saveBatch(entities);
}
/**
* 批量插入,适合大量数据插入
*
* @param entities 实体们
* @param size 插入数量 Db.saveBatch 默认为 1000
*/
default void insertBatch(Collection<T> entities, int size) {
Db.saveBatch(entities, size);
}
default void updateBatch(T update) {
update(update, new QueryWrapper<>());
}
default void updateBatch(Collection<T> entities, int size) {
Db.updateBatchById(entities, size);
}
}

View File

@@ -0,0 +1,88 @@
package com.maibu.mybatis.utils;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fastbee.common.core.domain.PageParam;
import com.fastbee.common.core.domain.SortingField;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
/**
* MyBatis 工具类
*/
public class MyBatisUtils {
private static final String MYSQL_ESCAPE_CHARACTER = "`";
public static <T> Page<T> buildPage(PageParam pageParam) {
return buildPage(pageParam, null);
}
public static <T> Page<T> buildPage(PageParam pageParam, Collection<SortingField> sortingFields) {
// 页码 + 数量
Page<T> page = new Page<>(pageParam.getPageNo(), pageParam.getPageSize());
// 排序字段
if (!CollectionUtil.isEmpty(sortingFields)) {
page.addOrder(sortingFields.stream().map(sortingField -> SortingField.ORDER_ASC.equals(sortingField.getOrder()) ?
OrderItem.asc(sortingField.getField()) : OrderItem.desc(sortingField.getField()))
.collect(Collectors.toList()));
}
return page;
}
/**
* 将拦截器添加到链中
* 由于 MybatisPlusInterceptor 不支持添加拦截器,所以只能全量设置
*
* @param interceptor 链
* @param inner 拦截器
* @param index 位置
*/
public static void addInterceptor(MybatisPlusInterceptor interceptor, InnerInterceptor inner, int index) {
List<InnerInterceptor> inners = new ArrayList<>(interceptor.getInterceptors());
inners.add(index, inner);
interceptor.setInterceptors(inners);
}
/**
* 获得 Table 对应的表名
*
* 兼容 MySQL 转义表名 `t_xxx`
*
* @param table 表
* @return 去除转移字符后的表名
*/
public static String getTableName(Table table) {
String tableName = table.getName();
if (tableName.startsWith(MYSQL_ESCAPE_CHARACTER) && tableName.endsWith(MYSQL_ESCAPE_CHARACTER)) {
tableName = tableName.substring(1, tableName.length() - 1);
}
return tableName;
}
/**
* 构建 Column 对象
*
* @param tableName 表名
* @param tableAlias 别名
* @param column 字段名
* @return Column 对象
*/
public static Column buildColumn(String tableName, Alias tableAlias, String column) {
if (tableAlias != null) {
tableName = tableAlias.getName();
}
return new Column(tableName + StringPool.DOT + column);
}
}

View File

@@ -0,0 +1,28 @@
package com.maibu.security.context;
import org.springframework.security.core.Authentication;
/**
* 身份验证信息
*
* @author ruoyi
*/
public class AuthenticationContextHolder
{
private static final ThreadLocal<Authentication> contextHolder = new ThreadLocal<>();
public static Authentication getContext()
{
return contextHolder.get();
}
public static void setContext(Authentication context)
{
contextHolder.set(context);
}
public static void clearContext()
{
contextHolder.remove();
}
}

View File

@@ -0,0 +1,27 @@
package com.maibu.security.context;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import com.fastbee.common.core.text.Convert;
/**
* 权限信息
*
* @author ruoyi
*/
public class PermissionContextHolder
{
private static final String PERMISSION_CONTEXT_ATTRIBUTES = "PERMISSION_CONTEXT";
public static void setContext(String permission)
{
RequestContextHolder.currentRequestAttributes().setAttribute(PERMISSION_CONTEXT_ATTRIBUTES, permission,
RequestAttributes.SCOPE_REQUEST);
}
public static String getContext()
{
return Convert.toStr(RequestContextHolder.currentRequestAttributes().getAttribute(PERMISSION_CONTEXT_ATTRIBUTES,
RequestAttributes.SCOPE_REQUEST));
}
}

View File

@@ -0,0 +1,55 @@
package com.maibu.security.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import com.fastbee.common.core.domain.model.LoginUser;
import com.fastbee.common.utils.SecurityUtils;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.framework.web.service.TokenService;
/**
* token过滤器 验证token有效性
*
* @author ruoyi
*/
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
{
@Autowired
private TokenService tokenService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException
{
if ("OPTIONS".equals(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
return;
}
// 若为 WebSocket 路径,直接放行
if (request.getRequestURI().contains("/ws")) {
chain.doFilter(request, response);
return;
}
LoginUser loginUser = tokenService.getLoginUser(request);
if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication()))
{
tokenService.verifyToken(loginUser);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
chain.doFilter(request, response);
}
}

View File

@@ -0,0 +1,44 @@
package com.maibu.security.handle;
import java.io.IOException;
import java.io.Serializable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson2.JSON;
import com.fastbee.common.constant.HttpStatus;
import com.fastbee.common.core.domain.AjaxResult;
import com.fastbee.common.utils.ServletUtils;
import com.fastbee.common.utils.StringUtils;
/**
* 认证失败处理类 返回未授权
*
* @author ruoyi
*/
@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable
{
private static final long serialVersionUID = -8970718410437077606L;
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e)
throws IOException
{
// if (isAjaxRequest(request)){
int code = HttpStatus.UNAUTHORIZED;
String msg = StringUtils.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI());
ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg)));
// }else {
// response.sendRedirect("/oauth/login");
// }
}
public static boolean isAjaxRequest(HttpServletRequest request) {
String ajaxFlag = request.getHeader("X-Requested-With");
return ajaxFlag != null && "XMLHttpRequest".equals(ajaxFlag);
}
}

View File

@@ -0,0 +1,52 @@
package com.maibu.security.handle;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import com.alibaba.fastjson2.JSON;
import com.fastbee.common.constant.Constants;
import com.fastbee.common.core.domain.AjaxResult;
import com.fastbee.common.core.domain.model.LoginUser;
import com.fastbee.common.utils.ServletUtils;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.framework.manager.AsyncManager;
import com.fastbee.framework.manager.factory.AsyncFactory;
import com.fastbee.framework.web.service.TokenService;
/**
* 自定义退出处理类 返回成功
*
* @author ruoyi
*/
@Configuration
public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler
{
@Autowired
private TokenService tokenService;
/**
* 退出处理
*
* @return
*/
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException
{
LoginUser loginUser = tokenService.getLoginUser(request);
if (StringUtils.isNotNull(loginUser))
{
String userName = loginUser.getUsername();
// 删除用户缓存记录
tokenService.delLoginUser(loginUser.getToken());
// 记录用户退出日志
AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, "退出成功"));
}
ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.success("退出成功")));
}
}

View File

@@ -0,0 +1,75 @@
package com.maibu.utils;
import com.fastbee.common.utils.spring.SpringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.redisson.api.RMap;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import java.util.Set;
/**
* 缓存操作工具类 {@link }
*
* @author Michelle.Chung
* @date 2022/8/13
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@SuppressWarnings(value = {"unchecked"})
public class CacheUtils {
private static final CacheManager CACHE_MANAGER = SpringUtils.getBean(CacheManager.class);
/**
* 获取缓存组内所有的KEY
*
* @param cacheNames 缓存组名称
*/
public static Set<Object> keys(String cacheNames) {
RMap<Object, Object> rmap = (RMap<Object, Object>) CACHE_MANAGER.getCache(cacheNames).getNativeCache();
return rmap.keySet();
}
/**
* 获取缓存值
*
* @param cacheNames 缓存组名称
* @param key 缓存key
*/
public static <T> T get(String cacheNames, Object key) {
Cache.ValueWrapper wrapper = CACHE_MANAGER.getCache(cacheNames).get(key);
return wrapper != null ? (T) wrapper.get() : null;
}
/**
* 保存缓存值
*
* @param cacheNames 缓存组名称
* @param key 缓存key
* @param value 缓存值
*/
public static void put(String cacheNames, Object key, Object value) {
CACHE_MANAGER.getCache(cacheNames).put(key, value);
}
/**
* 删除缓存值
*
* @param cacheNames 缓存组名称
* @param key 缓存key
*/
public static void evict(String cacheNames, Object key) {
CACHE_MANAGER.getCache(cacheNames).evict(key);
}
/**
* 清空缓存值
*
* @param cacheNames 缓存组名称
*/
public static void clear(String cacheNames) {
CACHE_MANAGER.getCache(cacheNames).clear();
}
}

View File

@@ -0,0 +1,236 @@
package com.maibu.utils;
import com.fastbee.common.utils.spring.SpringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.redisson.api.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
/**
* 分布式队列工具
* 轻量级队列 重量级数据量 请使用 MQ
* 要求 redis 5.X 以上
*
* @author Lion Li
* @version 3.6.0 新增
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class QueueUtils {
private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class);
/**
* 获取客户端实例
*/
public static RedissonClient getClient() {
return CLIENT;
}
/**
* 添加普通队列数据
*
* @param queueName 队列名
* @param data 数据
*/
public static <T> boolean addQueueObject(String queueName, T data) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
return queue.offer(data);
}
/**
* 通用获取一个队列数据 没有数据返回 null(不支持延迟队列)
*
* @param queueName 队列名
*/
public static <T> T getQueueObject(String queueName) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
return queue.poll();
}
/**
* 通用删除队列数据(不支持延迟队列)
*/
public static <T> boolean removeQueueObject(String queueName, T data) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
return queue.remove(data);
}
/**
* 通用销毁队列 所有阻塞监听 报错(不支持延迟队列)
*/
public static <T> boolean destroyQueue(String queueName) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
return queue.delete();
}
/**
* 添加延迟队列数据 默认毫秒
*
* @param queueName 队列名
* @param data 数据
* @param time 延迟时间
*/
public static <T> void addDelayedQueueObject(String queueName, T data, long time) {
addDelayedQueueObject(queueName, data, time, TimeUnit.MILLISECONDS);
}
/**
* 添加延迟队列数据
*
* @param queueName 队列名
* @param data 数据
* @param time 延迟时间
* @param timeUnit 单位
*/
public static <T> void addDelayedQueueObject(String queueName, T data, long time, TimeUnit timeUnit) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);
delayedQueue.offer(data, time, timeUnit);
}
/**
* 获取一个延迟队列数据 没有数据返回 null
*
* @param queueName 队列名
*/
public static <T> T getDelayedQueueObject(String queueName) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);
return delayedQueue.poll();
}
/**
* 删除延迟队列数据
*/
public static <T> boolean removeDelayedQueueObject(String queueName, T data) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);
return delayedQueue.remove(data);
}
/**
* 销毁延迟队列 所有阻塞监听 报错
*/
public static <T> void destroyDelayedQueue(String queueName) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);
delayedQueue.destroy();
}
/**
* 添加优先队列数据
*
* @param queueName 队列名
* @param data 数据
*/
public static <T> boolean addPriorityQueueObject(String queueName, T data) {
RPriorityBlockingQueue<T> priorityBlockingQueue = CLIENT.getPriorityBlockingQueue(queueName);
return priorityBlockingQueue.offer(data);
}
/**
* 优先队列获取一个队列数据 没有数据返回 null(不支持延迟队列)
*
* @param queueName 队列名
*/
public static <T> T getPriorityQueueObject(String queueName) {
RPriorityBlockingQueue<T> queue = CLIENT.getPriorityBlockingQueue(queueName);
return queue.poll();
}
/**
* 优先队列删除队列数据(不支持延迟队列)
*/
public static <T> boolean removePriorityQueueObject(String queueName, T data) {
RPriorityBlockingQueue<T> queue = CLIENT.getPriorityBlockingQueue(queueName);
return queue.remove(data);
}
/**
* 优先队列销毁队列 所有阻塞监听 报错(不支持延迟队列)
*/
public static <T> boolean destroyPriorityQueue(String queueName) {
RPriorityBlockingQueue<T> queue = CLIENT.getPriorityBlockingQueue(queueName);
return queue.delete();
}
/**
* 尝试设置 有界队列 容量 用于限制数量
*
* @param queueName 队列名
* @param capacity 容量
*/
public static <T> boolean trySetBoundedQueueCapacity(String queueName, int capacity) {
RBoundedBlockingQueue<T> boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName);
return boundedBlockingQueue.trySetCapacity(capacity);
}
/**
* 尝试设置 有界队列 容量 用于限制数量
*
* @param queueName 队列名
* @param capacity 容量
* @param destroy 已存在是否销毁
*/
public static <T> boolean trySetBoundedQueueCapacity(String queueName, int capacity, boolean destroy) {
RBoundedBlockingQueue<T> boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName);
if (boundedBlockingQueue.isExists() && destroy) {
destroyQueue(queueName);
}
return boundedBlockingQueue.trySetCapacity(capacity);
}
/**
* 添加有界队列数据
*
* @param queueName 队列名
* @param data 数据
* @return 添加成功 true 已达到界限 false
*/
public static <T> boolean addBoundedQueueObject(String queueName, T data) {
RBoundedBlockingQueue<T> boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName);
return boundedBlockingQueue.offer(data);
}
/**
* 有界队列获取一个队列数据 没有数据返回 null(不支持延迟队列)
*
* @param queueName 队列名
*/
public static <T> T getBoundedQueueObject(String queueName) {
RBoundedBlockingQueue<T> queue = CLIENT.getBoundedBlockingQueue(queueName);
return queue.poll();
}
/**
* 有界队列删除队列数据(不支持延迟队列)
*/
public static <T> boolean removeBoundedQueueObject(String queueName, T data) {
RBoundedBlockingQueue<T> queue = CLIENT.getBoundedBlockingQueue(queueName);
return queue.remove(data);
}
/**
* 有界队列销毁队列 所有阻塞监听 报错(不支持延迟队列)
*/
public static <T> boolean destroyBoundedQueue(String queueName) {
RBoundedBlockingQueue<T> queue = CLIENT.getBoundedBlockingQueue(queueName);
return queue.delete();
}
/**
* 订阅阻塞队列(可订阅所有实现类 例如: 延迟 优先 有界 等)
*/
public static <T> void subscribeBlockingQueue(String queueName, Consumer<T> consumer, boolean isDelayed) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
if (isDelayed) {
// 订阅延迟队列
CLIENT.getDelayedQueue(queue);
}
queue.subscribeOnElements(consumer);
}
}

View File

@@ -0,0 +1,548 @@
package com.maibu.utils;
import com.fastbee.common.utils.spring.SpringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.redisson.api.*;
import java.time.Duration;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* redis 工具类
*
* @author Lion Li
* @version 3.1.0 新增
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@SuppressWarnings(value = {"unchecked", "rawtypes"})
public class RedisUtils {
private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class);
/**
* 限流
*
* @param key 限流key
* @param rateType 限流类型
* @param rate 速率
* @param rateInterval 速率间隔
* @return -1 表示失败
*/
public static long rateLimiter(String key, RateType rateType, int rate, int rateInterval) {
RRateLimiter rateLimiter = CLIENT.getRateLimiter(key);
rateLimiter.trySetRate(rateType, rate, rateInterval, RateIntervalUnit.SECONDS);
if (rateLimiter.tryAcquire()) {
return rateLimiter.availablePermits();
} else {
return -1L;
}
}
/**
* 获取客户端实例
*/
public static RedissonClient getClient() {
return CLIENT;
}
/**
* 发布通道消息
*
* @param channelKey 通道key
* @param msg 发送数据
* @param consumer 自定义处理
*/
public static <T> void publish(String channelKey, T msg, Consumer<T> consumer) {
RTopic topic = CLIENT.getTopic(channelKey);
topic.publish(msg);
consumer.accept(msg);
}
/**
* 发布消息到指定的频道
*
* @param channelKey 通道key
* @param msg 发送数据
*/
public static <T> void publish(String channelKey, T msg) {
RTopic topic = CLIENT.getTopic(channelKey);
topic.publish(msg);
}
/**
* 订阅通道接收消息
*
* @param channelKey 通道key
* @param clazz 消息类型
* @param consumer 自定义处理
*/
public static <T> void subscribe(String channelKey, Class<T> clazz, Consumer<T> consumer) {
RTopic topic = CLIENT.getTopic(channelKey);
topic.addListener(clazz, (channel, msg) -> consumer.accept(msg));
}
/**
* 缓存基本的对象,Integer、String、实体类等
*
* @param key 缓存的键值
* @param value 缓存的值
*/
public static <T> void setCacheObject(final String key, final T value) {
setCacheObject(key, value, false);
}
/**
* 缓存基本的对象,保留当前对象 TTL 有效期
*
* @param key 缓存的键值
* @param value 缓存的值
* @param isSaveTtl 是否保留TTL有效期(例如: set之前ttl剩余90 set之后还是为90)
* @since Redis 6.X 以上使用 setAndKeepTTL 兼容 5.X 方案
*/
public static <T> void setCacheObject(final String key, final T value, final boolean isSaveTtl) {
RBucket<T> bucket = CLIENT.getBucket(key);
if (isSaveTtl) {
try {
bucket.setAndKeepTTL(value);
} catch (Exception e) {
long timeToLive = bucket.remainTimeToLive();
if (timeToLive == -1) {
setCacheObject(key, value);
} else {
setCacheObject(key, value, Duration.ofMillis(timeToLive));
}
}
} else {
bucket.set(value);
}
}
/**
* 缓存基本的对象,Integer、String、实体类等
*
* @param key 缓存的键值
* @param value 缓存的值
* @param duration 时间
*/
public static <T> void setCacheObject(final String key, final T value, final Duration duration) {
RBatch batch = CLIENT.createBatch();
RBucketAsync<T> bucket = batch.getBucket(key);
bucket.setAsync(value);
bucket.expireAsync(duration);
batch.execute();
}
/**
* 如果不存在则设置 并返回 true 如果存在则返回 false
*
* @param key 缓存的键值
* @param value 缓存的值
* @return set成功或失败
*/
public static <T> boolean setObjectIfAbsent(final String key, final T value, final Duration duration) {
RBucket<T> bucket = CLIENT.getBucket(key);
return bucket.setIfAbsent(value, duration);
}
/**
* 如果存在则设置 并返回 true 如果存在则返回 false
*
* @param key 缓存的键值
* @param value 缓存的值
* @return set成功或失败
*/
public static <T> boolean setObjectIfExists(final String key, final T value, final Duration duration) {
RBucket<T> bucket = CLIENT.getBucket(key);
return bucket.setIfExists(value, duration);
}
/**
* 注册对象监听器
* <p>
* key 监听器需开启 `notify-keyspace-events` 等 redis 相关配置
*
* @param key 缓存的键值
* @param listener 监听器配置
*/
public static <T> void addObjectListener(final String key, final ObjectListener listener) {
RBucket<T> result = CLIENT.getBucket(key);
result.addListener(listener);
}
/**
* 设置有效时间
*
* @param key Redis键
* @param timeout 超时时间
* @return true=设置成功;false=设置失败
*/
public static boolean expire(final String key, final long timeout) {
return expire(key, Duration.ofSeconds(timeout));
}
/**
* 设置有效时间
*
* @param key Redis键
* @param duration 超时时间
* @return true=设置成功;false=设置失败
*/
public static boolean expire(final String key, final Duration duration) {
RBucket rBucket = CLIENT.getBucket(key);
return rBucket.expire(duration);
}
/**
* 获得缓存的基本对象。
*
* @param key 缓存键值
* @return 缓存键值对应的数据
*/
public static <T> T getCacheObject(final String key) {
RBucket<T> rBucket = CLIENT.getBucket(key);
return rBucket.get();
}
/**
* 获得key剩余存活时间
*
* @param key 缓存键值
* @return 剩余存活时间
*/
public static <T> long getTimeToLive(final String key) {
RBucket<T> rBucket = CLIENT.getBucket(key);
return rBucket.remainTimeToLive();
}
/**
* 删除单个对象
*
* @param key 缓存的键值
*/
public static boolean deleteObject(final String key) {
return CLIENT.getBucket(key).delete();
}
/**
* 删除集合对象
*
* @param collection 多个对象
*/
public static void deleteObject(final Collection collection) {
RBatch batch = CLIENT.createBatch();
collection.forEach(t -> {
batch.getBucket(t.toString()).deleteAsync();
});
batch.execute();
}
/**
* 检查缓存对象是否存在
*
* @param key 缓存的键值
*/
public static boolean isExistsObject(final String key) {
return CLIENT.getBucket(key).isExists();
}
/**
* 缓存List数据
*
* @param key 缓存的键值
* @param dataList 待缓存的List数据
* @return 缓存的对象
*/
public static <T> boolean setCacheList(final String key, final List<T> dataList) {
RList<T> rList = CLIENT.getList(key);
return rList.addAll(dataList);
}
/**
* 追加缓存List数据
*
* @param key 缓存的键值
* @param data 待缓存的数据
* @return 缓存的对象
*/
public static <T> boolean addCacheList(final String key, final T data) {
RList<T> rList = CLIENT.getList(key);
return rList.add(data);
}
/**
* 注册List监听器
* <p>
* key 监听器需开启 `notify-keyspace-events` 等 redis 相关配置
*
* @param key 缓存的键值
* @param listener 监听器配置
*/
public static <T> void addListListener(final String key, final ObjectListener listener) {
RList<T> rList = CLIENT.getList(key);
rList.addListener(listener);
}
/**
* 获得缓存的list对象
*
* @param key 缓存的键值
* @return 缓存键值对应的数据
*/
public static <T> List<T> getCacheList(final String key) {
RList<T> rList = CLIENT.getList(key);
return rList.readAll();
}
/**
* 获得缓存的list对象(范围)
*
* @param key 缓存的键值
* @param form 起始下标
* @param to 截止下标
* @return 缓存键值对应的数据
*/
public static <T> List<T> getCacheListRange(final String key, int form, int to) {
RList<T> rList = CLIENT.getList(key);
return rList.range(form, to);
}
/**
* 缓存Set
*
* @param key 缓存键值
* @param dataSet 缓存的数据
* @return 缓存数据的对象
*/
public static <T> boolean setCacheSet(final String key, final Set<T> dataSet) {
RSet<T> rSet = CLIENT.getSet(key);
return rSet.addAll(dataSet);
}
/**
* 追加缓存Set数据
*
* @param key 缓存的键值
* @param data 待缓存的数据
* @return 缓存的对象
*/
public static <T> boolean addCacheSet(final String key, final T data) {
RSet<T> rSet = CLIENT.getSet(key);
return rSet.add(data);
}
/**
* 注册Set监听器
* <p>
* key 监听器需开启 `notify-keyspace-events` 等 redis 相关配置
*
* @param key 缓存的键值
* @param listener 监听器配置
*/
public static <T> void addSetListener(final String key, final ObjectListener listener) {
RSet<T> rSet = CLIENT.getSet(key);
rSet.addListener(listener);
}
/**
* 获得缓存的set
*
* @param key 缓存的key
* @return set对象
*/
public static <T> Set<T> getCacheSet(final String key) {
RSet<T> rSet = CLIENT.getSet(key);
return rSet.readAll();
}
/**
* 缓存Map
*
* @param key 缓存的键值
* @param dataMap 缓存的数据
*/
public static <T> void setCacheMap(final String key, final Map<String, T> dataMap) {
if (dataMap != null) {
RMap<String, T> rMap = CLIENT.getMap(key);
rMap.putAll(dataMap);
}
}
/**
* 注册Map监听器
* <p>
* key 监听器需开启 `notify-keyspace-events` 等 redis 相关配置
*
* @param key 缓存的键值
* @param listener 监听器配置
*/
public static <T> void addMapListener(final String key, final ObjectListener listener) {
RMap<String, T> rMap = CLIENT.getMap(key);
rMap.addListener(listener);
}
/**
* 获得缓存的Map
*
* @param key 缓存的键值
* @return map对象
*/
public static <T> Map<String, T> getCacheMap(final String key) {
RMap<String, T> rMap = CLIENT.getMap(key);
return rMap.getAll(rMap.keySet());
}
/**
* 获得缓存Map的key列表
*
* @param key 缓存的键值
* @return key列表
*/
public static <T> Set<String> getCacheMapKeySet(final String key) {
RMap<String, T> rMap = CLIENT.getMap(key);
return rMap.keySet();
}
/**
* 往Hash中存入数据
*
* @param key Redis键
* @param hKey Hash键
* @param value 值
*/
public static <T> void setCacheMapValue(final String key, final String hKey, final T value) {
RMap<String, T> rMap = CLIENT.getMap(key);
rMap.put(hKey, value);
}
/**
* 获取Hash中的数据
*
* @param key Redis键
* @param hKey Hash键
* @return Hash中的对象
*/
public static <T> T getCacheMapValue(final String key, final String hKey) {
RMap<String, T> rMap = CLIENT.getMap(key);
return rMap.get(hKey);
}
/**
* 删除Hash中的数据
*
* @param key Redis键
* @param hKey Hash键
* @return Hash中的对象
*/
public static <T> T delCacheMapValue(final String key, final String hKey) {
RMap<String, T> rMap = CLIENT.getMap(key);
return rMap.remove(hKey);
}
/**
* 删除Hash中的数据
*
* @param key Redis键
* @param hKeys Hash键
*/
public static <T> void delMultiCacheMapValue(final String key, final Set<String> hKeys) {
RBatch batch = CLIENT.createBatch();
RMapAsync<String, T> rMap = batch.getMap(key);
for (String hKey : hKeys) {
rMap.removeAsync(hKey);
}
batch.execute();
}
/**
* 获取多个Hash中的数据
*
* @param key Redis键
* @param hKeys Hash键集合
* @return Hash对象集合
*/
public static <K, V> Map<K, V> getMultiCacheMapValue(final String key, final Set<K> hKeys) {
RMap<K, V> rMap = CLIENT.getMap(key);
return rMap.getAll(hKeys);
}
/**
* 设置原子值
*
* @param key Redis键
* @param value 值
*/
public static void setAtomicValue(String key, long value) {
RAtomicLong atomic = CLIENT.getAtomicLong(key);
atomic.set(value);
}
/**
* 获取原子值
*
* @param key Redis键
* @return 当前值
*/
public static long getAtomicValue(String key) {
RAtomicLong atomic = CLIENT.getAtomicLong(key);
return atomic.get();
}
/**
* 递增原子值
*
* @param key Redis键
* @return 当前值
*/
public static long incrAtomicValue(String key) {
RAtomicLong atomic = CLIENT.getAtomicLong(key);
return atomic.incrementAndGet();
}
/**
* 递减原子值
*
* @param key Redis键
* @return 当前值
*/
public static long decrAtomicValue(String key) {
RAtomicLong atomic = CLIENT.getAtomicLong(key);
return atomic.decrementAndGet();
}
/**
* 获得缓存的基本对象列表
*
* @param pattern 字符串前缀
* @return 对象列表
*/
public static Collection<String> keys(final String pattern) {
Stream<String> stream = CLIENT.getKeys().getKeysStreamByPattern(pattern);
return stream.collect(Collectors.toList());
}
/**
* 删除缓存的基本对象列表
*
* @param pattern 字符串前缀
*/
public static void deleteKeys(final String pattern) {
CLIENT.getKeys().deleteByPattern(pattern);
}
/**
* 检查redis中是否存在key
*
* @param key 键
*/
public static Boolean hasKey(String key) {
RKeys rKeys = CLIENT.getKeys();
return rKeys.countExists(key) > 0;
}
}

View File

@@ -0,0 +1,237 @@
package com.maibu.web.domain;
import java.net.UnknownHostException;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import com.maibu.utils.Arith;
import com.maibu.utils.ip.IpUtils;
import com.maibu.web.domain.server.*;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.CentralProcessor.TickType;
import oshi.hardware.GlobalMemory;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.software.os.FileSystem;
import oshi.software.os.OSFileStore;
import oshi.software.os.OperatingSystem;
import oshi.util.Util;
/**
* 服务器相关信息
*
* @author ruoyi
*/
public class Server
{
private static final int OSHI_WAIT_SECOND = 1000;
/**
* CPU相关信息
*/
private Cpu cpu = new Cpu();
/**
* 內存相关信息
*/
private Mem mem = new Mem();
/**
* JVM相关信息
*/
private Jvm jvm = new Jvm();
/**
* 服务器相关信息
*/
private Sys sys = new Sys();
/**
* 磁盘相关信息
*/
private List<SysFile> sysFiles = new LinkedList<SysFile>();
public Cpu getCpu()
{
return cpu;
}
public void setCpu(Cpu cpu)
{
this.cpu = cpu;
}
public Mem getMem()
{
return mem;
}
public void setMem(Mem mem)
{
this.mem = mem;
}
public Jvm getJvm()
{
return jvm;
}
public void setJvm(Jvm jvm)
{
this.jvm = jvm;
}
public Sys getSys()
{
return sys;
}
public void setSys(Sys sys)
{
this.sys = sys;
}
public List<SysFile> getSysFiles()
{
return sysFiles;
}
public void setSysFiles(List<SysFile> sysFiles)
{
this.sysFiles = sysFiles;
}
public void copyTo() throws Exception
{
SystemInfo si = new SystemInfo();
HardwareAbstractionLayer hal = si.getHardware();
setCpuInfo(hal.getProcessor());
setMemInfo(hal.getMemory());
setSysInfo();
setJvmInfo();
setSysFiles(si.getOperatingSystem());
}
/**
* 设置CPU信息
*/
private void setCpuInfo(CentralProcessor processor)
{
// CPU信息
long[] prevTicks = processor.getSystemCpuLoadTicks();
Util.sleep(OSHI_WAIT_SECOND);
long[] ticks = processor.getSystemCpuLoadTicks();
long nice = ticks[TickType.NICE.getIndex()] - prevTicks[TickType.NICE.getIndex()];
long irq = ticks[TickType.IRQ.getIndex()] - prevTicks[TickType.IRQ.getIndex()];
long softirq = ticks[TickType.SOFTIRQ.getIndex()] - prevTicks[TickType.SOFTIRQ.getIndex()];
long steal = ticks[TickType.STEAL.getIndex()] - prevTicks[TickType.STEAL.getIndex()];
long cSys = ticks[TickType.SYSTEM.getIndex()] - prevTicks[TickType.SYSTEM.getIndex()];
long user = ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()];
long iowait = ticks[TickType.IOWAIT.getIndex()] - prevTicks[TickType.IOWAIT.getIndex()];
long idle = ticks[TickType.IDLE.getIndex()] - prevTicks[TickType.IDLE.getIndex()];
long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal;
cpu.setCpuNum(processor.getLogicalProcessorCount());
cpu.setTotal(totalCpu);
cpu.setSys(cSys);
cpu.setUsed(user);
cpu.setWait(iowait);
cpu.setFree(idle);
}
/**
* 设置内存信息
*/
private void setMemInfo(GlobalMemory memory)
{
mem.setTotal(memory.getTotal());
mem.setUsed(memory.getTotal() - memory.getAvailable());
mem.setFree(memory.getAvailable());
}
/**
* 设置服务器信息
*/
private void setSysInfo()
{
Properties props = System.getProperties();
sys.setComputerName(IpUtils.getHostName());
sys.setComputerIp(IpUtils.getHostIp());
sys.setOsName(props.getProperty("os.name"));
sys.setOsArch(props.getProperty("os.arch"));
sys.setUserDir(props.getProperty("user.dir"));
}
/**
* 设置Java虚拟机
*/
private void setJvmInfo() throws UnknownHostException
{
Properties props = System.getProperties();
jvm.setTotal(Runtime.getRuntime().totalMemory());
jvm.setMax(Runtime.getRuntime().maxMemory());
jvm.setFree(Runtime.getRuntime().freeMemory());
jvm.setVersion(props.getProperty("java.version"));
jvm.setHome(props.getProperty("java.home"));
}
/**
* 设置磁盘信息
*/
private void setSysFiles(OperatingSystem os)
{
FileSystem fileSystem = os.getFileSystem();
List<OSFileStore> fsArray = fileSystem.getFileStores();
for (OSFileStore fs : fsArray)
{
long free = fs.getUsableSpace();
long total = fs.getTotalSpace();
long used = total - free;
SysFile sysFile = new SysFile();
sysFile.setDirName(fs.getMount());
sysFile.setSysTypeName(fs.getType());
sysFile.setTypeName(fs.getName());
sysFile.setTotal(convertFileSize(total));
sysFile.setFree(convertFileSize(free));
sysFile.setUsed(convertFileSize(used));
sysFile.setUsage(Arith.mul(Arith.div(used, total, 4), 100));
sysFiles.add(sysFile);
}
}
/**
* 字节转换
*
* @param size 字节大小
* @return 转换后值
*/
public String convertFileSize(long size)
{
long kb = 1024;
long mb = kb * 1024;
long gb = mb * 1024;
if (size >= gb)
{
return String.format("%.1f GB", (float) size / gb);
}
else if (size >= mb)
{
float f = (float) size / mb;
return String.format(f > 100 ? "%.0f MB" : "%.1f MB", f);
}
else if (size >= kb)
{
float f = (float) size / kb;
return String.format(f > 100 ? "%.0f KB" : "%.1f KB", f);
}
else
{
return String.format("%d B", size);
}
}
}

View File

@@ -0,0 +1,17 @@
package com.maibu.web.domain.dto;
import lombok.Data;
@Data
public class LoginResultDto {
private boolean success;
private String msg;
private boolean toRegister;
private String userName;
private String password;
private String nickName;
private Integer aiChatNum;
private int resultCode;
private boolean skipLogin = false; //跳过登录 冗余
}

View File

@@ -0,0 +1,102 @@
package com.maibu.web.domain.server;
import com.maibu.utils.Arith;
/**
* CPU相关信息
*
* @author ruoyi
*/
public class Cpu
{
/**
* 核心数
*/
private int cpuNum;
/**
* CPU总的使用率
*/
private double total;
/**
* CPU系统使用率
*/
private double sys;
/**
* CPU用户使用率
*/
private double used;
/**
* CPU当前等待率
*/
private double wait;
/**
* CPU当前空闲率
*/
private double free;
public int getCpuNum()
{
return cpuNum;
}
public void setCpuNum(int cpuNum)
{
this.cpuNum = cpuNum;
}
public double getTotal()
{
return Arith.round(Arith.mul(total, 100), 2);
}
public void setTotal(double total)
{
this.total = total;
}
public double getSys()
{
return Arith.round(Arith.mul(sys / total, 100), 2);
}
public void setSys(double sys)
{
this.sys = sys;
}
public double getUsed()
{
return Arith.round(Arith.mul(used / total, 100), 2);
}
public void setUsed(double used)
{
this.used = used;
}
public double getWait()
{
return Arith.round(Arith.mul(wait / total, 100), 2);
}
public void setWait(double wait)
{
this.wait = wait;
}
public double getFree()
{
return Arith.round(Arith.mul(free / total, 100), 2);
}
public void setFree(double free)
{
this.free = free;
}
}

View File

@@ -0,0 +1,131 @@
package com.maibu.web.domain.server;
import com.maibu.utils.Arith;
import com.maibu.utils.DateUtils;
import java.lang.management.ManagementFactory;
/**
* JVM相关信息
*
* @author ruoyi
*/
public class Jvm
{
/**
* 当前JVM占用的内存总数(M)
*/
private double total;
/**
* JVM最大可用内存总数(M)
*/
private double max;
/**
* JVM空闲内存(M)
*/
private double free;
/**
* JDK版本
*/
private String version;
/**
* JDK路径
*/
private String home;
public double getTotal()
{
return Arith.div(total, (1024 * 1024), 2);
}
public void setTotal(double total)
{
this.total = total;
}
public double getMax()
{
return Arith.div(max, (1024 * 1024), 2);
}
public void setMax(double max)
{
this.max = max;
}
public double getFree()
{
return Arith.div(free, (1024 * 1024), 2);
}
public void setFree(double free)
{
this.free = free;
}
public double getUsed()
{
return Arith.div(total - free, (1024 * 1024), 2);
}
public double getUsage()
{
return Arith.mul(Arith.div(total - free, total, 4), 100);
}
/**
* 获取JDK名称
*/
public String getName()
{
return ManagementFactory.getRuntimeMXBean().getVmName();
}
public String getVersion()
{
return version;
}
public void setVersion(String version)
{
this.version = version;
}
public String getHome()
{
return home;
}
public void setHome(String home)
{
this.home = home;
}
/**
* JDK启动时间
*/
public String getStartTime()
{
return DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, DateUtils.getServerStartDate());
}
/**
* JDK运行时间
*/
public String getRunTime()
{
return DateUtils.getDatePoor(DateUtils.getNowDate(), DateUtils.getServerStartDate());
}
/**
* 运行参数
*/
public String getInputArgs()
{
return ManagementFactory.getRuntimeMXBean().getInputArguments().toString();
}
}

View File

@@ -0,0 +1,62 @@
package com.maibu.web.domain.server;
import com.maibu.utils.Arith;
/**
* 內存相关信息
*
* @author ruoyi
*/
public class Mem
{
/**
* 内存总量
*/
private double total;
/**
* 已用内存
*/
private double used;
/**
* 剩余内存
*/
private double free;
public double getTotal()
{
return Arith.div(total, (1024 * 1024 * 1024), 2);
}
public void setTotal(long total)
{
this.total = total;
}
public double getUsed()
{
return Arith.div(used, (1024 * 1024 * 1024), 2);
}
public void setUsed(long used)
{
this.used = used;
}
public double getFree()
{
return Arith.div(free, (1024 * 1024 * 1024), 2);
}
public void setFree(long free)
{
this.free = free;
}
public double getUsage()
{
return Arith.mul(Arith.div(used, total, 4), 100);
}
}

View File

@@ -0,0 +1,84 @@
package com.maibu.web.domain.server;
/**
* 系统相关信息
*
* @author ruoyi
*/
public class Sys
{
/**
* 服务器名称
*/
private String computerName;
/**
* 服务器Ip
*/
private String computerIp;
/**
* 项目路径
*/
private String userDir;
/**
* 操作系统
*/
private String osName;
/**
* 系统架构
*/
private String osArch;
public String getComputerName()
{
return computerName;
}
public void setComputerName(String computerName)
{
this.computerName = computerName;
}
public String getComputerIp()
{
return computerIp;
}
public void setComputerIp(String computerIp)
{
this.computerIp = computerIp;
}
public String getUserDir()
{
return userDir;
}
public void setUserDir(String userDir)
{
this.userDir = userDir;
}
public String getOsName()
{
return osName;
}
public void setOsName(String osName)
{
this.osName = osName;
}
public String getOsArch()
{
return osArch;
}
public void setOsArch(String osArch)
{
this.osArch = osArch;
}
}

View File

@@ -0,0 +1,114 @@
package com.maibu.web.domain.server;
/**
* 系统文件相关信息
*
* @author ruoyi
*/
public class SysFile
{
/**
* 盘符路径
*/
private String dirName;
/**
* 盘符类型
*/
private String sysTypeName;
/**
* 文件类型
*/
private String typeName;
/**
* 总大小
*/
private String total;
/**
* 剩余大小
*/
private String free;
/**
* 已经使用量
*/
private String used;
/**
* 资源的使用率
*/
private double usage;
public String getDirName()
{
return dirName;
}
public void setDirName(String dirName)
{
this.dirName = dirName;
}
public String getSysTypeName()
{
return sysTypeName;
}
public void setSysTypeName(String sysTypeName)
{
this.sysTypeName = sysTypeName;
}
public String getTypeName()
{
return typeName;
}
public void setTypeName(String typeName)
{
this.typeName = typeName;
}
public String getTotal()
{
return total;
}
public void setTotal(String total)
{
this.total = total;
}
public String getFree()
{
return free;
}
public void setFree(String free)
{
this.free = free;
}
public String getUsed()
{
return used;
}
public void setUsed(String used)
{
this.used = used;
}
public double getUsage()
{
return usage;
}
public void setUsage(double usage)
{
this.usage = usage;
}
}

View File

@@ -0,0 +1,115 @@
package com.maibu.web.exception;
import javax.servlet.http.HttpServletRequest;
import com.maibu.constant.HttpStatus;
import com.maibu.core.domain.AjaxResult;
import com.maibu.exception.DemoModeException;
import com.maibu.exception.ServiceException;
import com.maibu.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 全局异常处理器
*
* @author ruoyi
*/
@RestControllerAdvice
public class GlobalExceptionHandler
{
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 权限校验异常
*/
@ExceptionHandler(AccessDeniedException.class)
public AjaxResult handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request)
{
String requestURI = request.getRequestURI();
log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage());
return AjaxResult.error(HttpStatus.FORBIDDEN, "没有权限,请联系管理员授权");
}
/**
* 请求方式不支持
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,
HttpServletRequest request)
{
String requestURI = request.getRequestURI();
log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());
return AjaxResult.error(e.getMessage());
}
/**
* 业务异常
*/
@ExceptionHandler(ServiceException.class)
public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request)
{
log.error(e.getMessage(), e);
Integer code = e.getCode();
return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage());
}
/**
* 拦截未知的运行时异常
*/
@ExceptionHandler(RuntimeException.class)
public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request)
{
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生未知异常.", requestURI, e);
return AjaxResult.error(e.getMessage());
}
/**
* 系统异常
*/
@ExceptionHandler(Exception.class)
public AjaxResult handleException(Exception e, HttpServletRequest request)
{
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生系统异常.", requestURI, e);
return AjaxResult.error(e.getMessage());
}
/**
* 自定义验证异常
*/
@ExceptionHandler(BindException.class)
public AjaxResult handleBindException(BindException e)
{
log.error(e.getMessage(), e);
String message = e.getAllErrors().get(0).getDefaultMessage();
return AjaxResult.error(message);
}
/**
* 自定义验证异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e)
{
log.error(e.getMessage(), e);
String message = e.getBindingResult().getFieldError().getDefaultMessage();
return AjaxResult.error(message);
}
/**
* 演示模式异常
*/
@ExceptionHandler(DemoModeException.class)
public AjaxResult handleDemoModeException(DemoModeException e)
{
return AjaxResult.error("演示模式,不允许操作");
}
}

View File

@@ -0,0 +1,168 @@
package com.maibu.web.service;
import java.util.Set;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import com.fastbee.common.core.domain.entity.SysRole;
import com.fastbee.common.core.domain.model.LoginUser;
import com.fastbee.common.utils.SecurityUtils;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.framework.security.context.PermissionContextHolder;
/**
* RuoYi首创 自定义权限实现,ss取自SpringSecurity首字母
*
* @author ruoyi
*/
@Service("ss")
public class PermissionService
{
/** 所有权限标识 */
private static final String ALL_PERMISSION = "*:*:*";
/** 管理员角色权限标识 */
private static final String SUPER_ADMIN = "admin";
private static final String ROLE_DELIMETER = ",";
private static final String PERMISSION_DELIMETER = ",";
/**
* 验证用户是否具备某权限
*
* @param permission 权限字符串
* @return 用户是否具备某权限
*/
public boolean hasPermi(String permission)
{
if (StringUtils.isEmpty(permission))
{
return false;
}
LoginUser loginUser = SecurityUtils.getLoginUser();
if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions()))
{
return false;
}
PermissionContextHolder.setContext(permission);
return hasPermissions(loginUser.getPermissions(), permission);
}
/**
* 验证用户是否不具备某权限,与 hasPermi逻辑相反
*
* @param permission 权限字符串
* @return 用户是否不具备某权限
*/
public boolean lacksPermi(String permission)
{
return hasPermi(permission) != true;
}
/**
* 验证用户是否具有以下任意一个权限
*
* @param permissions 以 PERMISSION_NAMES_DELIMETER 为分隔符的权限列表
* @return 用户是否具有以下任意一个权限
*/
public boolean hasAnyPermi(String permissions)
{
if (StringUtils.isEmpty(permissions))
{
return false;
}
LoginUser loginUser = SecurityUtils.getLoginUser();
if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions()))
{
return false;
}
PermissionContextHolder.setContext(permissions);
Set<String> authorities = loginUser.getPermissions();
for (String permission : permissions.split(PERMISSION_DELIMETER))
{
if (permission != null && hasPermissions(authorities, permission))
{
return true;
}
}
return false;
}
/**
* 判断用户是否拥有某个角色
*
* @param role 角色字符串
* @return 用户是否具备某角色
*/
public boolean hasRole(String role)
{
if (StringUtils.isEmpty(role))
{
return false;
}
LoginUser loginUser = SecurityUtils.getLoginUser();
if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles()))
{
return false;
}
for (SysRole sysRole : loginUser.getUser().getRoles())
{
String roleKey = sysRole.getRoleKey();
if (SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role)))
{
return true;
}
}
return false;
}
/**
* 验证用户是否不具备某角色,与 isRole逻辑相反。
*
* @param role 角色名称
* @return 用户是否不具备某角色
*/
public boolean lacksRole(String role)
{
return hasRole(role) != true;
}
/**
* 验证用户是否具有以下任意一个角色
*
* @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表
* @return 用户是否具有以下任意一个角色
*/
public boolean hasAnyRoles(String roles)
{
if (StringUtils.isEmpty(roles))
{
return false;
}
LoginUser loginUser = SecurityUtils.getLoginUser();
if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles()))
{
return false;
}
for (String role : roles.split(ROLE_DELIMETER))
{
if (hasRole(role))
{
return true;
}
}
return false;
}
/**
* 判断是否包含权限
*
* @param permissions 权限列表
* @param permission 权限字符串
* @return 用户是否具备某权限
*/
private boolean hasPermissions(Set<String> permissions, String permission)
{
return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission));
}
}

View File

@@ -0,0 +1,511 @@
package com.maibu.web.service;
import cn.hutool.core.util.ObjectUtil;
import com.maibu.ali.AliSmsService;
import com.maibu.constant.CacheConstants;
import com.maibu.constant.Constants;
import com.maibu.constant.HttpStatus;
import com.maibu.core.domain.entity.SysDept;
import com.maibu.core.domain.entity.SysUser;
import com.maibu.core.domain.model.LoginBody;
import com.maibu.core.domain.model.LoginUser;
import com.maibu.core.redis.RedisCache;
import com.maibu.enums.UserStatus;
import com.maibu.exception.ServiceException;
import com.maibu.exception.user.CaptchaException;
import com.maibu.exception.user.CaptchaExpireException;
import com.maibu.exception.user.UserPasswordNotMatchException;
import com.maibu.manager.AsyncManager;
import com.maibu.manager.factory.AsyncFactory;
import com.maibu.mapper.SysUserMapper;
import com.maibu.security.context.AuthenticationContextHolder;
import com.maibu.service.ISysConfigService;
import com.maibu.service.ISysDeptService;
import com.maibu.service.ISysUserService;
import com.maibu.utils.*;
import com.maibu.utils.ip.IpUtils;
import com.maibu.web.domain.dto.LoginResultDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* 登录校验方法
*
* @author ruoyi
*/
@Component
public class SysLoginService {
@Autowired
private TokenService tokenService;
@Resource
private AuthenticationManager authenticationManager;
@Autowired
private RedisCache redisCache;
@Autowired
private ISysUserService userService;
@Autowired
private ISysConfigService configService;
@Autowired
private UserDetailsServiceImpl userDetailsServiceImpl;
@Resource
private SysPasswordService passwordService;
@Resource
private ISysDeptService sysDeptService;
@Autowired
private SysUserMapper userMapper;
@Autowired
private AliSmsService aliSmsService;
private static final String redisLoginKey = "phone:login:time";
private static final String redisOpenIdLoginKey = "openId:login:time";
/**
* 登录验证
*
* @param username 用户名
* @param password 密码
* @param code 验证码
* @param uuid 唯一标识
* @return 结果
*/
public String login(String username, String password, String code, String uuid, Integer sourceType, String language) {
// boolean captchaEnabled = configService.selectCaptchaEnabled();
boolean captchaEnabled = false;
// 验证码开关
if (captchaEnabled) {
validateCaptcha(username, code, uuid);
}
// 用户验证
Authentication authentication = null;
try {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
AuthenticationContextHolder.setContext(authenticationToken);
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
authentication = authenticationManager.authenticate(authenticationToken);
} catch (Exception e) {
if (e instanceof BadCredentialsException) {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
throw new UserPasswordNotMatchException();
} else {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
throw new ServiceException(e.getMessage());
}
} finally {
AuthenticationContextHolder.clearContext();
}
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
// 设置端类型
loginUser.setSourceType(sourceType == null ? 1 : sourceType);
// 移动端、小程序限制终端用户登录
Long deptId = loginUser.getDeptId();
/// TODO限制租户登录
// if (null != sourceType && 1 == sourceType && null == deptId) {
// throw new ServiceException("web端只允许租户登录!");
// }
// if (!"admin".equals(loginUser.getUsername()) && null != sourceType) {
// Long deptId = loginUser.getDeptId();
// if (1 == sourceType && null == deptId) {
// throw new ServiceException("web端只允许租户登录!");
// }
// if (1 != sourceType && null != deptId) {
// throw new ServiceException("只允许终端用户登录!");
// }
// }
SysDept sysDept = sysDeptService.selectDeptById(deptId);
recordLoginInfo(loginUser.getUserId());
loginUser.setLanguage(language);
if (null != sysDept) {
loginUser.setDeptUserId(sysDept.getDeptUserId());
}
// 生成token
return loginUser.getUserId() + "====" + tokenService.createToken(loginUser);
}
/**
* 第三方验证后,调用登录方法
*
* @param username 用户名
* @param password 密码
* @return token
*/
public String socialLogin(String username, String password, String language) {
// 用户验证
Authentication authentication = null;
try {
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
authentication = authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(username, password));
} catch (Exception e) {
if (e instanceof BadCredentialsException) {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
throw new UserPasswordNotMatchException();
} else {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
throw new ServiceException(e.getMessage());
}
}
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
recordLoginInfo(loginUser.getUserId());
loginUser.setLanguage(language);
// 生成token
return tokenService.createToken(loginUser);
}
/**
* 三方跳转登录认证方法
*
* @param username 系统用户名
* @param encodePwd 系统用户密码
* @return
*/
public String redirectLogin(String username, String encodePwd, String language) {
// UserDetails userDetails=userDetailsServiceImpl.loadUserByUsername(username);
SysUser user = userService.selectUserByUserName(username);
if (StringUtils.isNull(user)) {
throw new ServiceException("登录用户:" + username + " 不存在");
} else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
throw new ServiceException("对不起,您的账号:" + username + " 已被删除");
} else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
throw new ServiceException("对不起,您的账号:" + username + " 已停用");
}
// 重写验证方法
passwordService.socialValidate(user, encodePwd);
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
UserDetails userDetails = userDetailsServiceImpl.createLoginUser(user);
LoginUser loginUser = (LoginUser) userDetails;
recordLoginInfo(loginUser.getUserId());
loginUser.setLanguage(language);
// 生成token
return tokenService.createToken(loginUser);
}
/**
* 组态分享页面生成token
*
* @param username 系统用户名
* @return
*/
public String shareUserLogin(String username, String language) {
SysUser user = userService.selectUserByUserName(username);
if (StringUtils.isNull(user)) {
throw new ServiceException("分享用户:" + username + " 不存在");
} else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
throw new ServiceException("对不起,您的分享账号:" + username + " 已被删除");
} else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
throw new ServiceException("对不起,您的分享账号:" + username + " 已停用");
}
// 重写验证方法
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
UserDetails userDetails = userDetailsServiceImpl.createLoginUser(user);
LoginUser loginUser = (LoginUser) userDetails;
recordLoginInfo(loginUser.getUserId());
loginUser.setLanguage(language);
loginUser.setNeverExpire(Boolean.TRUE);
// 生成token
return tokenService.createToken(loginUser);
}
/**
* 校验验证码
*
* @param username 用户名
* @param code 验证码
* @param uuid 唯一标识
* @return 结果
*/
public void validateCaptcha(String username, String code, String uuid) {
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
String captcha = redisCache.getCacheObject(verifyKey);
redisCache.deleteObject(verifyKey);
if (captcha == null) {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
throw new CaptchaExpireException();
}
if (!code.equalsIgnoreCase(captcha)) {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
throw new CaptchaException();
}
}
/**
* 记录登录信息
*
* @param userId 用户ID
*/
public void recordLoginInfo(Long userId) {
SysUser sysUser = userService.selectUserById(userId);
sysUser.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
sysUser.setLoginDate(DateUtils.getNowDate());
userService.updateUserProfile(sysUser);
}
public String ssoLogin(String username, String password, String language) {
SysUser user = userService.selectUserByUserName(username);
if (StringUtils.isNull(user)) {
throw new ServiceException("登录用户:" + username + " 不存在");
} else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
throw new ServiceException("对不起,您的账号:" + username + " 已被删除");
} else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
throw new ServiceException("对不起,您的账号:" + username + " 已停用");
}
// 重写验证方法
passwordService.ssoValidate(user, password);
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
UserDetails userDetails = userDetailsServiceImpl.createLoginUser(user);
LoginUser loginUser = (LoginUser) userDetails;
recordLoginInfo(loginUser.getUserId());
loginUser.setLanguage(language);
// 生成token
return tokenService.createToken(loginUser);
}
/**
* 微信服务号登录 未注册的需要跳转手机验证码登录
*/
public synchronized LoginResultDto threePlatformOpenIdVerify(LoginBody loginBody) {
String openId = loginBody.getOpenId();
LoginResultDto dto = new LoginResultDto();
if (StringUtils.isEmpty(openId)) {
dto.setSuccess(false);
dto.setMsg("openId为空");
dto.setResultCode(HttpStatus.ERROR);
return dto;
}
//重复验证怎么跳过 怎么判断手机号刚登录
String key = redisOpenIdLoginKey + openId;
Long lastLoginTime = redisCache.getCacheObject(key);
if (lastLoginTime == null) {
SysUser sysUser = userMapper.selectUserByOpenId(openId);
if (sysUser != null) {
String phoneNumber = sysUser.getPhonenumber();
if (StringUtils.isEmpty(phoneNumber)) {
// dto.setSuccess(false);
// dto.setMsg("用户绑定的手机号为空");
// dto.setResultCode(HttpStatus.ERROR);
// return dto;
//todo 更新 假如有openId 没有手机号 也注册账号 没有手机号
dto.setSuccess(true);
dto.setMsg("用户绑定的手机号为空");
dto.setUserName(sysUser.getUserName());
dto.setPassword(StringUtils.isEmpty(sysUser.getDisplayPassword()) ? "12345" : sysUser.getDisplayPassword());
dto.setResultCode(HttpStatus.SUCCESS); //需要告诉前端跳转登录
dto.setAiChatNum(sysUser.getAiChatNum());
dto.setNickName(sysUser.getNickName());
redisCache.setCacheObject(key, System.currentTimeMillis(), 5, TimeUnit.SECONDS);
return dto;
} else {
dto.setSuccess(true);
dto.setUserName(sysUser.getUserName());
dto.setPassword(StringUtils.isEmpty(sysUser.getDisplayPassword()) ? "12345" : sysUser.getDisplayPassword());
dto.setResultCode(HttpStatus.SUCCESS);
dto.setAiChatNum(sysUser.getAiChatNum());
dto.setNickName(sysUser.getNickName());
redisCache.setCacheObject(key, System.currentTimeMillis(), 5, TimeUnit.SECONDS);
return dto;
}
} else {
// dto.setSuccess(false);
// dto.setMsg("用户未注册");
// dto.setResultCode(300); //需要告诉前端跳转登录
//todo 更新 假如有openId 没有手机号 也注册账号 没有手机号 ai限制10条 2025-12-15
sysUser = verifySmsCodeAndCreate(null, openId, 10);
dto.setMsg("用户未注册");
dto.setSuccess(true);
dto.setUserName(sysUser.getUserName());
dto.setPassword(StringUtils.isEmpty(sysUser.getDisplayPassword()) ? "12345" : sysUser.getDisplayPassword());
dto.setResultCode(HttpStatus.SUCCESS);
dto.setAiChatNum(sysUser.getAiChatNum());
dto.setNickName(sysUser.getNickName());
redisCache.setCacheObject(key, System.currentTimeMillis(), 5, TimeUnit.SECONDS);
return dto;
}
} else {
dto.setSuccess(true);
dto.setMsg("重复验证,跳过登录!");
dto.setResultCode(HttpStatus.SUCCESS);
dto.setSkipLogin(true);
return dto;
}
}
/**
* 手机号登录
*
*/
public synchronized LoginResultDto phoneNumberLoginVerify(LoginBody loginBody) {
String phoneParam = loginBody.getPhonenumber();
String smsCode = loginBody.getSmsCode();
String openId = loginBody.getOpenId();
Long userId = loginBody.getUserId();
LoginResultDto dto = new LoginResultDto();
if (StringUtils.isEmpty(phoneParam)) {
dto.setSuccess(false);
dto.setMsg("手机号为空");
dto.setResultCode(HttpStatus.ERROR);
return dto;
}
if (StringUtils.isEmpty(smsCode)) {
dto.setSuccess(false);
dto.setMsg("短信验证码为空");
dto.setResultCode(HttpStatus.ERROR);
return dto;
}
//重复验证怎么跳过 怎么判断手机号刚登录
String key = redisLoginKey + phoneParam;
Long lastLoginTime = redisCache.getCacheObject(key);
if (lastLoginTime == null) {
boolean result = aliSmsService.verifySmsCode(phoneParam, smsCode);
if (result) {
SysUser sysUser = userMapper.selectUserByPhoneNumber(phoneParam);
if (sysUser == null) {
//更新到openId 如果有openId走更新 没有 看userId
if (!org.apache.commons.lang3.StringUtils.isEmpty(openId)) {
sysUser = userMapper.selectUserByOpenId(openId);
if (sysUser != null) {
sysUser.setAiChatNum(-1);
sysUser.setUpdateTime(new Date());
sysUser.setPhonenumber(phoneParam);
userMapper.updateUser(sysUser);
} else {
sysUser = verifySmsCodeAndCreate(phoneParam, openId, -1);
}
dto.setSuccess(true);
dto.setUserName(sysUser.getUserName());
dto.setPassword(sysUser.getDisplayPassword());
dto.setAiChatNum(sysUser.getAiChatNum());
//登录成功记录当前手机号 登录成功时间 5秒后自动删除
redisCache.setCacheObject(key, System.currentTimeMillis(), 5, TimeUnit.SECONDS);
return dto;
} else {
if (userId != null) {
sysUser = userMapper.selectUserById(userId);
if (sysUser != null) {
sysUser.setAiChatNum(-1);
sysUser.setUpdateTime(new Date());
sysUser.setPhonenumber(phoneParam);
userMapper.updateUser(sysUser);
} else {
sysUser = verifySmsCodeAndCreate(phoneParam, null, -1);
}
} else {
sysUser = verifySmsCodeAndCreate(phoneParam, null, -1);
}
dto.setSuccess(true);
dto.setUserName(sysUser.getUserName());
dto.setPassword(sysUser.getDisplayPassword());
dto.setAiChatNum(sysUser.getAiChatNum());
//登录成功记录当前手机号 登录成功时间 5秒后自动删除
redisCache.setCacheObject(key, System.currentTimeMillis(), 5, TimeUnit.SECONDS);
return dto;
}
} else {
//手机号已注册的情况判断openId 是否匹配
if (!org.apache.commons.lang3.StringUtils.isEmpty(openId)) {
if (!openId.equals(sysUser.getOpenId())) {
dto.setSuccess(false);
dto.setMsg("手机号已注册");
dto.setResultCode(HttpStatus.ERROR);
return dto;
}
} else {
//手机号已注册的情况判断userId 是否匹配
if (userId != null) {
if (!userId.equals(sysUser.getUserId())) {
dto.setSuccess(false);
dto.setMsg("手机号已注册");
dto.setResultCode(HttpStatus.ERROR);
return dto;
}
}
}
if (sysUser.getAiChatNum() != -1) {
sysUser.setAiChatNum(-1);
sysUser.setUpdateTime(new Date());
userMapper.updateUser(sysUser);
}
dto.setSuccess(true);
dto.setUserName(sysUser.getUserName());
dto.setPassword(sysUser.getDisplayPassword());
dto.setAiChatNum(sysUser.getAiChatNum());
//登录成功记录当前手机号 登录成功时间 5秒后自动删除
redisCache.setCacheObject(key, System.currentTimeMillis(), 5, TimeUnit.SECONDS);
return dto;
}
} else {
dto.setSuccess(false);
dto.setMsg("短信验证码验证失败");
dto.setResultCode(HttpStatus.ERROR);
return dto;
}
} else {
dto.setSuccess(true);
dto.setMsg("重复验证,跳过登录!");
dto.setResultCode(HttpStatus.SUCCESS);
dto.setSkipLogin(true);
return dto;
}
}
public SysUser verifySmsCodeAndCreate(String phoneNumber, String openId, int aiCount) {
// 创建账号 初始化账号名等
SysUser sysUser = new SysUser();
String randomName = generateUserName();
sysUser.setUserName(randomName);
sysUser.setNickName(randomName);
sysUser.setDisplayPassword("12345");
sysUser.setPassword(SecurityUtils.encryptPassword("12345")); //初始密码12345
sysUser.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
sysUser.setPhonenumber(phoneNumber);
sysUser.setOpenId(openId);
sysUser.setCreateBy("admin");
sysUser.setAiChatNum(aiCount);
// 默认部门默认迈步
sysUser.setDeptId(100L);
sysUser.setLoginDate(new Date());
userMapper.insertUser(sysUser);
// 关联角色如果没有角色且是迈步的默认角色(设备用户 roleid:3)
if (sysUser.getDeptId() != null && sysUser.getDeptId() == 100L && ObjectUtil.isEmpty(sysUser.getRoleIds())) {
userService.insertUserAuth(sysUser.getUserId(), new Long[]{3L});
}
AsyncManager.me().execute(AsyncFactory.recordLogininfor(randomName, Constants.REGISTER, MessageUtils.message("user.register.success")));
return sysUser;
}
public String generateUserName() {
// 获取当前时间戳后8位
String timestamp = String.valueOf(System.currentTimeMillis()).substring(5);
// 生成随机数确保不重复
String random = String.format("%03d", (int) (Math.random() * 1000));
// 组合成8位ID(取时间戳5位+随机数3位)
return timestamp.substring(0, 5) + random;
}
public void switchLoginPlatForm() {
}
}

View File

@@ -0,0 +1,159 @@
package com.maibu.web.service;
import java.util.concurrent.TimeUnit;
import com.maibu.constant.CacheConstants;
import com.maibu.constant.Constants;
import com.maibu.core.domain.entity.SysUser;
import com.maibu.core.redis.RedisCache;
import com.maibu.exception.user.UserPasswordNotMatchException;
import com.maibu.exception.user.UserPasswordRetryLimitExceedException;
import com.maibu.manager.AsyncManager;
import com.maibu.manager.factory.AsyncFactory;
import com.maibu.security.context.AuthenticationContextHolder;
import com.maibu.utils.MessageUtils;
import com.maibu.utils.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
/**
* 登录密码方法
*
* @author ruoyi
*/
@Component
public class SysPasswordService
{
@Autowired
private RedisCache redisCache;
@Value(value = "${user.password.maxRetryCount}")
private int maxRetryCount;
@Value(value = "${user.password.lockTime}")
private int lockTime;
/**
* 登录账户密码错误次数缓存键名
*
* @param username 用户名
* @return 缓存键key
*/
private String getCacheKey(String username)
{
return CacheConstants.PWD_ERR_CNT_KEY + username;
}
public void validate(SysUser user)
{
Authentication usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext();
String username = usernamePasswordAuthenticationToken.getName();
String password = usernamePasswordAuthenticationToken.getCredentials().toString();
Integer retryCount = redisCache.getCacheObject(getCacheKey(username));
if (retryCount == null)
{
retryCount = 0;
}
if (retryCount >= Integer.valueOf(maxRetryCount).intValue())
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL,
MessageUtils.message("user.password.retry.limit.exceed", maxRetryCount, lockTime)));
throw new UserPasswordRetryLimitExceedException(maxRetryCount, lockTime);
}
if (!matches(user, password))
{
retryCount = retryCount + 1;
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL,
MessageUtils.message("user.password.retry.limit.count", retryCount)));
redisCache.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES);
throw new UserPasswordNotMatchException();
}
else
{
clearLoginRecordCache(username);
}
}
public void socialValidate(SysUser user, String encodePwd)
{
String username = user.getUserName();
String password = user.getPassword();
Integer retryCount = redisCache.getCacheObject(getCacheKey(username));
if (retryCount == null)
{
retryCount = 0;
}
if (retryCount >= Integer.valueOf(maxRetryCount).intValue())
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL,
MessageUtils.message("user.password.retry.limit.exceed", maxRetryCount, lockTime)));
throw new UserPasswordRetryLimitExceedException(maxRetryCount, lockTime);
}
if(!password.equals(encodePwd)){
retryCount = retryCount + 1;
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL,
MessageUtils.message("user.password.retry.limit.count", retryCount)));
redisCache.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES);
throw new UserPasswordNotMatchException();
}
else
{
clearLoginRecordCache(username);
}
}
public boolean matches(SysUser user, String rawPassword)
{
return SecurityUtils.matchesPassword(rawPassword, user.getPassword());
}
public void clearLoginRecordCache(String loginName)
{
if (redisCache.hasKey(getCacheKey(loginName)))
{
redisCache.deleteObject(getCacheKey(loginName));
}
}
public void ssoValidate(SysUser user, String encodePwd) {
String username = user.getUserName();
String password = user.getPassword();
Integer retryCount = redisCache.getCacheObject(getCacheKey(username));
if (retryCount == null)
{
retryCount = 0;
}
if (retryCount >= Integer.valueOf(maxRetryCount).intValue())
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL,
MessageUtils.message("user.password.retry.limit.exceed", maxRetryCount, lockTime)));
throw new UserPasswordRetryLimitExceedException(maxRetryCount, lockTime);
}
if (!matches(user, encodePwd))
{
retryCount = retryCount + 1;
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL,
MessageUtils.message("user.password.retry.limit.count", retryCount)));
redisCache.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES);
throw new UserPasswordNotMatchException();
}
else
{
clearLoginRecordCache(username);
}
}
}

View File

@@ -0,0 +1,80 @@
package com.maibu.web.service;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.maibu.core.domain.entity.SysRole;
import com.maibu.core.domain.entity.SysUser;
import com.maibu.service.ISysMenuService;
import com.maibu.service.ISysRoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 用户权限处理
*
* @author ruoyi
*/
@Component
public class SysPermissionService
{
@Autowired
private ISysRoleService roleService;
@Autowired
private ISysMenuService menuService;
/**
* 获取角色数据权限
*
* @param user 用户信息
* @return 角色权限信息
*/
public Set<String> getRolePermission(SysUser user)
{
Set<String> roles = new HashSet<String>();
// 管理员拥有所有权限
if (user.isAdmin())
{
roles.add("admin");
}
else
{
roles.addAll(roleService.selectRolePermissionByUserId(user.getUserId()));
}
return roles;
}
/**
* 获取菜单数据权限
*
* @param user 用户信息
* @return 菜单权限信息
*/
public Set<String> getMenuPermission(SysUser user)
{
Set<String> perms = new HashSet<String>();
// 管理员拥有所有权限
if (user.isAdmin()) {
perms.add("*:*:*");
} else {
List<SysRole> roles = user.getRoles();
if (!roles.isEmpty() && roles.size() > 1)
{
// 多角色设置permissions属性,以便数据权限匹配权限
for (SysRole role : roles)
{
Set<String> rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId());
role.setPermissions(rolePerms);
perms.addAll(rolePerms);
}
}
else
{
perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId()));
}
}
return perms;
}
}

View File

@@ -0,0 +1,135 @@
package com.maibu.web.service;
import cn.hutool.core.util.ObjectUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.fastbee.common.constant.CacheConstants;
import com.fastbee.common.constant.Constants;
import com.fastbee.common.constant.UserConstants;
import com.fastbee.common.core.domain.entity.SysUser;
import com.fastbee.common.core.domain.model.RegisterBody;
import com.fastbee.common.core.redis.RedisCache;
import com.fastbee.common.exception.user.CaptchaException;
import com.fastbee.common.exception.user.CaptchaExpireException;
import com.fastbee.common.utils.MessageUtils;
import com.fastbee.common.utils.SecurityUtils;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.framework.manager.AsyncManager;
import com.fastbee.framework.manager.factory.AsyncFactory;
import com.fastbee.system.service.ISysConfigService;
import com.fastbee.system.service.ISysUserService;
/**
* 注册校验方法
*
* @author ruoyi
*/
@Component
public class SysRegisterService
{
@Autowired
private ISysUserService userService;
@Autowired
private ISysConfigService configService;
@Autowired
private RedisCache redisCache;
/**
* 注册
*/
public String register(RegisterBody registerBody)
{
String msg = "", username = registerBody.getUsername(), password = registerBody.getPassword(),email=registerBody.getEmail(),phonenumber=registerBody.getPhonenumber();
SysUser sysUser = new SysUser();
sysUser.setUserName(username);
// 验证码开关
boolean captchaEnabled = configService.selectCaptchaEnabled();
if (captchaEnabled)
{
validateCaptcha(username, registerBody.getCode(), registerBody.getEmail());
}
if (StringUtils.isEmpty(username))
{
msg = "用户名不能为空";
}
else if (StringUtils.isEmpty(password))
{
msg = "用户密码不能为空";
}
else if (StringUtils.isEmpty(email))
{
msg = "邮箱不能为空";
}
else if (StringUtils.isEmpty(phonenumber))
{
msg = "手机号不能为空";
}
else if (username.length() < UserConstants.USERNAME_MIN_LENGTH
|| username.length() > UserConstants.USERNAME_MAX_LENGTH)
{
msg = "账户长度必须在2到20个字符之间";
}
else if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
|| password.length() > UserConstants.PASSWORD_MAX_LENGTH)
{
msg = "密码长度必须在5到20个字符之间";
}
else if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(sysUser)))
{
msg = "保存用户'" + username + "'失败,注册账号已存在";
}
else
{
sysUser.setNickName(username);
sysUser.setPassword(SecurityUtils.encryptPassword(password));
sysUser.setEmail(email);
sysUser.setPhonenumber(phonenumber);
if (sysUser.getDeptId() == null) {
// 默认部门默认迈步
sysUser.setDeptId(100L);
}
boolean regFlag = userService.registerUser(sysUser);
if (!regFlag)
{
msg = "注册失败,请联系系统管理人员";
}
else
{
// 关联角色如果没有角色且是迈步的默认角色(设备用户 roleid:3)
if (sysUser.getDeptId() != null&&sysUser.getDeptId()==100L&& ObjectUtil.isEmpty(sysUser.getRoleIds())){
userService.insertUserAuth(sysUser.getUserId(),new Long[]{3L});
}
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.register.success")));
}
}
return msg;
}
/**
* 校验验证码
*
* @param username 用户名
* @param code 验证码
* @param email 邮箱
* @return 结果
*/
public void validateCaptcha(String username, String code, String email)
{
String verifyKey = CacheConstants.REGISTER_EMAIL_CAPTCHA + StringUtils.nvl(email, "");
String captcha = redisCache.getCacheObject(verifyKey);
redisCache.deleteObject(verifyKey);
if (captcha == null)
{
throw new CaptchaExpireException();
}
if (!code.equalsIgnoreCase(captcha))
{
throw new CaptchaException();
}
}
}

View File

@@ -0,0 +1,349 @@
package com.maibu.web.service;
import com.fastbee.common.constant.CacheConstants;
import com.fastbee.common.constant.Constants;
import com.fastbee.common.core.domain.entity.SysUser;
import com.fastbee.common.core.domain.model.LoginUser;
import com.fastbee.common.core.redis.RedisCache;
import com.fastbee.common.exception.ServiceException;
import com.fastbee.common.utils.ServletUtils;
import com.fastbee.common.utils.StringUtils;
import com.fastbee.common.utils.ip.AddressUtils;
import com.fastbee.common.utils.ip.IpUtils;
import com.fastbee.common.utils.uuid.IdUtils;
import com.fastbee.system.domain.SysClient;
import com.fastbee.system.service.ISysClientService;
import eu.bitwalker.useragentutils.UserAgent;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* token验证处理
*
* @author ruoyi
*/
@Component
public class TokenService {
// 令牌自定义标识
@Value("${token.header}")
private String header;
// 令牌秘钥
@Value("${token.secret}")
private String secret;
// 令牌有效期(默认30分钟)
@Value("${token.expireTime}")
private int expireTime;
protected static final long MILLIS_SECOND = 1000;
protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L;
@Autowired
private RedisCache redisCache;
@Autowired
private ISysClientService sysClientService;
@Autowired
private UserDetailsServiceImpl userDetailsServiceImpl;
/**
* 获取用户身份信息
*
* @return 用户信息
*/
public LoginUser getLoginUser(HttpServletRequest request) {
// 获取请求携带的令牌
String token = getToken(request);
if (StringUtils.isNotEmpty(token)) {
try {
Claims claims = parseToken(token);
// 解析对应的权限以及用户信息
String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
String userKey = getTokenKey(uuid);
LoginUser user = redisCache.getCacheObject(userKey);
return user;
} catch (Exception e) {
}
}
return null;
}
/**
* 获取用户身份信息
*
* @return 用户信息
*/
public LoginUser getLoginUserByToken(String token) {
if (StringUtils.isNotEmpty(token)) {
try {
Claims claims = parseToken(token);
// 解析对应的权限以及用户信息
String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
String userKey = getTokenKey(uuid);
LoginUser user = redisCache.getCacheObject(userKey);
return user;
} catch (Exception e) {
}
}
return null;
}
/**
* 根据用户id获取用户身份信息
* 由于多端登录,根据token获取的用户信息不一样,所以增加一个根据用户id获取用户信息的缓存key,以后多端需要获取用户最新信息就用这个方法吧
*
* @return 用户信息
*/
public LoginUser getLoginUserByUserId(Long userId) {
if (userId != null) {
try {
String userKey = getUserIdKey(userId);
return redisCache.getCacheObject(userKey);
} catch (Exception e) {
}
}
return null;
}
/**
* 设置用户身份信息
*/
public void setLoginUser(LoginUser loginUser) {
if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken())) {
refreshToken(loginUser);
}
}
/**
* 删除用户身份信息
*/
public void delLoginUser(String token) {
if (StringUtils.isNotEmpty(token)) {
String userKey = getTokenKey(token);
LoginUser loginUser = redisCache.getCacheObject(userKey);
if (loginUser != null) {
// 删除按端缓存
String userSourceKey = CacheConstants.LOGIN_USERID_SOURCE_KEY + loginUser.getUserId() + ":" + loginUser.getSourceType();
redisCache.deleteObject(userSourceKey);
}
redisCache.deleteObject(userKey);
}
}
/**
* 创建令牌
*
* @param loginUser 用户信息
* @return 令牌
*/
public String createToken(LoginUser loginUser) {
//todo 直接关闭页面的 先注释
// if(loginUser.getSourceType() == 1){
// // 检查同端是否已存在登录记录
// String userSourceKey = CacheConstants.LOGIN_USERID_SOURCE_KEY + loginUser.getUserId() + ":" + loginUser.getSourceType();
// LoginUser existingUser = redisCache.getCacheObject(userSourceKey);
// if (existingUser != null) {
// // 同端已存在登录,拒绝新登录
// throw new ServiceException("当前端已有登录会话,请先退出登录");
// }
// }
String token = IdUtils.fastUUID();
loginUser.setToken(token);
setUserAgent(loginUser);
refreshToken(loginUser);
Map<String, Object> claims = new HashMap<>();
claims.put(Constants.LOGIN_USER_KEY, token);
return createToken(claims);
}
public int addToken(SysUser user, SysClient sysClient) {
UserDetails userDetails = userDetailsServiceImpl.createLoginUser(user);
LoginUser loginUser = (LoginUser) userDetails;
sysClient.setToken(Constants.TOKEN_PREFIX + createToken(loginUser, sysClient.getClientSecret(), Math.toIntExact(sysClient.getTimeout())));
return sysClientService.insertSysClient(sysClient);
}
public int updateToken(SysUser user, SysClient sysClient) {
UserDetails userDetails = userDetailsServiceImpl.createLoginUser(user);
LoginUser loginUser = (LoginUser) userDetails;
sysClient.setToken(Constants.TOKEN_PREFIX + createToken(loginUser, sysClient.getClientSecret(), Math.toIntExact(sysClient.getTimeout())));
return sysClientService.updateSysClient(sysClient);
}
public String createToken(LoginUser loginUser, String secret, int expireTime) {
String token = IdUtils.fastUUID();
loginUser.setToken(token);
setUserAgent(loginUser);
refreshToken(loginUser, expireTime);
Map<String, Object> claims = new HashMap<>();
claims.put(Constants.LOGIN_USER_KEY, token);
return createToken(claims, secret);
}
/**
* 验证令牌有效期,相差不足20分钟,自动刷新缓存
*
* @param loginUser
* @return 令牌
*/
public void verifyToken(LoginUser loginUser) {
long expireTime = loginUser.getExpireTime();
long currentTime = System.currentTimeMillis();
if (expireTime - currentTime <= MILLIS_MINUTE_TEN) {
refreshToken(loginUser);
}
}
/**
* 刷新令牌有效期
*
* @param loginUser 登录信息
*/
public void refreshToken(LoginUser loginUser, int expireTime) {
loginUser.setLoginTime(System.currentTimeMillis());
loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
// 根据uuid将loginUser缓存
String userKey = getTokenKey(loginUser.getToken());
redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
// 使用token作为用户信息缓存key,多端不能同步最新信息,需要重新登录,因此添加一个使用用户id作为缓存key
String userIdKey = getUserIdKey(loginUser.getUserId());
redisCache.setCacheObject(userIdKey, loginUser, expireTime, TimeUnit.MINUTES);
}
public void refreshToken(LoginUser loginUser) {
loginUser.setLoginTime(System.currentTimeMillis());
loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
// 根据token缓存loginUser
String userKey = getTokenKey(loginUser.getToken());
// 根据userId缓存loginUser(最新登录信息)
String userIdKey = getUserIdKey(loginUser.getUserId());
// 根据userId和sourceType缓存loginUser(按端登录信息)
String userSourceKey = CacheConstants.LOGIN_USERID_SOURCE_KEY + loginUser.getUserId() + ":" + loginUser.getSourceType();
if (Boolean.TRUE.equals(loginUser.getNeverExpire())) {
redisCache.setCacheObject(userKey, loginUser);
redisCache.setCacheObject(userIdKey, loginUser);
redisCache.setCacheObject(userSourceKey, loginUser);
} else {
redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
redisCache.setCacheObject(userIdKey, loginUser, expireTime, TimeUnit.MINUTES);
redisCache.setCacheObject(userSourceKey, loginUser, expireTime, TimeUnit.MINUTES);
}
}
/**
* 设置用户代理信息
*
* @param loginUser 登录信息
*/
public void setUserAgent(LoginUser loginUser) {
UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
loginUser.setIpaddr(ip);
loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
loginUser.setBrowser(userAgent.getBrowser().getName());
loginUser.setOs(userAgent.getOperatingSystem().getName());
}
/**
* 从数据声明生成令牌
*
* @param claims 数据声明
* @return 令牌
*/
private String createToken(Map<String, Object> claims) {
String token = Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS512, secret).compact();
return token;
}
private String createToken(Map<String, Object> claims, String secret) {
String token = Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS512, secret).compact();
return token;
}
/**
* 从令牌中获取数据声明
*
* @param token 令牌
* @return 数据声明
*/
private Claims parseToken(String token) {
SysClient sysClient = new SysClient();
sysClient.setEnable("1");
sysClient.setToken(Constants.TOKEN_PREFIX + token);
List<SysClient> list = sysClientService.selectSysClientList(sysClient);
if (!list.isEmpty()) {
return Jwts.parser()
.setSigningKey(list.get(0).getClientSecret())
.parseClaimsJws(token)
.getBody();
} else {
return Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
}
}
/**
* 从令牌中获取用户名
*
* @param token 令牌
* @return 用户名
*/
public String getUsernameFromToken(String token) {
Claims claims = parseToken(token);
return claims.getSubject();
}
/**
* 获取请求token
*
* @param request
* @return token
*/
private String getToken(HttpServletRequest request) {
String token = request.getHeader(header);
if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) {
token = token.replace(Constants.TOKEN_PREFIX, "");
}
return token;
}
private String getTokenKey(String uuid) {
return CacheConstants.LOGIN_TOKEN_KEY + uuid;
}
private String getUserIdKey(Long userId) {
return CacheConstants.LOGIN_USERID_KEY + userId;
}
}

View File

@@ -0,0 +1,65 @@
package com.maibu.web.service;
import com.maibu.core.domain.entity.SysUser;
import com.maibu.core.domain.model.LoginUser;
import com.maibu.enums.UserStatus;
import com.maibu.exception.ServiceException;
import com.maibu.service.ISysUserService;
import com.maibu.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
/**
* 用户验证处理
*
* @author ruoyi
*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService
{
private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
@Autowired
private ISysUserService userService;
@Autowired
private SysPasswordService passwordService;
@Autowired
private SysPermissionService permissionService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
SysUser user = userService.selectUserByUserName(username);
if (StringUtils.isNull(user))
{
log.info("登录用户:{} 不存在.", username);
throw new ServiceException("登录用户:" + username + " 不存在");
}
else if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
{
log.info("登录用户:{} 已被删除.", username);
throw new ServiceException("对不起,您的账号:" + username + " 已被删除");
}
else if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
{
log.info("登录用户:{} 已被停用.", username);
throw new ServiceException("对不起,您的账号:" + username + " 已停用");
}
passwordService.validate(user);
return createLoginUser(user);
}
public UserDetails createLoginUser(SysUser user)
{
return new LoginUser(user.getUserId(), user.getDeptId(), user.getLanguage(), user, permissionService.getMenuPermission(user));
}
}

90
maibu-iotDA/pom.xml Normal file
View File

@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.maibu</groupId>
<artifactId>MiddlePlatform</artifactId>
<version>4.0.0</version>
</parent>
<artifactId>maibu-iotDA</artifactId>
<dependencies>
<dependency>
<groupId>com.maibu</groupId>
<artifactId>maibu-common</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.qpid</groupId>
<artifactId>qpid-jms-client</artifactId>
<version>0.50.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.20.0</version> <!-- 使用最新版 -->
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.20.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId>
<version>4.5.7</version>
</dependency>
<dependency>
<groupId>com.huaweicloud</groupId>
<artifactId>iot-device-sdk-java</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>com.huaweicloud.sdk</groupId>
<artifactId>huaweicloud-sdk-core</artifactId>
<version>3.1.9</version>
</dependency>
<dependency>
<groupId>com.huaweicloud.sdk</groupId>
<artifactId>huaweicloud-sdk-iotda</artifactId>
<version>3.1.9</version>
</dependency>
<dependency>
<groupId>com.maibu</groupId>
<artifactId>maibu-framework</artifactId>
</dependency>
<dependency>
<groupId>com.maibu</groupId>
<artifactId>maibu-netty-server</artifactId>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

View File

@@ -0,0 +1,139 @@
package com.maibu.controller;
import com.fastbee.common.core.domain.AjaxResult;
import com.fastbee.common.utils.json.JsonUtils;
import com.fastbee.dto.IoTDACommandDTO;
import com.fastbee.dto.IoTDARuleDTO;
import com.fastbee.service.IoTDADeviceService;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
@RestController
@RequestMapping("/iotDA/device")
public class IoTDADeviceController {
@Autowired
private IoTDADeviceService ioTDADeviceService;
@GetMapping("/list")
public AjaxResult list() {
return AjaxResult.success(ioTDADeviceService.list());
}
@GetMapping("/detail")
public AjaxResult detail(@RequestParam String deviceId) {
return AjaxResult.success(ioTDADeviceService.detail(deviceId));
}
@PostMapping("/command")
public AjaxResult command(@RequestBody IoTDACommandDTO dto) {
return AjaxResult.success(ioTDADeviceService.command(dto));
}
@GetMapping("/ruleList")
public AjaxResult ruleList() {
return AjaxResult.success(ioTDADeviceService.ruleList());
}
@PostMapping("/deleteRule")
public AjaxResult deleteRule(@RequestParam String ruleId) {
return AjaxResult.success(ioTDADeviceService.deleteRule(ruleId));
}
@PostMapping("/createRule")
public AjaxResult createRule(@RequestBody IoTDARuleDTO dto) {
return AjaxResult.success(ioTDADeviceService.createRule(dto));
}
@GetMapping("/ruleActionList")
public AjaxResult ruleActionList(@RequestParam String ruleId) {
return AjaxResult.success(ioTDADeviceService.ruleActionList(ruleId));
}
@PostMapping("/createRuleAction")
public AjaxResult createRuleAction(@RequestParam String ruleId) {
return AjaxResult.success(ioTDADeviceService.createRuleAction(ruleId));
}
@PostMapping("/deleteRuleAction")
public AjaxResult deleteRuleAction(@RequestParam String actionId) {
return AjaxResult.success(ioTDADeviceService.deleteRuleAction(actionId));
}
@PostMapping("/callback")
public AjaxResult callback(@RequestBody Object obj) {
System.out.println("收到触发消息:" + JsonUtils.toJsonString(obj));
return AjaxResult.success();
}
@PostMapping("/addDevice")
public AjaxResult addDevice(@RequestParam String nodeId) throws IOException {
return AjaxResult.success(ioTDADeviceService.addDevice(nodeId));
}
@PostMapping("/deleteDevice")
public AjaxResult deleteDevice(@RequestParam String deviceId) {
ioTDADeviceService.deleteDevice(deviceId);
return AjaxResult.success();
}
@PostMapping("/updateDevice")
public AjaxResult updateDevice(@RequestParam String deviceName, @RequestParam String deviceId) {
return AjaxResult.success(ioTDADeviceService.updateDevice(deviceName, deviceId));
}
@PostMapping("/freezeDevice")
public AjaxResult freezeDevice(@RequestParam String deviceId) {
return AjaxResult.success(ioTDADeviceService.freezeDevice(deviceId));
}
@PostMapping("/unfreezeDevice")
public AjaxResult unfreezeDevice(@RequestParam String deviceId) {
return AjaxResult.success(ioTDADeviceService.unfreezeDevice(deviceId));
}
@PostMapping("/createMessage")
public AjaxResult createMessage(@RequestParam String deviceId, @RequestParam String json) {
return AjaxResult.success(ioTDADeviceService.createMessage(deviceId, json));
}
@PostMapping("/listDeviceMessages")
public AjaxResult listDeviceMessages(@RequestParam String deviceId) {
return AjaxResult.success(ioTDADeviceService.listDeviceMessages(deviceId));
}
@PostMapping("/addDeviceGroup")
public AjaxResult addDeviceGroup(@RequestParam String name) {
return AjaxResult.success(ioTDADeviceService.addDeviceGroup(name));
}
// actionId: addDevice/removeDevice
@PostMapping("/manageDeviceGroup")
public AjaxResult manageDeviceGroup(@RequestParam String groupId, @RequestParam String actionId, @RequestParam String deviceId) {
return AjaxResult.success(ioTDADeviceService.manageDeviceGroup(groupId, actionId, deviceId));
}
@PostMapping("/deleteDeviceGroup")
public AjaxResult deleteDeviceGroup(@RequestParam String groupId) {
return AjaxResult.success(ioTDADeviceService.deleteDeviceGroup(groupId));
}
@PostMapping("/listDeviceGroups")
public AjaxResult listDeviceGroups() {
return AjaxResult.success(ioTDADeviceService.listDeviceGroups());
}
@PostMapping("/showDevicesInGroup")
public AjaxResult showDevicesInGroup(@RequestParam String groupId) {
return AjaxResult.success(ioTDADeviceService.showDevicesInGroup(groupId));
}
@PostMapping("/updateDeviceGroup")
public AjaxResult updateDeviceGroup(@RequestParam String groupId, @RequestParam String name, @RequestParam String description) {
return AjaxResult.success(ioTDADeviceService.updateDeviceGroup(groupId, name, description));
}
}

View File

@@ -0,0 +1,10 @@
package com.maibu.dto;
import lombok.Data;
@Data
public class IoTDACommandDTO {
private String commandName;
private Object param;
private String deviceId;
}

View File

@@ -0,0 +1,10 @@
package com.maibu.dto;
import lombok.Data;
@Data
public class IoTDARuleDTO {
private String resource; // device.property
private String event;
private String ruleName;//report
}

View File

@@ -0,0 +1,26 @@
package com.maibu.entity;
import lombok.Data;
@Data
public class IoTDADeviceRunningStatus {
private String deviceId;
private String voltage; //电压
private String latitude;//纬度
private String longitude;//经度
private Integer onlineStatus;
private String time;
private String battery;//电池电量
private String workingArea;//作业面积
}

View File

@@ -0,0 +1,23 @@
package com.maibu.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
@Data
@TableName("iotda_device_secret")
public class IoTDADeviceSecret implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.AUTO)
private Long id;
private String deviceId;
private String secretId;
}

View File

@@ -0,0 +1,144 @@
package com.maibu.init;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fastbee.entity.IoTDADeviceSecret;
import com.fastbee.mapper.IoTDADeviceSecretMapper;
import com.fastbee.memory.IotGlobalMemory;
import com.fastbee.utils.IoTDAAMQClient;
import com.fastbee.utils.IoTDAMqttClient;
import com.huaweicloud.sdk.iotda.v5.model.QueryDeviceSimplify;
import io.vertx.core.Vertx;
import io.vertx.core.VertxOptions;
import lombok.Getter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.List;
/**
* 系统初始化资源类
* 实现CommandLineRunner接口,在Spring Boot启动时自动执行
*/
@Getter
@Component
public class InitIotResource implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(InitIotResource.class);
@Resource
private IoTDAMqttClient ioTDAMqttClient;
@Resource
private IoTDAAMQClient client;
@Value("${ioTDA.serverIp}")
private String serverIp;
// @Value("${ioTDA.deviceId}")
// private String deviceId;
@Value("${ioTDA.secret}")
private String secret;
@Value("${ioTDA.serviceId}")
private String serviceId;
@Autowired
private IoTDADeviceSecretMapper secretMapper;
@Override
public void run(String... args) throws Exception {
logger.info("InitResource 开始初始化系统资源...");
// 初始化Vert.x
initResource();
logger.info("InitResource 系统资源初始化完成");
initDeviceConnectAndMesPush();
}
/**
* 初始化Vert.x
*/
private void initResource() {
try {
VertxOptions options = new VertxOptions()
.setWorkerPoolSize(20)
.setEventLoopPoolSize(10)
.setWarningExceptionTime(5000000000L);
IotGlobalMemory.vertx = Vertx.vertx(options);
logger.info("Vert.x 初始化成功");
IotGlobalMemory.ioTDAAMQClient = this.client;
IotGlobalMemory.ioTDAAMQClient.connect();
List<QueryDeviceSimplify> list = IotGlobalMemory.ioTDAAMQClient.listDevices();
if (!CollectionUtils.isEmpty(list)) {
list.forEach(x -> {
LambdaQueryWrapper<IoTDADeviceSecret> lqw = Wrappers.lambdaQuery();
lqw.eq(IoTDADeviceSecret::getDeviceId, x.getDeviceId());
IoTDADeviceSecret secret = secretMapper.selectOne(lqw);
IoTDAMqttClient c = new IoTDAMqttClient();
if (secret == null) {
c.secret = this.secret;
} else {
c.secret = secret.getSecretId();
}
c.serverIp = this.serverIp;
c.serviceId = this.serviceId;
c.deviceId = x.getDeviceId();
// try {
// c.connect(true);
// } catch (IOException e) {
// throw new RuntimeException(e);
// }
IotGlobalMemory.ioTDAMqttClientMap.put(x.getDeviceId(), c);
});
}
logger.info("iotDevice AMQ 初始化结束");
} catch (Exception e) {
logger.error("初始化失败: {}", e.getMessage(), e);
}
}
public void initDeviceConnectAndMesPush() {
IotGlobalMemory.vertx.setPeriodic(3000, timerId -> {
//todo 查看当前存在的所有设备循环推送状态?
try {
if (!CollectionUtils.isEmpty(IotGlobalMemory.ioTDAMqttClientMap.values())) {
IotGlobalMemory.ioTDAMqttClientMap.values().forEach(x -> {
if (x.status != 0) {
try {
x.judeConnect();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (x.status == 0) {
x.publishProperty();
}
});
}
// if (IotGlobalMemory.ioTDAAMQClient != null) {
// IotGlobalMemory.ioTDAAMQClient.deviceShadow(null);
// }
} catch (Exception e) {
logger.error("循环执行设备状态任务异常", e);
}
});
}
}

View File

@@ -0,0 +1,13 @@
package com.maibu.mapper;
import com.fastbee.entity.IoTDADeviceSecret;
import com.fastbee.framework.mybatis.mapper.BaseMapperX;
import org.springframework.stereotype.Repository;
/**
* 设备历史状态
*/
@Repository
public interface IoTDADeviceSecretMapper extends BaseMapperX<IoTDADeviceSecret> {
}

View File

@@ -0,0 +1,47 @@
package com.maibu.memory;
import com.fastbee.utils.IoTDAAMQClient;
import com.fastbee.utils.IoTDAMqttClient;
import io.vertx.core.Vertx;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
@Data
public class IotGlobalMemory {
private static final Logger logger = LoggerFactory.getLogger(IotGlobalMemory.class);
/**
* -- GETTER --
* 获取Vert.x实例
*/
public static Vertx vertx;
public static Map<String,IoTDAMqttClient> ioTDAMqttClientMap = new ConcurrentHashMap<>();
public static IoTDAAMQClient ioTDAAMQClient;
/**
* 关闭资源
*/
public void vertxClose() {
if (vertx != null) {
vertx.close(res -> {
if (res.succeeded()) {
logger.info("Vert.x 关闭成功");
} else {
logger.error("Vert.x 关闭失败: {}", res.cause().getMessage());
}
});
}
}
}

View File

@@ -0,0 +1,156 @@
package com.maibu.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fastbee.dto.IoTDACommandDTO;
import com.fastbee.dto.IoTDARuleDTO;
import com.fastbee.entity.IoTDADeviceSecret;
import com.fastbee.mapper.IoTDADeviceSecretMapper;
import com.fastbee.memory.IotGlobalMemory;
import com.fastbee.utils.IoTDAMqttClient;
import com.huaweicloud.sdk.iotda.v5.model.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.List;
@Service
public class IoTDADeviceService {
@Value("${ioTDA.serverIp}")
private String serverIp;
// @Value("${ioTDA.deviceId}")
// private String deviceId;
@Value("${ioTDA.secret}")
private String secret;
@Value("${ioTDA.serviceId}")
private String serviceId;
@Autowired
private IoTDADeviceSecretMapper secretMapper;
public List<QueryDeviceSimplify> list() {
return IotGlobalMemory.ioTDAAMQClient.listDevices();
}
public Object detail(String id) {
return IotGlobalMemory.ioTDAAMQClient.deviceShadow(id);
}
public String command(IoTDACommandDTO dto) {
return IotGlobalMemory.ioTDAAMQClient.command(dto.getDeviceId(), dto.getCommandName(), dto.getParam());
}
public List<RoutingRule> ruleList() {
return IotGlobalMemory.ioTDAAMQClient.ruleList();
}
public String createRule(IoTDARuleDTO dto) {
return IotGlobalMemory.ioTDAAMQClient.createRule(dto.getRuleName(), dto.getResource(), dto.getEvent());
}
public String deleteRule(String ruleId) {
return IotGlobalMemory.ioTDAAMQClient.deleteRule(ruleId);
}
public List<RoutingRuleAction> ruleActionList(String ruleId) {
return IotGlobalMemory.ioTDAAMQClient.ruleActionList(ruleId);
}
public String createRuleAction(String ruleId) {
return IotGlobalMemory.ioTDAAMQClient.createRuleAction(ruleId);
}
public String deleteRuleAction(String actionId) {
return IotGlobalMemory.ioTDAAMQClient.deleteRuleAction(actionId);
}
public String addDevice(String nodeId) throws IOException {
AddDeviceResponse response = IotGlobalMemory.ioTDAAMQClient.addDevice(nodeId);
String id = null;
if (response != null) {
id = response.getDeviceId();
if (!StringUtils.isEmpty(id)) {
LambdaQueryWrapper<IoTDADeviceSecret> lqw = Wrappers.lambdaQuery();
lqw.eq(IoTDADeviceSecret::getDeviceId, id);
IoTDADeviceSecret secret = secretMapper.selectOne(lqw);
if (secret == null) {
secret = new IoTDADeviceSecret();
secret.setDeviceId(id);
secret.setSecretId(response.getAuthInfo().getSecret());
secretMapper.insert(secret);
} else {
secret.setSecretId(response.getAuthInfo().getSecret());
secretMapper.updateById(secret);
}
IoTDAMqttClient c = new IoTDAMqttClient();
c.secret = response.getAuthInfo().getSecret();
c.serverIp = this.serverIp;
c.serviceId = this.serviceId;
c.deviceId = id;
// c.connect(true);
IotGlobalMemory.ioTDAMqttClientMap.put(id, c);
}
}
return id;
}
public void deleteDevice(String id) {
IotGlobalMemory.ioTDAAMQClient.deleteDevice(id);
IotGlobalMemory.ioTDAMqttClientMap.remove(id);
LambdaQueryWrapper<IoTDADeviceSecret> lqw = Wrappers.lambdaQuery();
lqw.eq(IoTDADeviceSecret::getDeviceId, id);
secretMapper.delete(lqw);
}
public String updateDevice(String deviceName, String deviceId) {
return IotGlobalMemory.ioTDAAMQClient.updateDevice(deviceName, deviceId);
}
public String freezeDevice(String deviceId) {
return IotGlobalMemory.ioTDAAMQClient.freezeDevice(deviceId);
}
public String unfreezeDevice(String deviceId) {
return IotGlobalMemory.ioTDAAMQClient.unfreezeDevice(deviceId);
}
public String createMessage(String deviceId, String json) {
return IotGlobalMemory.ioTDAAMQClient.createMessage(deviceId, json);
}
public List<DeviceMessage> listDeviceMessages(String deviceId) {
return IotGlobalMemory.ioTDAAMQClient.listDeviceMessages(deviceId);
}
public String addDeviceGroup(String name) {
return IotGlobalMemory.ioTDAAMQClient.addDeviceGroup(name);
}
public String manageDeviceGroup(String groupId, String actionId, String deviceId) {
return IotGlobalMemory.ioTDAAMQClient.manageDeviceGroup(groupId, actionId, deviceId);
}
public String deleteDeviceGroup(String groupId) {
return IotGlobalMemory.ioTDAAMQClient.deleteDeviceGroup(groupId);
}
public List<DeviceGroupResponseDTO> listDeviceGroups() {
return IotGlobalMemory.ioTDAAMQClient.listDeviceGroups();
}
public List<SimplifyDevice> showDevicesInGroup(String groupId) {
return IotGlobalMemory.ioTDAAMQClient.showDevicesInGroup(groupId);
}
public String updateDeviceGroup(String groupId, String name, String description) {
return IotGlobalMemory.ioTDAAMQClient.updateDeviceGroup(groupId, name, description);
}
}

View File

@@ -0,0 +1,409 @@
package com.maibu.utils;
import com.fastbee.common.utils.json.JsonUtils;
import com.huaweicloud.sdk.core.auth.BasicCredentials;
import com.huaweicloud.sdk.core.auth.ICredential;
import com.huaweicloud.sdk.core.exception.ConnectionException;
import com.huaweicloud.sdk.core.exception.RequestTimeoutException;
import com.huaweicloud.sdk.core.exception.ServiceResponseException;
import com.huaweicloud.sdk.core.region.Region;
import com.huaweicloud.sdk.iotda.v5.*;
import com.huaweicloud.sdk.iotda.v5.model.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@Component
public class IoTDAAMQClient {
@Value("${ioTDA.REGION_ID}")
private String REGION_ID;
@Value("${ioTDA.ENDPOINT}")
private String ENDPOINT;
@Value("${ioTDA.ak}")
private String ak;
@Value("${ioTDA.sk}")
private String sk;
@Value("${ioTDA.projectId}")
private String projectId;
// @Value("${ioTDA.deviceId}")
// private String deviceId;
@Value("${ioTDA.serviceId}")
private String serviceId;
@Value("${ioTDA.productId}")
private String productId;
public ICredential auth;
public static IoTDAClient client;
public void connect() {
// 创建认证
auth = new BasicCredentials()
.withAk(ak)
.withSk(sk)
// 标准版/企业版需要使用衍生算法,基础版请删除配置"withDerivedPredicate"
.withDerivedPredicate(BasicCredentials.DEFAULT_DERIVED_PREDICATE)
.withProjectId(projectId);
// 创建IoTDAClient实例并初始化
client = IoTDAClient.newBuilder()
.withCredential(auth)
// 标准版/企业版:需自行创建Region对象,基础版:请使用IoTDARegion的region对象,如"withRegion(IoTDARegion.CN_NORTH_4)"
.withRegion(new Region(REGION_ID, ENDPOINT))
// .withRegion(IoTDARegion.CN_NORTH_4)
// 配置是否忽略SSL证书校验, 默认不忽略
// .withHttpConfig(new HttpConfig().withIgnoreSSLVerification(true))
.build();
}
public List<QueryDeviceSimplify> listDevices() {
// 实例化请求对象
ListDevicesRequest request = new ListDevicesRequest();
try {
System.out.println("通过SDK下发获取设备列表");
// 调用查询设备列表接口
ListDevicesResponse response = client.listDevices(request);
System.out.println("获取设备列表方法返回结果: " + JsonUtils.toJsonString(response.getDevices()));
return response.getDevices();
} catch (ConnectionException | RequestTimeoutException | ServiceResponseException e) {
e.printStackTrace();
}
return Collections.emptyList();
}
public Object deviceShadow(String id) {
// 实例化请求对象
ShowDeviceShadowRequest request = new ShowDeviceShadowRequest();
try {
request.withDeviceId(id);
// request.withInstanceId()
List<DeviceShadowData> list = Collections.emptyList();
ShowDeviceShadowResponse response = client.showDeviceShadow(request);
if (response != null && !CollectionUtils.isEmpty(response.getShadow())) {
List<DeviceShadowData> data = response.getShadow();
list = data.stream().filter(x -> x.getServiceId().equals(serviceId)).collect(Collectors.toList());
}
if (!CollectionUtils.isEmpty(list) && list.get(0).getReported() != null) {
return list.get(0).getReported().getProperties();
}
} catch (ConnectionException | RequestTimeoutException | ServiceResponseException e) {
e.printStackTrace();
}
return null;
}
public String command(String deviceId, String commandName, Object params) {
CreateCommandRequest request = new CreateCommandRequest();
request.withDeviceId(deviceId);
DeviceCommandRequest body = new DeviceCommandRequest();
body.setServiceId(serviceId);
body.setCommandName(commandName);
body.setParas(params);
request.withBody(body);
try {
CreateCommandResponse response = client.createCommand(request);
return response.getCommandId();
} catch (ConnectionException | RequestTimeoutException | ServiceResponseException e) {
e.printStackTrace();
}
return null;
}
public String deleteRule(String ruleId) {
DeleteRoutingRuleRequest request = new DeleteRoutingRuleRequest();
request.withRuleId(ruleId);
try {
DeleteRoutingRuleResponse response = client.deleteRoutingRule(request);
System.out.println(response.toString());
} catch (ConnectionException | RequestTimeoutException | ServiceResponseException e) {
e.printStackTrace();
}
return null;
}
public String createRule(String ruleName, String resource, String event) {
CreateRoutingRuleRequest request = new CreateRoutingRuleRequest();
AddRuleReq body = new AddRuleReq();
body.withRuleName(ruleName);
RoutingRuleSubject subjectbody = new RoutingRuleSubject();
subjectbody.withResource(resource)
.withEvent(event);
body.withSubject(subjectbody);
request.withBody(body);
try {
CreateRoutingRuleResponse response = client.createRoutingRule(request);
if (response != null && !StringUtils.isEmpty(response.getRuleId())) {
createRuleAction(response.getRuleId());
updateRule(true, response.getRuleId());
return response.getRuleId();
}
} catch (ConnectionException | RequestTimeoutException | ServiceResponseException e) {
e.printStackTrace();
}
return null;
}
public Boolean updateRule(Boolean b, String ruleId) {
UpdateRoutingRuleRequest request = new UpdateRoutingRuleRequest();
request.withRuleId(ruleId);
UpdateRuleReq body = new UpdateRuleReq();
body.withActive(b);
request.withBody(body);
try {
UpdateRoutingRuleResponse response = client.updateRoutingRule(request);
return response.getActive();
} catch (ConnectionException | RequestTimeoutException | ServiceResponseException e) {
e.printStackTrace();
}
return null;
}
public List<RoutingRule> ruleList() {
ListRoutingRulesRequest request = new ListRoutingRulesRequest();
try {
ListRoutingRulesResponse response = client.listRoutingRules(request);
return response.getRules();
} catch (ConnectionException | RequestTimeoutException | ServiceResponseException e) {
e.printStackTrace();
}
return null;
}
public String deleteRuleAction(String actionId) {
DeleteRuleActionRequest request = new DeleteRuleActionRequest();
request.withActionId(actionId);
try {
DeleteRuleActionResponse response = client.deleteRuleAction(request);
System.out.println(response.toString());
} catch (ConnectionException | RequestTimeoutException | ServiceResponseException e) {
e.printStackTrace();
}
return null;
}
public String createRuleAction(String ruleId) {
CreateRuleActionRequest request = new CreateRuleActionRequest();
AddActionReq body = new AddActionReq();
HttpForwarding httpForwardingChannelDetail = new HttpForwarding();
httpForwardingChannelDetail.withUrl("https://serviceri.satabot.com/iotDA/device/callback");
ChannelDetail channelDetailbody = new ChannelDetail();
channelDetailbody.withHttpForwarding(httpForwardingChannelDetail);
body.withChannelDetail(channelDetailbody);
body.withChannel("HTTP_FORWARDING");
body.withRuleId(ruleId);
request.withBody(body);
try {
CreateRuleActionResponse response = client.createRuleAction(request);
System.out.println(response.toString());
} catch (ConnectionException | ServiceResponseException | RequestTimeoutException e) {
e.printStackTrace();
}
return null;
}
public List<RoutingRuleAction> ruleActionList(String ruleId) {
ListRuleActionsRequest request = new ListRuleActionsRequest();
request.withRuleId(ruleId);
try {
ListRuleActionsResponse response = client.listRuleActions(request);
return response.getActions();
} catch (ConnectionException | RequestTimeoutException | ServiceResponseException e) {
e.printStackTrace();
}
return null;
}
public AddDeviceResponse addDevice(String nodeId) {
AddDeviceRequest request = new AddDeviceRequest();
AddDevice body = new AddDevice();
body.withProductId(productId);
body.withNodeId(nodeId);
request.withBody(body);
try {
AddDeviceResponse response = client.addDevice(request);
return response;
} catch (ConnectionException | RequestTimeoutException | ServiceResponseException e) {
e.printStackTrace();
}
return null;
}
public String deleteDevice(String id) {
DeleteDeviceRequest request = new DeleteDeviceRequest();
request.withDeviceId(id);
try {
DeleteDeviceResponse response = client.deleteDevice(request);
System.out.println(response.toString());
} catch (ConnectionException | ServiceResponseException | RequestTimeoutException e) {
e.printStackTrace();
}
return null;
}
public String updateDevice(String deviceName, String deviceId) {
UpdateDeviceRequest request = new UpdateDeviceRequest();
request.withDeviceId(deviceId);
UpdateDevice body = new UpdateDevice();
body.withDeviceName(deviceName);
request.withBody(body);
try {
UpdateDeviceResponse response = client.updateDevice(request);
System.out.println(response.toString());
} catch (ConnectionException | ServiceResponseException | RequestTimeoutException e) {
e.printStackTrace();
}
return null;
}
public String freezeDevice(String deviceId) {
FreezeDeviceRequest request = new FreezeDeviceRequest();
request.withDeviceId(deviceId);
try {
FreezeDeviceResponse response = client.freezeDevice(request);
System.out.println(response.toString());
} catch (ConnectionException | ServiceResponseException | RequestTimeoutException e) {
e.printStackTrace();
}
return null;
}
public String unfreezeDevice(String deviceId) {
UnfreezeDeviceRequest request = new UnfreezeDeviceRequest();
request.withDeviceId(deviceId);
try {
UnfreezeDeviceResponse response = client.unfreezeDevice(request);
System.out.println(response.toString());
} catch (ConnectionException | ServiceResponseException | RequestTimeoutException e) {
e.printStackTrace();
}
return null;
}
public String createMessage(String deviceId, String json) {
CreateMessageRequest request = new CreateMessageRequest();
request.withDeviceId(deviceId);
DeviceMessageRequest body = new DeviceMessageRequest();
body.withMessage(json);
request.withBody(body);
try {
CreateMessageResponse response = client.createMessage(request);
System.out.println(response.toString());
} catch (ConnectionException | ServiceResponseException | RequestTimeoutException e) {
e.printStackTrace();
}
return null;
}
public List<DeviceMessage> listDeviceMessages(String deviceId) {
ListDeviceMessagesRequest request = new ListDeviceMessagesRequest();
request.withDeviceId(deviceId);
try {
ListDeviceMessagesResponse response = client.listDeviceMessages(request);
return response.getMessages();
} catch (ConnectionException | ServiceResponseException | RequestTimeoutException e) {
e.printStackTrace();
}
return null;
}
public String addDeviceGroup(String name) {
AddDeviceGroupRequest request = new AddDeviceGroupRequest();
AddDeviceGroupDTO body = new AddDeviceGroupDTO();
body.withName(name);
request.withBody(body);
try {
AddDeviceGroupResponse response = client.addDeviceGroup(request);
System.out.println(response.toString());
} catch (ConnectionException | ServiceResponseException | RequestTimeoutException e) {
e.printStackTrace();
}
return null;
}
public String manageDeviceGroup(String groupId, String actionId, String deviceId) {
CreateOrDeleteDeviceInGroupRequest request = new CreateOrDeleteDeviceInGroupRequest();
request.withGroupId(groupId);
request.withActionId(actionId);
request.withDeviceId(deviceId);
try {
CreateOrDeleteDeviceInGroupResponse response = client.createOrDeleteDeviceInGroup(request);
System.out.println(response.toString());
} catch (ConnectionException | ServiceResponseException | RequestTimeoutException e) {
e.printStackTrace();
}
return null;
}
public String deleteDeviceGroup(String groupId) {
DeleteDeviceGroupRequest request = new DeleteDeviceGroupRequest();
request.withGroupId(groupId);
try {
DeleteDeviceGroupResponse response = client.deleteDeviceGroup(request);
System.out.println(response.toString());
} catch (ConnectionException | ServiceResponseException | RequestTimeoutException e) {
e.printStackTrace();
}
return null;
}
public List<DeviceGroupResponseDTO> listDeviceGroups() {
ListDeviceGroupsRequest request = new ListDeviceGroupsRequest();
try {
ListDeviceGroupsResponse response = client.listDeviceGroups(request);
return response.getDeviceGroups();
} catch (ConnectionException | ServiceResponseException | RequestTimeoutException e) {
e.printStackTrace();
}
return null;
}
public List<SimplifyDevice> showDevicesInGroup(String groupId) {
ShowDevicesInGroupRequest request = new ShowDevicesInGroupRequest();
request.withGroupId(groupId);
try {
ShowDevicesInGroupResponse response = client.showDevicesInGroup(request);
return response.getDevices();
} catch (ConnectionException | ServiceResponseException | RequestTimeoutException e) {
e.printStackTrace();
}
return null;
}
public String updateDeviceGroup(String groupId, String name, String description) {
UpdateDeviceGroupRequest request = new UpdateDeviceGroupRequest();
request.withGroupId(groupId);
UpdateDeviceGroupDTO body = new UpdateDeviceGroupDTO();
body.withDescription(description);
body.withName(name);
request.withBody(body);
try {
UpdateDeviceGroupResponse response = client.updateDeviceGroup(request);
System.out.println(response.toString());
} catch (ConnectionException | ServiceResponseException | RequestTimeoutException e) {
e.printStackTrace();
}
return null;
}
}

View File

@@ -0,0 +1,213 @@
package com.maibu.utils;
import com.fastbee.common.NettyCacheKey;
import com.fastbee.common.core.redis.RedisCache;
import com.fastbee.common.utils.spring.SpringUtils;
import com.fastbee.dto.NettyDevice;
import com.fastbee.iot.domain.DeviceRunningStatusHistory;
import com.fastbee.iot.domain.DeviceStatusRecordDTO;
import com.fastbee.manager.DeviceSessionManager;
import com.huaweicloud.sdk.iot.device.IoTDevice;
import com.huaweicloud.sdk.iot.device.client.listener.CommandListener;
import com.huaweicloud.sdk.iot.device.client.requests.CommandRsp;
import com.huaweicloud.sdk.iot.device.client.requests.DeviceMessage;
import com.huaweicloud.sdk.iot.device.client.requests.ServiceProperty;
import com.huaweicloud.sdk.iot.device.transport.ActionListener;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@Component
public class IoTDAMqttClient {
private static final Logger logger = LoggerFactory.getLogger(IoTDAMqttClient.class);
public String serverIp;
// @Value("${ioTDA.deviceId}")
// private String deviceId;
public String secret;
public String serviceId;
private final SecureRandom random = new SecureRandom();
public IoTDevice device;
public String deviceId;
public int status = -1;
private static final RedisCache redisCache = SpringUtils.getBean(RedisCache.class);
private static final DeviceSessionManager deviceSessionManager = SpringUtils.getBean(DeviceSessionManager.class);
public void judeConnect() throws IOException {
String[] s = deviceId.split("_");
String nettyDeviceId = s[1];
NettyDevice nettyDevice = deviceSessionManager.getDevice(nettyDeviceId);
if (nettyDevice == null || nettyDevice.getOnlineStatus() != 1) {
if (device != null) {
device.getClient().close();
device = null;
}
} else {
this.connect(true);
}
}
public void connect(boolean isSSL) throws IOException {
// 获取证书路径:加载iot平台的ca证书,进行服务端校验,使用sdk默认的ca.jks即可。
// URL resource = IoTDAMqttClient.class.getClassLoader().getResource("ca.jks");
InputStream inputStream = IoTDAMqttClient.class.getClassLoader().getResourceAsStream("ca.jks");
if (inputStream == null) {
throw new FileNotFoundException("无法在 classpath 中找到 ca.jks");
}
// 2. 创建临时文件 (这会在服务器硬盘上生成一个真实的文件,例如 /tmp/iot_ca_xxx.jks)
// deleteOnExit() 保证程序退出时自动删除,避免垃圾堆积
File tempCertFile = File.createTempFile("iot_ca_", ".jks");
tempCertFile.deleteOnExit();
// 3. 将流内容复制到临时文件
Files.copy(inputStream, tempCertFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
// 关闭流
inputStream.close();
System.out.println("证书已解压至临时文件: " + tempCertFile.getAbsolutePath());
// 4. 【关键】使用这个临时文件的路径,而不是原始 resource 的路径
// 假设你的 SDK 初始化代码是这样写的,请替换掉原来的 new File(resource.getPath())
// 原错误代码:
// URL resource = IoTDAMqttClient.class.getClassLoader().getResource("ca.jks");
// File file = new File(resource.getPath());
// ✅ 修正后的代码:
File file = tempCertFile;
//例如在iot-device-demo文件 MessageSample.java中修改以下参数
String url;
if (isSSL) {
url = "ssl://" + serverIp + ":" + 8883; //mqtts连接
} else {
url = "tcp://" + serverIp + ":" + 1883; //mqtt连接
}
device = new IoTDevice(url,
deviceId, secret, file);
// 设置监听器接收下行命令
device.getClient().setCommandListener(new CommandListener() {
@Override
public void onCommand(String requestId, String serviceId, String commandName, Map<String, Object> paras) {
System.out.println("收到命令: serviceId=" + serviceId + ", commandName=" + commandName);
System.out.println("命令参数: " + paras.toString());
//todo
// 处理命令逻辑(示例:打印参数并响应)
System.out.println("正在处理命令...");
// 发送命令响应(必须调用)
device.getClient().respondCommand(requestId, new CommandRsp(0)); // 0表示执行成功
}
});
this.status = device.init();
}
/**
* 上报json数据,注意serviceId要与Profile中的定义对应
*/
public void publishMessage() {
//todo 模拟推送
String jsonMsg = "{\"services\":[{\"service_id\":\"BasicData\",\"properties\":{\"luminance\":%s},\"eventTime\":null}]}";
String upMsg = String.format(jsonMsg, random.nextInt(100));
DeviceMessage message = new DeviceMessage(upMsg);
try {
device.getClient().reportDeviceMessage(message, new ActionListener() {
@Override
public void onSuccess(Object context) {
logger.info("reportDeviceMessage ok");
}
@Override
public void onFailure(Object context, Throwable var2) {
logger.error("reportDeviceMessage fail: {}", String.valueOf(var2));
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 上报json数据,注意serviceId要与Profile中的定义对应
*/
public void publishProperty() {
Map<String, Object> json = new HashMap<>();
String[] s = deviceId.split("_");
String nettyDeviceId = s[1];
String key = NettyCacheKey.deviceRunningStatusKey + nettyDeviceId;
DeviceStatusRecordDTO recordDTO = redisCache.getCacheObject(key);
NettyDevice nettyDevice = deviceSessionManager.getDevice(nettyDeviceId);
if (nettyDevice == null || nettyDevice.getOnlineStatus() != 1) {
if (device != null) {
device.getClient().close();
device = null;
}
} else {
if (recordDTO != null && !CollectionUtils.isEmpty(recordDTO.getHistories())) {
DeviceRunningStatusHistory history = recordDTO.getHistories().get(recordDTO.getHistories().size() - 1);
//按照物模型设置属性
json.put("latitude", history.getLatitude());
json.put("longitude", history.getLongitude());
json.put("onlineStatus", nettyDevice.getOnlineStatus());
json.put("time", nettyDevice.getLatestStatus() == null ? null : nettyDevice.getLatestStatus().getTime());
json.put("battery", history.getBattery());
json.put("workingArea", history.getWorkingArea());
json.put("voltage", history.getVoltage());
json.put("leftMeasureSpeed", history.getLeftMeasureSpeed());
json.put("leftCurrent", history.getLeftCurrent());
json.put("rightMeasureSpeed", history.getRightMeasureSpeed());
json.put("rightCurrent", history.getRightCurrent());
json.put("chipTemp", history.getChipTemp());
}
ServiceProperty serviceProperty = new ServiceProperty();
serviceProperty.setProperties(json);
serviceProperty.setServiceId(serviceId);//serviceId要和物模型一致
device.getClient().reportProperties(Arrays.asList(serviceProperty), new ActionListener() {
@Override
public void onSuccess(Object context) {
logger.info("pubMessage success");
}
@Override
public void onFailure(Object context, Throwable var2) {
logger.error("reportProperties failed{}", var2.toString());
}
});
}
}
}

Binary file not shown.

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.maibu</groupId>
<artifactId>MiddlePlatform</artifactId>
<version>4.0.0</version>
</parent>
<artifactId>maibu-netty-server</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

View File

@@ -0,0 +1,17 @@
package com.maibu;
//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
public class Main {
public static void main(String[] args) {
//TIP Press <shortcut actionId="ShowIntentionActions"/> with your caret at the highlighted text
// to see how IntelliJ IDEA suggests fixing it.
System.out.printf("Hello and welcome!");
for (int i = 1; i <= 5; i++) {
//TIP Press <shortcut actionId="Debug"/> to start debugging your code. We have set one <icon src="AllIcons.Debugger.Db_set_breakpoint"/> breakpoint
// for you, but you can always add more by pressing <shortcut actionId="ToggleLineBreakpoint"/>.
System.out.println("i = " + i);
}
}
}

View File

@@ -0,0 +1,154 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.maibu</groupId>
<artifactId>maibu-service</artifactId>
<version>4.0.0</version>
</parent>
<artifactId>maibu-iot-service</artifactId>
<dependencies>
<!-- 通用工具-->
<dependency>
<groupId>com.maibu</groupId>
<artifactId>maibu-common</artifactId>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.6.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>${velocity.version}</version>
</dependency>
<dependency>
<groupId>com.maibu</groupId>
<artifactId>maibu-framework</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.6</version>
</dependency>
<!-- mqtt-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.5</version>
</dependency>
<!--AES-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<!-- 第三方登录模块-->
<dependency>
<groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId>
<version>${justAuth.version}</version>
</dependency>
<!-- 通用http接口模块-->
<dependency>
<groupId>com.dtflys.forest</groupId>
<artifactId>forest-spring-boot-starter</artifactId>
<version>${forest.version}</version>
</dependency>
<!-- TDengine连接 START-->
<dependency>
<groupId>com.taosdata.jdbc</groupId>
<artifactId>taos-jdbcdriver</artifactId>
<version>3.3.2</version>
</dependency>
<!-- TDengine连接 END-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-spring-boot-starter</artifactId>
<version>${liteflow.version}</version>
</dependency>
<!--数据库规则配置源插件-->
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-rule-sql</artifactId>
<version>${liteflow.version}</version>
</dependency>
<!--groovy脚本插件-->
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-script-groovy</artifactId>
<version>${liteflow.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>8.4.1.jre8</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.5.0</version>
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>19.3.0.0</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.200</version>
</dependency>
<dependency>
<groupId>com.dtflys.forest</groupId>
<artifactId>forest-spring-boot-starter</artifactId>
<version>1.5.36</version>
</dependency>
<!-- 海康加密认证SDK -->
<dependency>
<groupId>com.hikvision.ga</groupId>
<artifactId>artemis-http-client</artifactId>
<version>1.1.3</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

View File

@@ -0,0 +1,35 @@
package com.maibu.cache;
import com.fastbee.common.core.mq.DeviceStatusBo;
import com.fastbee.common.enums.DeviceStatus;
import com.fastbee.iot.domain.Device;
import java.util.List;
/**
* 设备缓存
* @author bill
*/
public interface IDeviceCache {
/**
* 更新设备状态
* @param dto dto
*/
public void updateDeviceStatusCache(DeviceStatusBo dto,Device device);
/**
* 获取设备在线总数
* @return 设备在线总数
*/
public long deviceOnlineTotal();
/**
* 批量更新redis缓存设备状态
* @param serialNumbers 设备列表
* @param status 状态
*/
void updateBatchDeviceStatusCache(List<String> serialNumbers, DeviceStatus status);
}

View File

@@ -0,0 +1,30 @@
package com.maibu.cache;
/**
* 固件版本缓存
* @author gsb
* @date 2022/10/25 11:34
*/
public interface IFirmwareCache {
/**
* 固件版本缓存
* @param firmwareId 固件ID
* @param urlName 升级URL
* @return
*/
public void setFirmwareCache(Long firmwareId,String urlName);
/**
* 获取固件版本信息
* @param firmwareId 固件ID
* @return 升级URL
*/
public String getFirmwareCache(Long firmwareId);
/**
* 移除固件版本
* @param firmwareId 固件id
*/
public void remove(Long firmwareId);
}

View File

@@ -0,0 +1,47 @@
package com.maibu.cache;
import com.fastbee.iot.domain.ModbusConfig;
import java.util.List;
import java.util.Map;
/**
* 产品对应modbus配置参数缓存
* @author gsb
* @date 2024/6/13 9:44
*/
public interface IModbusConfigCache {
/**
* 获取modbus参数 --返回 Map<String, ModbusConfig>
* @param productId
* @return
* <p>
* 返回key-value
* key: 物模型标识符
* value:ModbusConfig
* </p>
*/
Map<Integer, List<ModbusConfig>> getModbusConfigCacheByProductId(Long productId);
/**
* 缓存modbus参数-返回Map
* @param productId
* <p>
* 返回key-value
* key: 物模型标识符
* value:ModbusConfig
* </p>
*/
Map<Integer, List<ModbusConfig>> setModbusConfigCacheByProductId(Long productId);
/**
* 获取单个modbus参数缓存值
* @param productId 产品id
* @param identify 标识符
* @return ModbusConfig
*/
public List<ModbusConfig> getSingleModbusConfig(Long productId, String identify);
}

View File

@@ -0,0 +1,17 @@
package com.maibu.cache;
import java.util.Map;
public interface IOtaTaskCache {
void setOtaCache(String serialNumber, Map<String, String> hMap, long timeout);
void removeOtaCache(String serialNumber);
String getOtaCacheValue(String serialNumber, String hKey);
void setOtaCacheValue(String serialNumber, String hKey, String value, long timeout);
boolean checkOtaCacheExist(String serialNumber);
}

View File

@@ -0,0 +1,103 @@
package com.maibu.cache;
import com.fastbee.iot.model.ThingsModels.ThingsModelValueItem;
import java.util.List;
import java.util.Map;
/**
* 物模型缓存
* @author gsb
* @date 2024/6/12 15:57
*/
public interface ITSLCache {
/**
* 获取JSON物模型 -组装的方式是为了兼容前端数据结构
* @param productId 产品id
* @return
* <p>
* {
* "functions": [
* ],
* "events": [
* ],
* "properties": [
* {
* "datatype": {
* "max": 100,
* "min": 1,
* "step": 1,
* "type": "integer",
* "unit": "流明"
* },
* "id": "light",
* "isChart": 1,
* "isHistory": 1,
* "isMonitor": 1,
* "isReadonly": 1,
* "name": "光强",
* "order": 0,
* "regId": "light",
* "type": 1
* }
* ]
* }
* </p>
*
*/
String getCacheThingsModelByProductId(Long productId);
/**
* 缓存物模型
*
* @param productId 产品id
* @return <p>
* {\"functions\":[],\"events\":[],\"properties\":[{\"datatype\":{\"max\":100,\"min\":1
* ,\"step\":1,\"type\":\"integer\",\"unit\":\"流明\"},
* \"id\":\"light\",\"isChart\":1,\"isHistory\":1,\"isMonitor\":1,\"isReadonly\":1,
* \"name\":\"光强\",\"order\":0,\"regId\":\"light\",\"type\":1}]}
* </p>
*/
public String setCacheThingsModelByProductId(Long productId);
/**
* 获取map物模型 --返回 Map<String, PropertyDto>
* @param productId
* @return
* <p>
* 返回key-value
* key: 物模型标识符
* value:PropertyDto
* </p>
*/
Map<String, ThingsModelValueItem> getCacheThMapByProductId(Long productId);
/**
* 获取List集合物模型
*/
List<ThingsModelValueItem> getThingsModelList(Long productId);
/**
* 缓存物模型-返回Map
* @param productId
* <p>
* 返回key-value
* key: 物模型标识符
* value:PropertyDto
* </p>
*/
public Map<String, ThingsModelValueItem> setCacheThMapByProductId(Long productId);
/**
* 获取单个物模型缓存
* @param productId 产品id
* @param identify 标识符
* @return PropertyDto
*/
public ThingsModelValueItem getSingleThingModels(Long productId, String identify);
}

View File

@@ -0,0 +1,49 @@
package com.maibu.cache;
import com.fastbee.iot.model.ThingsModels.ValueItem;
import java.util.List;
/**
* @author gsb
* @date 2024/3/22 16:37
*/
public interface ITSLValueCache {
/**
* 获取Redis缓存的设备物模型值
*
* @param productId 产品ID
* @param deviceNumber 设备编号
* @return
*/
public List<ValueItem> getCacheDeviceStatus(Long productId, String deviceNumber);
/**
* 缓存设备物模型值到redis
*
* @return
*/
public List<ValueItem> addCacheDeviceStatus(Long productId, String serialNumber);
/**
* 获取单个物模型的值
* @param productId 产品id
* @param: deviceNumber 设备编号
* @param: identifier 物模型标识
* @return java.lang.String
*/
String getCacheIdentifierValue(Long productId, String serialNumber, String identifier);
/**
* 获取单个物模型的值
* @param productId 产品id
* @param: deviceNumber 设备编号
* @param: identifier 物模型标识
* @return java.lang.String
*/
ValueItem getCacheIdentifier(Long productId, String serialNumber, String identifier);
}

Some files were not shown because too many files have changed in this diff Show More