This commit is contained in:
2025-09-24 11:10:42 +08:00
parent a72b60be98
commit 05b923b1ac
348 changed files with 611 additions and 8472 deletions

3
.gitignore vendored
View File

@@ -16,7 +16,8 @@ out/
/ruoyi-common/target/ /ruoyi-common/target/
/ruoyi-system/target/ /ruoyi-system/target/
/ruoyi-quartz/target/ /ruoyi-quartz/target/
/ruoyi-generator/target/s /ruoyi-generator/target/
/ruoyi-framework/target/ /ruoyi-framework/target/
/ruoyi-admin/target/ /ruoyi-admin/target/
/erp_client_sb/target/ /erp_client_sb/target/
target /

2
.idea/misc.xml generated
View File

@@ -9,5 +9,5 @@
</list> </list>
</option> </option>
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="ms-21" project-jdk-type="JavaSDK" /> <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK" />
</project> </project>

381
.idea/workspace.xml generated
View File

@@ -5,11 +5,354 @@
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="71d19dd6-7472-4ebf-b309-b7afee3f99de" name="更改" comment="1"> <list default="true" id="71d19dd6-7472-4ebf-b309-b7afee3f99de" name="更改" comment="1">
<change beforePath="$PROJECT_DIR$/.gitignore" beforeDir="false" afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/misc.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/erp_client_sb/src/main/java/com/tashow/erp/service/impl/Alibaba1688ServiceImpl.java" beforeDir="false" afterPath="$PROJECT_DIR$/erp_client_sb/src/main/java/com/tashow/erp/service/impl/Alibaba1688ServiceImpl.java" afterDir="false" /> <change beforePath="$PROJECT_DIR$/data/erp-cache.db" beforeDir="false" afterPath="$PROJECT_DIR$/data/erp-cache.db" afterDir="false" />
<change beforePath="$PROJECT_DIR$/data/erp-cache.db-shm" beforeDir="false" afterPath="$PROJECT_DIR$/data/erp-cache.db-shm" afterDir="false" />
<change beforePath="$PROJECT_DIR$/data/erp-cache.db-wal" beforeDir="false" afterPath="$PROJECT_DIR$/data/erp-cache.db-wal" afterDir="false" />
<change beforePath="$PROJECT_DIR$/electron-vue-template/src/renderer/App.vue" beforeDir="false" afterPath="$PROJECT_DIR$/electron-vue-template/src/renderer/App.vue" afterDir="false" />
<change beforePath="$PROJECT_DIR$/electron-vue-template/src/renderer/components/amazon/AmazonDashboard.vue" beforeDir="false" afterPath="$PROJECT_DIR$/electron-vue-template/src/renderer/components/amazon/AmazonDashboard.vue" afterDir="false" />
<change beforePath="$PROJECT_DIR$/electron-vue-template/src/renderer/components/rakuten/RakutenDashboard.vue" beforeDir="false" afterPath="$PROJECT_DIR$/electron-vue-template/src/renderer/components/rakuten/RakutenDashboard.vue" afterDir="false" />
<change beforePath="$PROJECT_DIR$/electron-vue-template/src/renderer/components/zebra/ZebraDashboard.vue" beforeDir="false" afterPath="$PROJECT_DIR$/electron-vue-template/src/renderer/components/zebra/ZebraDashboard.vue" afterDir="false" />
<change beforePath="$PROJECT_DIR$/erp_client_sb/src/main/java/com/tashow/erp/controller/AuthController.java" beforeDir="false" afterPath="$PROJECT_DIR$/erp_client_sb/src/main/java/com/tashow/erp/controller/AuthController.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/erp_client_sb/src/main/java/com/tashow/erp/controller/RakutenController.java" beforeDir="false" afterPath="$PROJECT_DIR$/erp_client_sb/src/main/java/com/tashow/erp/controller/RakutenController.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/erp_client_sb/src/main/java/com/tashow/erp/model/RakutenProduct.java" beforeDir="false" afterPath="$PROJECT_DIR$/erp_client_sb/src/main/java/com/tashow/erp/model/RakutenProduct.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/erp_client_sb/src/main/java/com/tashow/erp/service/IAuthService.java" beforeDir="false" afterPath="$PROJECT_DIR$/erp_client_sb/src/main/java/com/tashow/erp/service/IAuthService.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/erp_client_sb/src/main/java/com/tashow/erp/test/SeleniumWithProfile.java" beforeDir="false" afterPath="$PROJECT_DIR$/erp_client_sb/src/main/java/com/tashow/erp/test/SeleniumWithProfile.java" afterDir="false" /> <change beforePath="$PROJECT_DIR$/erp_client_sb/src/main/java/com/tashow/erp/test/SeleniumWithProfile.java" beforeDir="false" afterPath="$PROJECT_DIR$/erp_client_sb/src/main/java/com/tashow/erp/test/SeleniumWithProfile.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/erp_client_sb/src/main/java/com/tashow/erp/utils/PersistentDriverManager.java" beforeDir="false" /> <change beforePath="$PROJECT_DIR$/erp_client_sb/src/main/java/com/tashow/erp/utils/RakutenProxyUtil.java" beforeDir="false" afterPath="$PROJECT_DIR$/erp_client_sb/src/main/java/com/tashow/erp/utils/RakutenProxyUtil.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/erp_client_sb/src/main/java/com/tashow/erp/utils/StealthSelenium.java" beforeDir="false" /> <change beforePath="$PROJECT_DIR$/pom.xml" beforeDir="false" afterPath="$PROJECT_DIR$/pom.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/AdvancedSliderCaptchaHandler.java" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ClientAccountController.java" beforeDir="false" afterPath="$PROJECT_DIR$/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ClientAccountController.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ClientMonitorController.java" beforeDir="false" afterPath="$PROJECT_DIR$/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ClientMonitorController.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-admin/src/main/java/com/ruoyi/web/service/IClientMonitorService.java" beforeDir="false" afterPath="$PROJECT_DIR$/ruoyi-admin/src/main/java/com/ruoyi/web/service/IClientMonitorService.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-admin/src/main/java/com/ruoyi/web/service/impl/ClientMonitorServiceImpl.java" beforeDir="false" afterPath="$PROJECT_DIR$/ruoyi-admin/src/main/java/com/ruoyi/web/service/impl/ClientMonitorServiceImpl.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/api.properties" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/annotation/Anonymous.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/annotation/DataScope.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/annotation/DataSource.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/annotation/Excel$ColumnType.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/annotation/Excel$Type.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/annotation/Excel.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/annotation/Excels.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/annotation/Log.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/annotation/RateLimiter.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/annotation/RepeatSubmit.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/annotation/Sensitive.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/config/FileUploadConfig.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/config/Qiniu.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/config/RuoYiConfig.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/config/serializer/SensitiveJsonSerializer.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/constant/CacheConstants.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/constant/Constants.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/constant/GenConstants.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/constant/HttpStatus.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/constant/ScheduleConstants$Status.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/constant/ScheduleConstants.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/constant/UserConstants.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/core/controller/BaseController$1.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/core/controller/BaseController.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/core/domain/AjaxResult.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/core/domain/BaseEntity.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/core/domain/R.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/core/domain/TreeEntity.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/core/domain/TreeSelect.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysDept.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysDictData.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysDictType.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysMenu.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysRole.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/core/domain/entity/SysUser.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/core/domain/model/LoginBody.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/core/domain/model/LoginUser.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/core/domain/model/RegisterBody.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/core/page/PageDomain.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/core/page/TableDataInfo.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/core/page/TableSupport.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/core/redis/RedisCache.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/core/text/CharsetKit.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/core/text/Convert.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/core/text/StrFormatter.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/enums/BusinessStatus.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/enums/BusinessType.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/enums/DataSourceType.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/enums/DesensitizedType.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/enums/HttpMethod.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/enums/LimitType.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/enums/OperatorType.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/enums/QiniuZone.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/enums/UserStatus.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/exception/DemoModeException.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/exception/GlobalException.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/exception/ServiceException.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/exception/UtilException.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/exception/base/BaseException.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/exception/file/FileException.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/exception/file/FileSizeLimitExceededException.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/exception/file/FileUploadException.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidFlashExtensionException.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidImageExtensionException.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidMediaExtensionException.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException$InvalidVideoExtensionException.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/exception/file/InvalidExtensionException.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/exception/job/TaskException$Code.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/exception/job/TaskException.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/exception/user/BlackListException.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/exception/user/CaptchaException.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/exception/user/CaptchaExpireException.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/exception/user/UserException.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/exception/user/UserNotExistsException.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/exception/user/UserPasswordNotMatchException.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/filter/PropertyPreExcludeFilter.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/filter/RepeatableFilter.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/filter/RepeatedlyRequestWrapper$1.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/filter/RepeatedlyRequestWrapper.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/filter/XssFilter.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/filter/XssHttpServletRequestWrapper$1.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/filter/XssHttpServletRequestWrapper.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/Arith.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/DateUtils.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/DesensitizedUtil.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/DictUtils.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/ExceptionUtil.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/LogUtils.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/MessageUtils.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/PageUtils.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/SecurityUtils.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/ServletUtils.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/StringUtils.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/Threads.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/bean/BeanUtils.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/bean/BeanValidators.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/file/FileTypeUtils.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/file/FileUploadUtils.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/file/FileUtils.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/file/ImageUtils.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/file/MimeTypeUtils.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/html/EscapeUtil.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/html/HTMLFilter.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/http/HttpHelper.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/http/HttpUtils$TrustAnyHostnameVerifier.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/http/HttpUtils$TrustAnyTrustManager.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/http/HttpUtils.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/ip/AddressUtils.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/ip/IpUtils.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/poi/ExcelUtil.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/reflect/ReflectUtils.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/sign/Base64.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/sign/Md5Utils.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/spring/SpringUtils.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/sql/SqlUtil.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/uuid/IdUtils.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/uuid/Seq.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/uuid/UUID$Holder.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/utils/uuid/UUID.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/xss/Xss.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/com/ruoyi/common/xss/XssValidator.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/classes/shop.properties" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-common/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/aspectj/DataScopeAspect.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/aspectj/DataSourceAspect.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/aspectj/LogAspect.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/aspectj/RateLimiterAspect.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/config/ApplicationConfig.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/config/CaptchaConfig.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/config/CorsInterceptor.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/config/DruidConfig$1.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/config/DruidConfig.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/config/FilterConfig.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/config/I18nConfig.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/config/InterceptorConfig.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/config/KaptchaTextCreator.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/config/MyBatisConfig.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/config/RedisConfig.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/config/ResourcesConfig.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/config/SecurityConfig.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/config/ServerConfig.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/config/ThreadPoolConfig$1.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/config/ThreadPoolConfig.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/config/properties/DruidProperties.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/config/properties/PermitAllUrlProperties.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/datasource/DynamicDataSource.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/manager/AsyncManager.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/manager/ShutdownManager.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/manager/factory/AsyncFactory$1.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/manager/factory/AsyncFactory$2.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/manager/factory/AsyncFactory.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/security/context/AuthenticationContextHolder.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/security/context/PermissionContextHolder.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/web/domain/Server.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/web/domain/server/Cpu.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/web/domain/server/Jvm.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/web/domain/server/Mem.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/web/domain/server/Sys.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/web/domain/server/SysFile.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/web/exception/GlobalExceptionHandler.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/PermissionService.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/SysLoginService.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/SysPasswordService.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/SysPermissionService.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/SysRegisterService.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/TokenService.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/classes/com/ruoyi/framework/web/service/UserDetailsServiceImpl.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-framework/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/com/ruoyi/generator/config/GenConfig.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/com/ruoyi/generator/controller/GenController.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/com/ruoyi/generator/domain/GenTable.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/com/ruoyi/generator/domain/GenTableColumn.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/com/ruoyi/generator/mapper/GenTableColumnMapper.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/com/ruoyi/generator/mapper/GenTableMapper.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/com/ruoyi/generator/service/GenTableColumnServiceImpl.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/com/ruoyi/generator/service/GenTableServiceImpl.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/com/ruoyi/generator/service/IGenTableColumnService.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/com/ruoyi/generator/service/IGenTableService.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/com/ruoyi/generator/util/GenUtils.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/com/ruoyi/generator/util/VelocityInitializer.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/com/ruoyi/generator/util/VelocityUtils.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/generator.yml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/mapper/generator/GenTableColumnMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/mapper/generator/GenTableMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/vm/java/controller.java.vm" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/vm/java/domain.java.vm" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/vm/java/mapper.java.vm" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/vm/java/service.java.vm" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/vm/java/serviceImpl.java.vm" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/vm/java/sub-domain.java.vm" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/vm/js/api.js.vm" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/vm/sql/sql.vm" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/vm/vue/index-tree.vue.vm" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/vm/vue/index.vue.vm" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/vm/vue/v3/index-tree.vue.vm" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/vm/vue/v3/index.vue.vm" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/classes/vm/xml/mapper.xml.vm" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-generator/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-quartz/target/classes/com/ruoyi/quartz/controller/SysJobController.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-quartz/target/classes/com/ruoyi/quartz/controller/SysJobLogController.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-quartz/target/classes/com/ruoyi/quartz/domain/SysJob.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-quartz/target/classes/com/ruoyi/quartz/domain/SysJobLog.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-quartz/target/classes/com/ruoyi/quartz/mapper/SysJobLogMapper.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-quartz/target/classes/com/ruoyi/quartz/mapper/SysJobMapper.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-quartz/target/classes/com/ruoyi/quartz/service/ISysJobLogService.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-quartz/target/classes/com/ruoyi/quartz/service/ISysJobService.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-quartz/target/classes/com/ruoyi/quartz/service/impl/SysJobLogServiceImpl.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-quartz/target/classes/com/ruoyi/quartz/service/impl/SysJobServiceImpl.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-quartz/target/classes/com/ruoyi/quartz/task/RyTask.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-quartz/target/classes/com/ruoyi/quartz/util/AbstractQuartzJob.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-quartz/target/classes/com/ruoyi/quartz/util/CronUtils.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-quartz/target/classes/com/ruoyi/quartz/util/JobInvokeUtil.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-quartz/target/classes/com/ruoyi/quartz/util/QuartzDisallowConcurrentExecution.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-quartz/target/classes/com/ruoyi/quartz/util/QuartzJobExecution.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-quartz/target/classes/com/ruoyi/quartz/util/ScheduleUtils.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-quartz/target/classes/mapper/quartz/SysJobLogMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-quartz/target/classes/mapper/quartz/SysJobMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-quartz/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-quartz/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/src/main/java/com/ruoyi/system/mapper/ClientMonitorMapper.java" beforeDir="false" afterPath="$PROJECT_DIR$/ruoyi-system/src/main/java/com/ruoyi/system/mapper/ClientMonitorMapper.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/src/main/resources/mapper/system/ClientMonitorMapper.xml" beforeDir="false" afterPath="$PROJECT_DIR$/ruoyi-system/src/main/resources/mapper/system/ClientMonitorMapper.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/domain/ClientAccount.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/domain/ClientAuthInfo.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/domain/ClientAuthKey.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/domain/ClientDataReport.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/domain/ClientDevice.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/domain/ClientErrorReport.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/domain/ClientEventLog.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/domain/ClientInfo.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/domain/RefreshToken.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/domain/SysCache.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/domain/SysConfig.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/domain/SysLogininfor.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/domain/SysNotice.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/domain/SysOperLog.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/domain/SysPost.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/domain/SysRoleDept.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/domain/SysRoleMenu.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/domain/SysUserOnline.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/domain/SysUserPost.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/domain/SysUserRole.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/MetaVo.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/domain/vo/RouterVo.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/mapper/ClientAccountMapper.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/mapper/ClientDeviceMapper.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/mapper/ClientEventLogMapper.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/mapper/ClientInfoMapper.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/mapper/ClientMonitorMapper.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/mapper/RefreshTokenMapper.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysConfigMapper.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysDeptMapper.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysDictDataMapper.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysDictTypeMapper.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysLogininforMapper.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysMenuMapper.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysNoticeMapper.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysOperLogMapper.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysPostMapper.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysRoleDeptMapper.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysRoleMapper.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysRoleMenuMapper.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysUserMapper.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysUserPostMapper.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/mapper/SysUserRoleMapper.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/service/ISysConfigService.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/service/ISysDeptService.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/service/ISysDictDataService.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/service/ISysDictTypeService.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/service/ISysLogininforService.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/service/ISysMenuService.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/service/ISysNoticeService.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/service/ISysOperLogService.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/service/ISysPostService.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/service/ISysRoleService.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/service/ISysUserOnlineService.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/service/ISysUserService.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysConfigServiceImpl.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysDeptServiceImpl.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysDictDataServiceImpl.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysLogininforServiceImpl.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysMenuServiceImpl.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysNoticeServiceImpl.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysOperLogServiceImpl.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysPostServiceImpl.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysRoleServiceImpl.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/com/ruoyi/system/service/impl/SysUserServiceImpl.class" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/mapper/system/ClientAccountMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/mapper/system/ClientDeviceMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/mapper/system/ClientEventLogMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/mapper/system/ClientInfoMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/mapper/system/ClientMonitorMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/mapper/system/ClientMonitorMapper.xml.bak" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/mapper/system/RefreshTokenMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/mapper/system/SysConfigMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/mapper/system/SysDeptMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/mapper/system/SysDictDataMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/mapper/system/SysDictTypeMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/mapper/system/SysLogininforMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/mapper/system/SysMenuMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/mapper/system/SysNoticeMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/mapper/system/SysOperLogMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/mapper/system/SysPostMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/mapper/system/SysRoleDeptMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/mapper/system/SysRoleMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/mapper/system/SysRoleMenuMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/mapper/system/SysUserMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/mapper/system/SysUserPostMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/classes/mapper/system/SysUserRoleMapper.xml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/ruoyi-system/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst" beforeDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -48,6 +391,7 @@
"Spring Boot.ErpClientSbApplication.executor": "Debug", "Spring Boot.ErpClientSbApplication.executor": "Debug",
"Spring Boot.RuoYiApplication.executor": "Debug", "Spring Boot.RuoYiApplication.executor": "Debug",
"git-widget-placeholder": "master", "git-widget-placeholder": "master",
"last_opened_file_path": "C:/Users/ZiJIe/Desktop/wox/RuoYi-Vue",
"node.js.detected.package.eslint": "true", "node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true", "node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)", "node.js.selected.package.eslint": "(autodetect)",
@@ -61,6 +405,11 @@
<component name="ReactorSettings"> <component name="ReactorSettings">
<option name="notificationShown" value="true" /> <option name="notificationShown" value="true" />
</component> </component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="C:\Users\ZiJIe\Desktop\wox\RuoYi-Vue" />
</key>
</component>
<component name="RunDashboard"> <component name="RunDashboard">
<option name="configurationTypes"> <option name="configurationTypes">
<set> <set>
@@ -94,6 +443,10 @@
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>
</configuration> </configuration>
<list>
<item itemvalue="Spring Boot.ErpClientSbApplication" />
<item itemvalue="Spring Boot.RuoYiApplication" />
</list>
</component> </component>
<component name="SharedIndexes"> <component name="SharedIndexes">
<attachedChunks> <attachedChunks>
@@ -225,12 +578,31 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1758619552979</updated> <updated>1758619552979</updated>
</task> </task>
<option name="localTasksCounter" value="15" /> <task id="LOCAL-00015" summary="1">
<option name="closed" value="true" />
<created>1758683139068</created>
<option name="number" value="00015" />
<option name="presentableId" value="LOCAL-00015" />
<option name="project" value="LOCAL" />
<updated>1758683139068</updated>
</task>
<option name="localTasksCounter" value="16" />
<servers /> <servers />
</component> </component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" /> <option name="version" value="3" />
</component> </component>
<component name="Vcs.Log.Tabs.Properties">
<option name="TAB_STATES">
<map>
<entry key="MAIN">
<value>
<State />
</value>
</entry>
</map>
</option>
</component>
<component name="VcsManagerConfiguration"> <component name="VcsManagerConfiguration">
<MESSAGE value="1" /> <MESSAGE value="1" />
<option name="LAST_COMMIT_MESSAGE" value="1" /> <option name="LAST_COMMIT_MESSAGE" value="1" />
@@ -241,7 +613,6 @@
<line-breakpoint enabled="true" type="javascript"> <line-breakpoint enabled="true" type="javascript">
<url>file://$PROJECT_DIR$/electron-vue-template/src/renderer/components/common/AccountManager.vue</url> <url>file://$PROJECT_DIR$/electron-vue-template/src/renderer/components/common/AccountManager.vue</url>
<line>34</line> <line>34</line>
<properties lambdaOrdinal="-1" />
<option name="timeStamp" value="8" /> <option name="timeStamp" value="8" />
</line-breakpoint> </line-breakpoint>
</breakpoints> </breakpoints>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -150,6 +150,12 @@ async function handleLoginSuccess(data: { token: string; permissions?: string })
} }
async function logout() { async function logout() {
const token = await authApi.getToken()
if (token) {
await authApi.logout(token)
}
await authApi.deleteTokenCache() await authApi.deleteTokenCache()
// 清理前端状态 // 清理前端状态
isAuthenticated.value = false isAuthenticated.value = false
@@ -158,7 +164,8 @@ async function logout() {
showAuthDialog.value = true showAuthDialog.value = true
showDeviceDialog.value = false showDeviceDialog.value = false
// 关闭SSE连接 // 关闭SSE连接`-+++++++
SSEManager.disconnect() SSEManager.disconnect()
} }

View File

@@ -269,7 +269,7 @@ onMounted(async () => {
<div class="body-layout"> <div class="body-layout">
<!-- 左侧步骤栏 --> <!-- 左侧步骤栏 -->
<aside class="steps-sidebar"> <aside class="steps-sidebar">
<div class="steps-title">查询步骤</div> <div class="steps-title">操作流程</div>
<div class="steps-flow"> <div class="steps-flow">
<!-- 1 --> <!-- 1 -->
<div class="flow-item"> <div class="flow-item">
@@ -408,7 +408,7 @@ onMounted(async () => {
.main-container { background: #fff; border-radius: 4px; padding: 15px; box-shadow: 0 2px 4px rgba(0,0,0,0.05); height: 100%; display: flex; flex-direction: column; } .main-container { background: #fff; border-radius: 4px; padding: 15px; box-shadow: 0 2px 4px rgba(0,0,0,0.05); height: 100%; display: flex; flex-direction: column; }
.body-layout { display: flex; gap: 12px; height: 100%; } .body-layout { display: flex; gap: 12px; height: 100%; }
.steps-sidebar { width: 220px; background: #fff; border: 1px solid #ebeef5; border-radius: 6px; padding: 10px; height: 100%; flex-shrink: 0; } .steps-sidebar { width: 220px; background: #fff; border: 1px solid #ebeef5; border-radius: 6px; padding: 10px; height: 100%; flex-shrink: 0; }
.steps-title { font-size: 14px; font-weight: 600; color: #303133; margin-bottom: 8px; } .steps-title { font-size: 14px; font-weight: 600; color: #303133; margin-bottom: 8px; text-align: left; }
.steps-flow { position: relative; } .steps-flow { position: relative; }
.steps-flow:before { content: ''; position: absolute; left: 12px; top: 0; bottom: 0; width: 2px; background: #e5e7eb; } .steps-flow:before { content: ''; position: absolute; left: 12px; top: 0; bottom: 0; width: 2px; background: #e5e7eb; }
.flow-item { position: relative; display: grid; grid-template-columns: 24px 1fr; gap: 10px; padding: 8px 0; } .flow-item { position: relative; display: grid; grid-template-columns: 24px 1fr; gap: 10px; padding: 8px 0; }

View File

@@ -30,19 +30,6 @@ const progressStarted = ref(false)
const progressPercentage = ref(0) const progressPercentage = ref(0)
const totalProducts = ref(0) const totalProducts = ref(0)
const processedProducts = ref(0) const processedProducts = ref(0)
// 进度头部文案(展示在进度条上方)
const successCount = computed(() =>
allProducts.value.filter(
(p: any) => p && p.mapRecognitionLink && String(p.mapRecognitionLink).trim() !== ''
).length
)
const progressHeader = computed(() => {
if (!progressStarted.value) return ''
if (progressPercentage.value >= 100) {
return `数据获取完成(成功获取 ${successCount.value} 个) 左侧操作栏点击“导出数据”按钮可导出为Excel文件`
}
return '数据获取中'
})
// 左侧步骤栏进度 // 左侧步骤栏进度
const activeStep = computed(() => { const activeStep = computed(() => {
@@ -79,20 +66,33 @@ function openRakutenUpload() {
uploadInputRef.value?.click() uploadInputRef.value?.click()
} }
function parseSkuPrices(product: any) { function parseSkuPrices(input: any) {
if (!product.skuPrice) return []
try { try {
let skuStr = product.skuPrice let skuSource: any = input
if (typeof skuStr === 'string') { if (input && typeof input === 'object') {
skuStr = skuStr.replace(/(\d+(?:\.\d+)?):"/g, '"$1":"') skuSource = input.skuPriceJson ?? input.skuPrice
skuStr = JSON.parse(skuStr)
} }
return Object.keys(skuStr).map(p => parseFloat(p)).filter(n => !isNaN(n)).sort((a, b) => a - b) if (!skuSource) return []
if (typeof skuSource === 'string') {
skuSource = skuSource.replace(/(\d+(?:\.\d+)?):"/g, '"$1":"')
skuSource = JSON.parse(skuSource)
}
if (!skuSource || typeof skuSource !== 'object') return []
return Object.keys(skuSource)
.map(p => parseFloat(p))
.filter(n => !isNaN(n))
.sort((a, b) => a - b)
} catch { } catch {
return [] return []
} }
} }
function needsSearch(product: any) {
const hasParsedPrices = Array.isArray(product?.skuPrices) && product.skuPrices.length > 0
const hasRawPrices = !!(product?.skuPriceJson || product?.skuPrice)
return !(hasParsedPrices || hasRawPrices)
}
async function loadLatest() { async function loadLatest() {
const resp = await rakutenApi.getLatestProducts() const resp = await rakutenApi.getLatestProducts()
allProducts.value = (resp.products || []).map(p => ({...p, skuPrices: parseSkuPrices(p)})) allProducts.value = (resp.products || []).map(p => ({...p, skuPrices: parseSkuPrices(p)}))
@@ -101,16 +101,18 @@ async function loadLatest() {
async function searchProductInternal(product: any) { async function searchProductInternal(product: any) {
if (!product || !product.imgUrl) return if (!product || !product.imgUrl) return
if (product.mapRecognitionLink && String(product.mapRecognitionLink).trim() !== '') return if (!needsSearch(product)) return
const res = await rakutenApi.search1688(product.imgUrl, currentBatchId.value) const res = await rakutenApi.search1688(product.imgUrl, currentBatchId.value)
const data = res const data = res
const skuJson = (data as any)?.skuPriceJson ?? (data as any)?.skuPrice
Object.assign(product, { Object.assign(product, {
mapRecognitionLink: data.mapRecognitionLink, mapRecognitionLink: data.mapRecognitionLink,
freight: data.freight, freight: data.freight,
median: data.median, median: data.median,
weight: data.weight, weight: data.weight,
skuPrice: data.skuPrice, skuPriceJson: skuJson,
skuPrices: parseSkuPrices(data), skuPrice: skuJson,
skuPrices: parseSkuPrices({ skuPriceJson: skuJson }),
image1688Url: data.mapRecognitionLink, image1688Url: data.mapRecognitionLink,
detailUrl1688: data.mapRecognitionLink, detailUrl1688: data.mapRecognitionLink,
}) })
@@ -163,36 +165,9 @@ async function onDrop(e: DragEvent) {
await processFile(file) await processFile(file)
} }
async function searchSingleShop() {
const shop = singleShopName.value.trim()
if (!shop) return
// 重置进度与状态 // 点击“获取数据
progressStarted.value = true
progressPercentage.value = 0
totalProducts.value = 0
processedProducts.value = 0
loading.value = true
tableLoading.value = true
currentBatchId.value = `RAKUTEN_${Date.now()}`
try {
const resp = await rakutenApi.getProducts({shopName: shop, batchId: currentBatchId.value})
allProducts.value = (resp.products || []).filter((p: any) => p.originalShopName === shop).map(p => ({ ...p, skuPrices: parseSkuPrices(p) }))
statusType.value = 'info'
statusMessage.value = `店铺 ${shop}${allProducts.value.length} 条,点击“获取数据”开始识图`
singleShopName.value = ''
} catch (e: any) {
statusMessage.value = e?.message || '查询失败'
statusType.value = 'error'
} finally {
loading.value = false
tableLoading.value = false
}
}
// 点击“获取数据”触发识图
async function handleStartSearch() { async function handleStartSearch() {
// 如果存在待解析文件,先请求后端解析再进入识图
if (pendingFile.value) { if (pendingFile.value) {
try { try {
loading.value = true loading.value = true
@@ -216,10 +191,19 @@ async function handleStartSearch() {
tableLoading.value = false tableLoading.value = false
} }
} }
const items = allProducts.value.filter(p => p && p.imgUrl && !p.mapRecognitionLink) const items = allProducts.value.filter(p => p && p.imgUrl && needsSearch(p))
if (allProducts.value.length > 0 && items.length === 0) {
progressStarted.value = true
totalProducts.value = allProducts.value.length
processedProducts.value = totalProducts.value
progressPercentage.value = 100
statusType.value = 'success'
statusMessage.value = ''
return
}
if (items.length === 0) { if (items.length === 0) {
statusType.value = 'warning' statusType.value = 'warning'
statusMessage.value = '没有可识图的商品,请先导入或查询店铺' statusMessage.value = '没有可处理的商品,请先导入或查询店铺'
return return
} }
await startBatch1688Search(items) await startBatch1688Search(items)
@@ -235,7 +219,7 @@ function stopTask() {
} }
async function startBatch1688Search(products: any[]) { async function startBatch1688Search(products: any[]) {
const items = (products || []).filter(p => p && p.imgUrl && !p.mapRecognitionLink) const items = (products || []).filter(p => p && p.imgUrl && needsSearch(p))
if (items.length === 0) { if (items.length === 0) {
progressPercentage.value = 100 progressPercentage.value = 100
statusType.value = 'success' statusType.value = 'success'
@@ -319,7 +303,7 @@ onMounted(loadLatest)
<div class="body-layout"> <div class="body-layout">
<!-- 左侧步骤栏 --> <!-- 左侧步骤栏 -->
<aside class="steps-sidebar"> <aside class="steps-sidebar">
<div class="steps-title">查询步骤</div> <div class="steps-title">操作流程</div>
<div class="steps-flow"> <div class="steps-flow">
<!-- Step 1 导入乐天店铺 --> <!-- Step 1 导入乐天店铺 -->
@@ -399,12 +383,8 @@ onMounted(loadLatest)
<!-- 数据显示区域 --> <!-- 数据显示区域 -->
<div class="table-container"> <div class="table-container">
<div class="table-section"> <div class="table-section">
<!-- 表格上方进度条移动到表格容器内部 --> <!-- 表格上方进度条 -->
<div v-if="progressStarted" class="progress-head"> <div v-if="progressStarted" class="progress-head">
<div class="progress-title">
<span v-if="progressPercentage>=100" class="ok-badge"></span>
<span class="title-text">{{ progressHeader }}</span>
</div>
<div class="progress-section"> <div class="progress-section">
<div class="progress-box"> <div class="progress-box">
<div class="progress-container"> <div class="progress-container">
@@ -519,7 +499,7 @@ onMounted(loadLatest)
.body-layout { display: flex; gap: 12px; height: 100%; } .body-layout { display: flex; gap: 12px; height: 100%; }
.steps-sidebar { width: 220px; background: #fff; border: 1px solid #ebeef5; border-radius: 6px; padding: 10px; height: 100%; flex-shrink: 0; } .steps-sidebar { width: 220px; background: #fff; border: 1px solid #ebeef5; border-radius: 6px; padding: 10px; height: 100%; flex-shrink: 0; }
.steps-title { font-size: 14px; font-weight: 600; color: #303133; margin-bottom: 8px; } .steps-title { font-size: 14px; font-weight: 600; color: #303133; margin-bottom: 8px; text-align: left; }
/* 卡片式步骤,与示例一致 */ /* 卡片式步骤,与示例一致 */
.steps-flow { position: relative; } .steps-flow { position: relative; }
@@ -568,43 +548,12 @@ onMounted(loadLatest)
.btn-blue:disabled { background: #a6c8ff; border-color: #a6c8ff; color: #fff; } .btn-blue:disabled { background: #a6c8ff; border-color: #a6c8ff; color: #fff; }
.w100 { width: 100%; } .w100 { width: 100%; }
.steps-sidebar :deep(.el-button + .el-button) { margin-left: 0; } .steps-sidebar :deep(.el-button + .el-button) { margin-left: 0; }
.progress-section { margin: 0px 12px 0px 12px; }
.import-section {
margin-bottom: 10px;
flex-shrink: 0;
}
.import-controls {
display: flex;
align-items: flex-end;
gap: 20px;
flex-wrap: wrap;
margin-bottom: 8px;
}
.single-input {
display: flex;
align-items: center;
gap: 8px;
}
.action-buttons {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.progress-section { margin: 12px 12px 6px 12px; }
.progress-box { padding: 4px 0; } .progress-box { padding: 4px 0; }
.progress-container { display: flex; align-items: center; gap: 8px; } .progress-container { display: flex; align-items: center; gap: 8px; }
.progress-bar { flex: 1; height: 6px; background: #e3eeff; border-radius: 999px; overflow: hidden; } .progress-bar { flex: 1; height: 6px; background: #e3eeff; border-radius: 999px; overflow: hidden; }
.progress-fill { height: 100%; background: #1677FF; border-radius: 999px; transition: width 0.3s ease; } .progress-fill { height: 100%; background: #1677FF; border-radius: 999px; transition: width 0.3s ease; }
.progress-text { font-size: 13px; color: #1677FF; font-weight: 500; min-width: 44px; text-align: right; } .progress-text { font-size: 13px; color: #1677FF; font-weight: 500; min-width: 44px; text-align: right; }
.progress-head { padding: 8px 12px 0 12px; }
.progress-title { display:flex; align-items:center; gap:8px; color:#606266; font-size: 13px; margin-bottom: 6px; }
.progress-title .ok-badge { color: #52c41a; font-size: 12px; }
.progress-title .title-text { color:#303133; font-weight:600; }
.current-status { .current-status {
font-size: 12px; font-size: 12px;

View File

@@ -7,7 +7,7 @@ type Shop = { id: string; shopName: string }
const accounts = ref<BanmaAccount[]>([]) const accounts = ref<BanmaAccount[]>([])
const accountId = ref<number>() const accountId = ref<number>()
const isCollapsed = ref(false) // 收起功能移除
const shopList = ref<Shop[]>([]) const shopList = ref<Shop[]>([])
const selectedShops = ref<string[]>([]) const selectedShops = ref<string[]>([])
@@ -220,10 +220,9 @@ async function removeCurrentAccount() {
<template> <template>
<div class="zebra-root"> <div class="zebra-root">
<div class="layout"> <div class="layout">
<aside :class="['aside', { collapsed: isCollapsed }]"> <aside class="aside">
<div class="aside-header"> <div class="aside-header">
<span>操作流程</span> <span>操作流程</span>
<el-button link @click="isCollapsed = !isCollapsed">{{ isCollapsed ? '展开' : '收起' }}</el-button>
</div> </div>
<div class="aside-steps"> <div class="aside-steps">
<section class="step step-accounts"> <section class="step step-accounts">
@@ -428,7 +427,7 @@ export default {
.layout { background: #fff; border-radius: 4px; padding: 12px; box-shadow: 0 2px 4px rgba(0,0,0,0.05); height: 100%; display: grid; grid-template-columns: 220px 1fr; gap: 12px; } .layout { background: #fff; border-radius: 4px; padding: 12px; box-shadow: 0 2px 4px rgba(0,0,0,0.05); height: 100%; display: grid; grid-template-columns: 220px 1fr; gap: 12px; }
.aside { border: 1px solid #ebeef5; border-radius: 4px; padding: 10px; display: flex; flex-direction: column; transition: width 0.2s ease; } .aside { border: 1px solid #ebeef5; border-radius: 4px; padding: 10px; display: flex; flex-direction: column; transition: width 0.2s ease; }
.aside.collapsed { width: 56px; overflow: hidden; } .aside.collapsed { width: 56px; overflow: hidden; }
.aside-header { display: flex; justify-content: space-between; align-items: center; font-weight: 600; color: #606266; margin-bottom: 8px; } .aside-header { display: flex; justify-content: flex-start; align-items: center; font-weight: 600; color: #606266; margin-bottom: 8px; }
.aside-steps { position: relative; } .aside-steps { position: relative; }
.step { display: grid; grid-template-columns: 24px 1fr; gap: 10px; position: relative; padding: 8px 0; } .step { display: grid; grid-template-columns: 24px 1fr; gap: 10px; position: relative; padding: 8px 0; }
.step + .step { border-top: 1px dashed #ebeef5; } .step + .step { border-top: 1px dashed #ebeef5; }

View File

@@ -67,6 +67,12 @@ public class AuthController {
return ResponseEntity.ok(result); return ResponseEntity.ok(result);
} }
@PostMapping("/logout")
public ResponseEntity<?> logout(@RequestBody Map<String, Object> data) {
authService.logout();
return ResponseEntity.ok(Map.of("code", 0, "message", "退出成功"));
}
@GetMapping("/check-username") @GetMapping("/check-username")
public ResponseEntity<?> checkUsername(@RequestParam String username) { public ResponseEntity<?> checkUsername(@RequestParam String username) {
if (username == null || username.trim().isEmpty()) { if (username == null || username.trim().isEmpty()) {

View File

@@ -79,12 +79,8 @@ public class RakutenController {
log.info("采集完成: {}, 数量: {}", currentShopName, fresh.size()); log.info("采集完成: {}, 数量: {}", currentShopName, fresh.size());
} }
} }
// 3. 处理新采集的数据,存储并生成 sessionId
List<RakutenProduct> newProducts = allProducts.stream().filter(p -> !skippedShops.contains(p.getOriginalShopName())).toList(); List<RakutenProduct> newProducts = allProducts.stream().filter(p -> !skippedShops.contains(p.getOriginalShopName())).toList();
if (!newProducts.isEmpty()) { if (!newProducts.isEmpty()) {
// 使用已有 sessionId 保存
rakutenCacheService.saveProductsWithSessionId(newProducts, batchId); rakutenCacheService.saveProductsWithSessionId(newProducts, batchId);
} }
// 4. 上报缓存数据使用情况 // 4. 上报缓存数据使用情况

View File

@@ -29,7 +29,7 @@ public class RakutenProduct {
private Double freight; private Double freight;
private Double median; private Double median;
private String weight; private String weight;
private String skuPriceJson;
@Override @Override
public String toString() { public String toString() {
return "RakutenProduct{" + return "RakutenProduct{" +

View File

@@ -32,6 +32,8 @@ public interface IAuthService {
*/ */
String getClientId(); String getClientId();
void logout();
/** /**
* 验证token * 验证token
*/ */

View File

@@ -6,7 +6,6 @@ import java.util.regex.Pattern;
public class SeleniumWithProfile { public class SeleniumWithProfile {
private static final Pattern WEIGHT_PATTERN = Pattern.compile("\"(?:unitWeight|weight)\":(\\d+(?:\\.\\d+)?)"); private static final Pattern WEIGHT_PATTERN = Pattern.compile("\"(?:unitWeight|weight)\":(\\d+(?:\\.\\d+)?)");
public static void main(String[] args) { public static void main(String[] args) {
String uuid = ""; String uuid = "";
try { try {

View File

@@ -229,7 +229,6 @@
<module>ruoyi-quartz</module> <module>ruoyi-quartz</module>
<module>ruoyi-generator</module> <module>ruoyi-generator</module>
<module>ruoyi-common</module> <module>ruoyi-common</module>
<module>untitled</module>
</modules> </modules>
<packaging>pom</packaging> <packaging>pom</packaging>

View File

@@ -1,394 +0,0 @@
package com.ruoyi.web.controller.common;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.support.ui.ExpectedConditions;
import java.time.Duration;
import java.util.List;
import java.util.Random;
/**
* 高级滑块验证码处理器
* 支持多种滑块类型和重试机制
*/
public class AdvancedSliderCaptchaHandler {
private final WebDriver driver;
private final WebDriverWait wait;
private final Actions actions;
private final Random random;
private static final int MAX_RETRY_ATTEMPTS = 3;
private static final Duration WAIT_TIMEOUT = Duration.ofSeconds(15);
// 人类行为参数
private static final int MIN_CLICK_HOLD_DURATION = 300;
private static final int MAX_CLICK_HOLD_DURATION = 800;
private static final int MIN_MOVE_DURATION = 1000;
private static final int MAX_MOVE_DURATION = 2000;
private static final double OVERSHOOT_FACTOR = 1.05; // 5%的过冲概率
public AdvancedSliderCaptchaHandler(WebDriver driver) {
this.driver = driver;
this.wait = new WebDriverWait(driver, WAIT_TIMEOUT.toSeconds());
this.actions = new Actions(driver);
this.random = new Random();
}
public boolean handleAnyCaptcha() {
for (int attempt = 1; attempt <= MAX_RETRY_ATTEMPTS; attempt++) {
System.out.println("" + attempt + " 次尝试处理验证码");
CaptchaType captchaType = detectCaptchaType();
if (captchaType == CaptchaType.NONE) {
System.out.println("未检测到验证码");
return true;
}
boolean success = processCaptchaByType(captchaType);
if (success) {
System.out.println("验证码处理成功");
return true;
}
if (attempt < MAX_RETRY_ATTEMPTS) {
waitBeforeRetry();
}
}
System.err.println("验证码处理失败,已达到最大重试次数");
return false;
}
/**
* 检测验证码类型
*/
private CaptchaType detectCaptchaType() {
try {
if (isElementPresent(By.id("nc_1_n1z"))) {
return CaptchaType.ALIBABA_SLIDER;
}
// 检测通用滑块
if (isElementPresent(By.className("btn_slide")) ||
isElementPresent(By.className("slider-btn"))) {
return CaptchaType.GENERIC_SLIDER;
}
// 检测其他常见滑块选择器
String[] commonSelectors = {
".captcha-slider-btn",
".slide-verify-slider-mask-item",
".slider_bg .slider_btn"
};
for (String selector : commonSelectors) {
if (isElementPresent(By.cssSelector(selector))) {
return CaptchaType.GENERIC_SLIDER;
}
}
return CaptchaType.NONE;
} catch (Exception e) {
System.err.println("检测验证码类型时发生错误: " + e.getMessage());
return CaptchaType.NONE;
}
}
/**
* 根据验证码类型进行处理
*/
private boolean processCaptchaByType(CaptchaType type) {
switch (type) {
case ALIBABA_SLIDER:
return handleAlibabaSlider();
case GENERIC_SLIDER:
return handleGenericSlider();
default:
return false;
}
}
/**
* 处理阿里云滑块验证码
*/
private boolean handleAlibabaSlider() {
try {
WebElement sliderButton = wait.until(
ExpectedConditions.elementToBeClickable(By.id("nc_1_n1z"))
);
WebElement sliderTrack = driver.findElement(By.id("nc_1_n1t"));
int moveDistance = calculateMoveDistance(sliderTrack, sliderButton);
return performSmartSlide(sliderButton, moveDistance);
} catch (Exception e) {
System.err.println("处理阿里云滑块失败: " + e.getMessage());
return false;
}
}
/**
* 处理通用滑块验证码
*/
private boolean handleGenericSlider() {
try {
List<WebElement> sliderButtons = driver.findElements(By.className("btn_slide"));
if (sliderButtons.isEmpty()) {
sliderButtons = driver.findElements(By.className("slider-btn"));
}
if (sliderButtons.isEmpty()) {
return false;
}
WebElement sliderButton = sliderButtons.get(0);
WebElement sliderTrack = findSliderTrack(sliderButton);
if (sliderTrack == null) {
return false;
}
int moveDistance = calculateMoveDistance(sliderTrack, sliderButton);
System.out.println("处理通用滑块失败: ");
return performSmartSlide(sliderButton, moveDistance);
} catch (Exception e) {
System.err.println("处理通用滑块失败: " + e.getMessage());
return false;
}
}
/**
* 智能滑动算法
*/
// private boolean performSmartSlide(WebElement sliderButton, int totalDistance) {
// try {
// actions.clickAndHold(sliderButton).perform();
// Thread.sleep(50 + random.nextInt(200));
//
// int moved = 0;
// int segments = 20 + random.nextInt(5);
//
// for (int i = 0; i < segments && moved < totalDistance; i++) {
// double progress = (double) i / segments;
// int stepSize = (int) (totalDistance * getBezierValue(progress) - moved);
//
// if (stepSize > 0 && moved + stepSize <= totalDistance) {
// int yOffset = (int) (Math.sin(progress * Math.PI * 2) * 2);
// actions.moveByOffset(stepSize, yOffset).perform();
// moved += stepSize;
//
// }
// }
// if (moved < totalDistance) {
// actions.moveByOffset(totalDistance - moved, 0).perform();
// }
//
// Thread.sleep(50 + random.nextInt(200));
// actions.release().perform();
//
// return waitForVerificationResult();
//
// } catch (Exception e) {
// System.err.println("智能滑动失败: " + e.getMessage());
// return false;
// }
// }
/**
* 贝塞尔曲线值计算
*/
private double getBezierValue(double t) {
return t * t * (3.0 - 2.0 * t);
}
/**
* 计算移动距离
*/
private int calculateMoveDistance(WebElement track, WebElement button) {
int trackWidth = track.getSize().getWidth() + 100;
int buttonWidth = button.getSize().getWidth();
return Math.max(trackWidth - buttonWidth - 5, 0);
}
/**
* 查找滑块轨道
*/
private WebElement findSliderTrack(WebElement button) {
try {
return button.findElement(By.xpath("./parent::*"));
} catch (Exception e) {
return null;
}
}
/**
* 等待验证结果
*/
private boolean waitForVerificationResult() {
try {
Thread.sleep(2000);
return true;
} catch (Exception e) {
return false;
}
}
/**
* 检查元素是否存在
*/
private boolean isElementPresent(By locator) {
try {
driver.findElement(locator);
return true;
} catch (Exception e) {
return false;
}
}
/**
* 重试前等待
*/
private void waitBeforeRetry() {
try {
Thread.sleep(2000 + random.nextInt(3000));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
/**
* 验证码类型枚举
*/
private enum CaptchaType {
NONE,
ALIBABA_SLIDER,
GENERIC_SLIDER
}
private boolean performSmartSlide(WebElement sliderButton, int totalDistance) {
try {
// 模拟人类点击前的短暂思考时间
randomSleep(MIN_CLICK_HOLD_DURATION, MAX_CLICK_HOLD_DURATION);
// 点击并按住滑块
actions.clickAndHold(sliderButton).perform();
// 生成更自然的移动轨迹
List<MoveStep> moveSteps = generateHumanLikeTrajectory(totalDistance);
// 执行移动步骤
long startTime = System.currentTimeMillis();
long currentTime = startTime;
long endTime = startTime + randomLong(MIN_MOVE_DURATION, MAX_MOVE_DURATION);
for (MoveStep step : moveSteps) {
// 根据时间进度执行步骤,使整体移动速度更自然
while (currentTime < endTime * step.timeProgress) {
actions.moveByOffset(step.xOffset, step.yOffset).perform();
currentTime = System.currentTimeMillis();
// 微小的随机停顿
if (random.nextDouble() < 0.1) {
Thread.sleep(random.nextInt(50));
}
}
}
// 随机过冲然后回退,更像人类操作
if (random.nextDouble() < OVERSHOOT_FACTOR) {
int overshoot = random.nextInt(5) + 5;
actions.moveByOffset(overshoot, 0).perform();
Thread.sleep(random.nextInt(100) + 50);
actions.moveByOffset(-overshoot, 0).perform();
}
// 释放前的随机延迟
randomSleep(100, 300);
actions.release().perform();
// 模拟验证过程中的等待
randomSleep(500, 1500);
return waitForVerificationResult();
} catch (Exception e) {
System.err.println("智能滑动失败: " + e.getMessage());
return false;
}
}
/**
* 生成更接近人类行为的滑动轨迹
*/
private List<MoveStep> generateHumanLikeTrajectory(int totalDistance) {
java.util.List<MoveStep> steps = new java.util.ArrayList<>();
// 总步数 - 随机但合理的范围
int totalSteps = random.nextInt(15) + 25;
// 计算每个时间点的进度0-1之间
double[] timeProgress = new double[totalSteps];
for (int i = 0; i < totalSteps; i++) {
timeProgress[i] = (double) i / totalSteps;
}
// 生成更自然的S型速度曲线开始和结束较慢中间较快
double[] distanceProgress = new double[totalSteps];
for (int i = 0; i < totalSteps; i++) {
double t = timeProgress[i];
// 使用改进的S型曲线函数
distanceProgress[i] = t * t * t * (t * (t * 6 - 15) + 10);
}
// 计算每一步的偏移量
for (int i = 0; i < totalSteps; i++) {
double currentDistance = i == 0 ?
distanceProgress[i] * totalDistance :
(distanceProgress[i] - distanceProgress[i-1]) * totalDistance;
// 添加小幅度的垂直偏移,模拟人类手部颤抖
int yOffset = random.nextInt(3) - 1; // -1, 0, 1
steps.add(new MoveStep(
(int) Math.round(currentDistance),
yOffset,
timeProgress[i]
));
}
return steps;
}
/**
* 随机睡眠一段时间
*/
private void randomSleep(int min, int max) throws InterruptedException {
Thread.sleep(random.nextInt(max - min + 1) + min);
}
/**
* 生成随机长整型数
*/
private long randomLong(long min, long max) {
return min + (long) (random.nextDouble() * (max - min));
}
/**
* 移动步骤类 - 封装每一步的移动信息
*/
private static class MoveStep {
final int xOffset;
final int yOffset;
final double timeProgress;
MoveStep(int xOffset, int yOffset, double timeProgress) {
this.xOffset = xOffset;
this.yOffset = yOffset;
this.timeProgress = timeProgress;
}
}
}

View File

@@ -198,7 +198,6 @@ public class ClientAccountController extends BaseController {
Map<String, Object> claims = Jwts.parser().setSigningKey(jwtRsaKeyService.getPublicKey()).parseClaimsJws(token).getBody(); Map<String, Object> claims = Jwts.parser().setSigningKey(jwtRsaKeyService.getPublicKey()).parseClaimsJws(token).getBody();
String username = (String) claims.getOrDefault("sub", claims.get("subject")); String username = (String) claims.getOrDefault("sub", claims.get("subject"));
String tokenClientId = (String) claims.get("clientId"); String tokenClientId = (String) claims.get("clientId");
if (username == null || tokenClientId == null || !tokenClientId.equals(clientId)) { if (username == null || tokenClientId == null || !tokenClientId.equals(clientId)) {
throw new RuntimeException("会话不匹配"); throw new RuntimeException("会话不匹配");
} }

View File

@@ -1,10 +1,6 @@
package com.ruoyi.web.controller.monitor; package com.ruoyi.web.controller.monitor;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import com.ruoyi.common.annotation.Anonymous;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@@ -12,8 +8,8 @@ import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.system.domain.*; import com.ruoyi.system.domain.*;
import com.ruoyi.web.service.IClientMonitorService;
import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.web.service.IClientMonitorService;
/** /**
* 客户端监控控制器 * 客户端监控控制器
@@ -111,18 +107,7 @@ public class ClientMonitorController extends BaseController {
} }
} }
/**
* 客户端心跳API
*/
@PostMapping("/api/heartbeat")
public AjaxResult clientHeartbeat(@RequestBody Map<String, Object> heartbeatData) {
try {
clientMonitorService.recordHeartbeat(heartbeatData);
return AjaxResult.success();
} catch (Exception e) {
return AjaxResult.error(e.getMessage());
}
}
/** /**
* 客户端错误上报API * 客户端错误上报API
@@ -170,66 +155,6 @@ public class ClientMonitorController extends BaseController {
/**
* 获取客户端日志内容
*/
@GetMapping("/logs/{clientId}")
public AjaxResult getClientLogs(@PathVariable String clientId) {
try {
return AjaxResult.success(clientMonitorService.getClientLogs(clientId));
} catch (Exception e) {
return AjaxResult.error("获取客户端日志失败: " + e.getMessage());
}
}
/**
* 下载客户端日志文件
*/
@PreAuthorize("@ss.hasPermi('monitor:client:list')")
@GetMapping("/logs/{clientId}/download")
public void downloadClientLogs(@PathVariable String clientId, HttpServletResponse response) {
try {
clientMonitorService.downloadClientLogs(clientId, response);
} catch (Exception e) {
logger.error("下载客户端日志失败: {}", e.getMessage());
}
}
/**
* 客户端日志上传接口
*/
@PostMapping("/logs/upload")
public AjaxResult uploadClientLogs(@RequestBody Map<String, Object> logData) {
try {
clientMonitorService.saveClientLogs(logData);
return AjaxResult.success();
} catch (Exception e) {
return AjaxResult.error("保存客户端日志失败: " + e.getMessage());
}
}
/**
* 客户端日志批量上传接口
*/
@PostMapping("/logs/batchUpload")
public AjaxResult batchUploadClientLogs(@RequestBody Map<String, Object> batchLogData) {
try {
String clientId = (String) batchLogData.get("clientId");
List<String> logEntries = (List<String>) batchLogData.get("logEntries");
if (clientId == null || logEntries == null || logEntries.isEmpty()) {
return AjaxResult.error("无效的批量日志数据");
}
clientMonitorService.saveBatchClientLogs(clientId, logEntries);
return AjaxResult.success();
} catch (Exception e) {
return AjaxResult.error("批量保存客户端日志失败: " + e.getMessage());
}
}
/** /**
* 清理过期数据 * 清理过期数据
*/ */

View File

@@ -2,11 +2,7 @@ package com.ruoyi.web.service;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.ruoyi.system.domain.*;
import com.ruoyi.system.domain.ClientInfo;
import com.ruoyi.system.domain.ClientErrorReport;
import com.ruoyi.system.domain.ClientDataReport;
import com.ruoyi.system.domain.ClientEventLog;
/** /**
* 客户端监控服务接口 * 客户端监控服务接口
@@ -16,144 +12,117 @@ import com.ruoyi.system.domain.ClientEventLog;
public interface IClientMonitorService { public interface IClientMonitorService {
/** /**
* 查询客户端信息列表 * 查询客户端设备列表
*/ */
public List<ClientInfo> selectClientInfoList(ClientInfo clientInfo); List<ClientDevice> selectClientDeviceList(String username);
/** /**
* 查询客户端错误报告列表 * 查询客户端错误报告列表
*/ */
public List<Map<String, Object>> selectClientErrorList(ClientErrorReport clientErrorReport); List<Map<String, Object>> selectClientErrorList(ClientErrorReport clientErrorReport);
/** /**
* 查询客户端事件日志列表 * 查询客户端事件日志列表
*/ */
public List<ClientEventLog> selectClientEventLogList(ClientEventLog clientEventLog); List<ClientEventLog> selectClientEventLogList(ClientEventLog clientEventLog);
/** /**
* 查询客户端数据采集报告列表 * 查询客户端数据采集报告列表
*/ */
public List<ClientDataReport> selectClientDataReportList(ClientDataReport clientDataReport); List<ClientDataReport> selectClientDataReportList(ClientDataReport clientDataReport);
/** /**
* 查询在线客户端数量 * 查询在线客户端数量
*/ */
public int selectOnlineClientCount(); int selectOnlineClientCount();
/**
* 检查客户端是否在线
*/
boolean isClientOnline(String clientId);
/**
* 获取在线客户端ID列表
*/
List<String> getOnlineClientIds();
/** /**
* 查询客户端总数 * 查询客户端总数
*/ */
public int selectTotalClientCount(); int selectTotalClientCount();
/**
* 新增客户端信息
*/
public int insertClientInfo(ClientInfo clientInfo);
/**
* 新增客户端错误报告
*/
public int insertClientError(ClientErrorReport clientErrorReport);
/**
* 新增客户端事件日志
*/
public int insertClientEventLog(ClientEventLog clientEventLog);
/**
* 新增客户端数据采集报告
*/
public int insertDataReport(ClientDataReport clientDataReport);
/** /**
* 获取客户端统计数据 * 获取客户端统计数据
*/ */
public Map<String, Object> getClientStatistics(); Map<String, Object> getClientStatistics();
/** /**
* 获取客户端活跃趋势 * 获取客户端活跃趋势
*/ */
public Map<String, Object> getClientActiveTrend(); Map<String, Object> getClientActiveTrend();
/** /**
* 获取数据采集类型分布 * 获取数据采集类型分布
*/ */
public Map<String, Object> getDataTypeDistribution(); Map<String, Object> getDataTypeDistribution();
/** /**
* 获取近7天在线客户端趋势 * 获取在线客户端趋势
*/ */
public Map<String, Object> getOnlineClientTrend(); Map<String, Object> getOnlineClientTrend();
/** /**
* 客户端认证 * 客户端认证
*/ */
public Map<String, Object> authenticateClient(String authKey, Map<String, Object> clientInfo); Map<String, Object> authenticateClient(String authKey, Map<String, Object> clientInfo);
/** /**
* 记录客户端心跳 * 记录错误报告
*/ */
public void recordHeartbeat(Map<String, Object> heartbeatData); void recordErrorReport(Map<String, Object> errorData);
/** /**
* 记录客户端错误 * 记录数据报告
*/ */
public void recordErrorReport(Map<String, Object> errorData); void recordDataReport(Map<String, Object> dataReport);
/**
* 记录客户端数据采集报告
*/
public void recordDataReport(Map<String, Object> dataReport);
/** /**
* 获取客户端详细信息 * 获取客户端详细信息
*/ */
public Map<String, Object> getClientDetail(String clientId); Map<String, Object> getClientDetail(String clientId);
/** /**
* 获取客户端版本分布 * 获取版本分布
*/ */
public List<Map<String, Object>> getVersionDistribution(); List<Map<String, Object>> getVersionDistribution();
/** /**
* 记录1688风控监控数据 * 插入客户端错误报告
*/ */
public void recordAlibaba1688MonitorData(Map<String, Object> monitorData); int insertClientError(ClientErrorReport clientErrorReport);
/** /**
* 查询1688风控监控数据列表 * 插入客户端设备
*/ */
public List<Map<String, Object>> selectAlibaba1688MonitorList(Map<String, Object> params); int insertClientDevice(ClientDevice clientDevice);
/** /**
* 获取1688风控数据统计 * 插入客户端事件日志
*/ */
public Map<String, Object> getAlibaba1688Statistics(); int insertClientEventLog(ClientEventLog clientEventLog);
/** /**
* 获取客户端日志内容 * 插入数据报告
*/ */
public Map<String, Object> getClientLogs(String clientId); int insertDataReport(ClientDataReport clientDataReport);
/** /**
* 下载客户端日志文件 * 查询客户端信息列表
*/ */
public void downloadClientLogs(String clientId, javax.servlet.http.HttpServletResponse response); List<ClientInfo> selectClientInfoList(ClientInfo clientInfo);
/**
* 保存客户端日志
*/
public void saveClientLogs(Map<String, Object> logData);
/**
* 批量保存客户端日志
*
* @param clientId 客户端ID
* @param logEntries 日志条目列表
*/
public void saveBatchClientLogs(String clientId, List<String> logEntries);
/** /**
* 清理过期数据 * 清理过期数据
*/ */
public void cleanExpiredData(); void cleanExpiredData();
} }

View File

@@ -4,7 +4,6 @@ import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import com.ruoyi.system.domain.*; import com.ruoyi.system.domain.*;
import com.ruoyi.common.core.redis.RedisCache;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
@@ -21,6 +20,7 @@ import org.slf4j.LoggerFactory;
import com.ruoyi.web.service.IClientAccountService; import com.ruoyi.web.service.IClientAccountService;
import com.ruoyi.web.service.IClientMonitorService; import com.ruoyi.web.service.IClientMonitorService;
import com.ruoyi.system.mapper.ClientMonitorMapper; import com.ruoyi.system.mapper.ClientMonitorMapper;
import com.ruoyi.system.mapper.ClientDeviceMapper;
/** /**
* 客户端监控服务实现 * 客户端监控服务实现
@@ -36,10 +36,11 @@ public class ClientMonitorServiceImpl implements IClientMonitorService {
private ClientMonitorMapper clientMonitorMapper; private ClientMonitorMapper clientMonitorMapper;
@Autowired @Autowired
private IClientAccountService clientAccountService; private ClientDeviceMapper clientDeviceMapper;
@Autowired @Autowired
private RedisCache redisCache; private IClientAccountService clientAccountService;
// 线程池用于异步处理日志记录 // 线程池用于异步处理日志记录
private final ExecutorService logExecutor = new ThreadPoolExecutor( private final ExecutorService logExecutor = new ThreadPoolExecutor(
@@ -52,21 +53,16 @@ public class ClientMonitorServiceImpl implements IClientMonitorService {
private final AtomicLong apiCallCounter = new AtomicLong(0); private final AtomicLong apiCallCounter = new AtomicLong(0);
/** /**
* 查询客户端信息列表 - 增强Redis在线状态 * 查询设备列表 - 基于ClientDevice表
*/ */
@Override @Override
public List<ClientInfo> selectClientInfoList(ClientInfo clientInfo) { public List<ClientDevice> selectClientDeviceList(String username) {
logApiCallAsync("selectClientInfoList", null); logApiCallAsync("selectClientDeviceList", null);
List<ClientInfo> clientList = clientMonitorMapper.selectClientInfoList(clientInfo); if (username != null && !username.isEmpty()) {
return clientDeviceMapper.selectByUsername(username);
// 使用Redis检查并更新在线状态 } else {
if (clientList != null) { return clientMonitorMapper.selectOnlineDevices();
for (ClientInfo client : clientList) {
boolean isOnline = isClientOnline(client.getClientId());
client.setOnline(isOnline ? "1" : "0");
}
} }
return clientList;
} }
/** /**
@@ -97,36 +93,25 @@ public class ClientMonitorServiceImpl implements IClientMonitorService {
} }
/** /**
* 查询在线客户端数量 - 基于Redis TTL * 查询在线客户端数量 - 基于数据库
*/ */
@Override @Override
public int selectOnlineClientCount() { public int selectOnlineClientCount() {
logApiCallAsync("selectOnlineClientCount", null); logApiCallAsync("selectOnlineClientCount", null);
try { return clientMonitorMapper.selectOnlineClientCount();
// 通过Redis检查所有心跳key
Collection<String> heartbeatKeys = redisCache.keys("client:heartbeat:*");
int onlineCount = heartbeatKeys != null ? heartbeatKeys.size() : 0;
System.out.println("Redis在线客户端数量: " + onlineCount + ", keys: " + heartbeatKeys);
return onlineCount;
} catch (Exception e) {
System.err.println("Redis查询在线客户端失败: " + e.getMessage());
// Redis查询失败时使用数据库备用方案
return clientMonitorMapper.selectOnlineClientCount();
}
} }
/** /**
* 检查客户端是否在线 - 基于Redis TTL * 检查客户端是否在线 - 基于数据库
*/ */
@Override
public boolean isClientOnline(String clientId) { public boolean isClientOnline(String clientId) {
if (clientId == null || clientId.isEmpty()) { if (clientId == null || clientId.isEmpty()) {
return false; return false;
} }
try { try {
String heartbeatKey = "client:heartbeat:" + clientId; ClientInfo clientInfo = clientMonitorMapper.selectClientInfoByClientId(clientId);
boolean isOnline = redisCache.hasKey(heartbeatKey); return clientInfo != null && "1".equals(clientInfo.getOnline());
System.out.println("检查客户端在线状态: " + clientId + " -> " + isOnline);
return isOnline;
} catch (Exception e) { } catch (Exception e) {
System.err.println("检查客户端在线状态失败: " + e.getMessage()); System.err.println("检查客户端在线状态失败: " + e.getMessage());
return false; return false;
@@ -134,17 +119,17 @@ public class ClientMonitorServiceImpl implements IClientMonitorService {
} }
/** /**
* 获取在线客户端列表 - 基于Redis TTL * 获取在线客户端列表 - 基于数据库
*/ */
@Override
public List<String> getOnlineClientIds() { public List<String> getOnlineClientIds() {
try { try {
Collection<String> heartbeatKeys = redisCache.keys("client:heartbeat:*"); ClientInfo queryParam = new ClientInfo();
queryParam.setOnline("1");
List<ClientInfo> onlineClients = clientMonitorMapper.selectClientInfoList(queryParam);
List<String> onlineClientIds = new ArrayList<>(); List<String> onlineClientIds = new ArrayList<>();
if (heartbeatKeys != null) { for (ClientInfo client : onlineClients) {
for (String key : heartbeatKeys) { onlineClientIds.add(client.getClientId());
String clientId = key.replace("client:heartbeat:", "");
onlineClientIds.add(clientId);
}
} }
return onlineClientIds; return onlineClientIds;
} catch (Exception e) { } catch (Exception e) {
@@ -443,40 +428,6 @@ public class ClientMonitorServiceImpl implements IClientMonitorService {
private void recordClientAuth(String username, String authKey, String clientId, Map<String, Object> clientInfo, String accessToken) { private void recordClientAuth(String username, String authKey, String clientId, Map<String, Object> clientInfo, String accessToken) {
} }
/**
* 记录客户端心跳 - 使用Redis TTL机制
*/
@Override
public void recordHeartbeat(Map<String, Object> heartbeatData) {
try {
String clientId = (String) heartbeatData.get("clientId");
if (clientId == null || clientId.isEmpty()) {
System.err.println("心跳记录失败: clientId为空");
return;
}
// 使用Redis TTL记录心跳600秒TTL10分钟
String heartbeatKey = "client:heartbeat:" + clientId;
Map<String, Object> clientData = new HashMap<>();
clientData.put("clientId", clientId);
clientData.put("timestamp", System.currentTimeMillis());
clientData.put("cpuUsage", parseDouble(heartbeatData.get("cpuUsage"), 0.0));
clientData.put("memoryUsage", parseDouble(heartbeatData.get("memoryUsage"), 0.0));
clientData.put("diskUsage", parseDouble(heartbeatData.get("diskUsage"), 0.0));
clientData.put("networkStatus", heartbeatData.getOrDefault("networkStatus", "normal"));
// 设置Redis keyTTL为600秒
redisCache.setCacheObject(heartbeatKey, clientData, 600, TimeUnit.SECONDS);
System.out.println("心跳记录成功: " + heartbeatKey + ", TTL: 600秒");
// 只在数据库中更新基本信息(减少频率)
clientMonitorMapper.updateClientOnlineStatus(clientId, "1");
} catch (Exception e) {
System.err.println("记录心跳失败: " + e.getMessage());
e.printStackTrace();
}
}
/** /**
* 安全解析Double值 * 安全解析Double值
*/ */
@@ -662,27 +613,6 @@ public class ClientMonitorServiceImpl implements IClientMonitorService {
} }
} }
/**
* 清理过期客户端数据和日志
*/
public void cleanExpiredData() {
try {
// 删除7天前的心跳记录将离线状态的客户端设为离线
clientMonitorMapper.updateExpiredClientsOffline();
// 删除30天前的错误报告
clientMonitorMapper.deleteExpiredErrorReports();
// 删除7天前的事件日志
clientMonitorMapper.deleteExpiredEventLogs();
logger.info("✅ 清理过期数据完成");
} catch (Exception e) {
logger.error("清理过期数据失败: {}", e.getMessage(), e);
}
}
/** /**
* 优雅关闭线程池 * 优雅关闭线程池
@@ -726,303 +656,10 @@ public class ClientMonitorServiceImpl implements IClientMonitorService {
return distribution; return distribution;
} }
/**
* 记录1688爬取风控监控数据
*/
@Override
public void recordAlibaba1688MonitorData(Map<String, Object> monitorData) {
// 记录API调用日志
logApiCall("recordAlibaba1688MonitorData", "记录1688爬取风控数据");
try {
// 获取客户端ID
String clientId = (String) monitorData.get("clientId");
// 如果没有客户端ID则使用系统用户名生成
if (clientId == null || clientId.isEmpty()) {
clientId = "user";
monitorData.put("clientId", clientId);
}
// 尝试查找已有客户端信息,如果没有则不进行严格验证
ClientInfo authInfo = null;
try {
authInfo = clientMonitorMapper.selectClientInfoByClientId(clientId);
} catch (Exception e) {
// 查询失败时忽略,允许匿名上报
}
// 如果客户端不存在,创建一个简单的客户端记录
if (authInfo == null) {
ClientInfo newClientInfo = new ClientInfo();
newClientInfo.setClientId(clientId);
newClientInfo.setUsername((String) monitorData.getOrDefault("username", "user"));
newClientInfo.setIpAddress((String) monitorData.get("ipAddress"));
newClientInfo.setHostname((String) monitorData.get("hostname"));
newClientInfo.setStatus("0");
newClientInfo.setAuthTime(DateUtils.getNowDate());
newClientInfo.setLastActiveTime(DateUtils.getNowDate());
newClientInfo.setOnline("1");
// 添加系统信息
newClientInfo.setOsName((String) monitorData.get("osName"));
newClientInfo.setOsVersion((String) monitorData.get("osVersion"));
newClientInfo.setJavaVersion((String) monitorData.get("javaVersion"));
newClientInfo.setAppVersion((String) monitorData.get("appVersion"));
try {
clientMonitorMapper.insertClientInfo(newClientInfo);
} catch (Exception e) {
// 插入失败时忽略,可能是因为主键冲突(客户端已存在)
logger.warn("插入客户端信息失败,可能客户端已存在: {}", e.getMessage());
}
} else {
// 更新现有客户端的活跃状态
try {
authInfo.setLastActiveTime(DateUtils.getNowDate());
authInfo.setOnline("1");
if (monitorData.get("ipAddress") != null) {
authInfo.setIpAddress((String) monitorData.get("ipAddress"));
}
// 更新系统信息
if (monitorData.get("osName") != null) {
authInfo.setOsName((String) monitorData.get("osName"));
}
if (monitorData.get("osVersion") != null) {
authInfo.setOsVersion((String) monitorData.get("osVersion"));
}
if (monitorData.get("javaVersion") != null) {
authInfo.setJavaVersion((String) monitorData.get("javaVersion"));
}
if (monitorData.get("appVersion") != null) {
authInfo.setAppVersion((String) monitorData.get("appVersion"));
}
clientMonitorMapper.updateClientInfo(authInfo);
} catch (Exception e) {
logger.warn("更新客户端信息失败: {}", e.getMessage());
}
}
// 设置当前时间
monitorData.put("createTime", DateUtils.getNowDate());
// 插入监控数据
clientMonitorMapper.insertAlibaba1688MonitorData(monitorData);
} catch (Exception e) {
throw new RuntimeException("处理1688爬取风控监控数据失败: " + e.getMessage());
}
}
/**
* 查询1688爬取风控监控数据列表
*/
@Override
public List<Map<String, Object>> selectAlibaba1688MonitorList(Map<String, Object> params) {
// 记录API调用日志
logApiCall("selectAlibaba1688MonitorList", null);
return clientMonitorMapper.selectAlibaba1688MonitorList(params);
}
/**
* 获取客户端日志内容
*/
@Override
public Map<String, Object> getClientLogs(String clientId) {
Map<String, Object> result = new HashMap<>();
try {
// 构建日志文件路径
String logDir = System.getProperty("user.dir") + "/logs/clients/";
String logFilePath = logDir + clientId + ".txt";
File logFile = new File(logFilePath);
if (logFile.exists()) {
// 读取日志文件内容
StringBuilder content = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new FileReader(logFile, StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
content.append(line).append("\n");
}
}
result.put("content", content.toString());
result.put("lastUpdate", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(logFile.lastModified())));
} else {
result.put("content", "暂无日志文件");
result.put("lastUpdate", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
// 记录API调用日志
logApiCall("getClientLogs", "客户端ID: " + clientId);
} catch (Exception e) {
result.put("content", "读取日志文件失败: " + e.getMessage());
result.put("lastUpdate", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
return result;
}
/**
* 下载客户端日志文件
*/
@Override
public void downloadClientLogs(String clientId, HttpServletResponse response) {
try {
// 构建日志文件路径
String logDir = System.getProperty("user.dir") + "/logs/clients/";
String logFilePath = logDir + clientId + ".txt";
File logFile = new File(logFilePath);
if (!logFile.exists()) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
// 设置响应头
response.setContentType("text/plain;charset=UTF-8");
response.setHeader("Content-Disposition", "attachment; filename=\"client_" + clientId + "_logs.txt\"");
response.setContentLength((int) logFile.length());
// 输出文件内容
try (FileInputStream fis = new FileInputStream(logFile);
OutputStream os = response.getOutputStream()) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.flush();
}
// 记录API调用日志
logApiCall("downloadClientLogs", "客户端ID: " + clientId);
} catch (Exception e) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
/**
* 保存客户端日志
*/
@Override
public void saveClientLogs(Map<String, Object> logData) {
try {
String clientId = (String) logData.get("clientId");
String logContent = (String) logData.get("logContent");
if (clientId == null || logContent == null) {
throw new RuntimeException("缺少必要的日志参数");
}
// 创建日志目录
String logDir = System.getProperty("user.dir") + "/logs/clients/";
File dir = new File(logDir);
if (!dir.exists()) {
dir.mkdirs();
}
// 写入日志文件
String logFilePath = logDir + clientId + ".txt";
try (FileWriter writer = new FileWriter(logFilePath, StandardCharsets.UTF_8, true)) {
writer.write(logContent);
writer.flush();
}
// 记录API调用日志
logApiCall("saveClientLogs", "客户端ID: " + clientId + ", 日志长度: " + logContent.length());
} catch (Exception e) {
throw new RuntimeException("保存客户端日志失败: " + e.getMessage());
}
}
/**
* 批量保存客户端日志
*/
@Override
public void saveBatchClientLogs(String clientId, List<String> logEntries) {
try {
if (clientId == null || logEntries == null || logEntries.isEmpty()) {
throw new RuntimeException("缺少必要的日志参数");
}
// 创建日志目录
String logDir = System.getProperty("user.dir") + "/logs/clients/";
File dir = new File(logDir);
if (!dir.exists()) {
dir.mkdirs();
}
// 写入日志文件
String logFilePath = logDir + clientId + ".txt";
try (FileWriter writer = new FileWriter(logFilePath, StandardCharsets.UTF_8, true)) {
for (String logEntry : logEntries) {
if (logEntry != null && !logEntry.isEmpty()) {
writer.write(logEntry);
writer.write(System.lineSeparator());
}
}
writer.flush();
}
// 记录API调用日志
logApiCall("saveBatchClientLogs", "客户端ID: " + clientId + ", 日志条目数: " + logEntries.size());
} catch (Exception e) {
throw new RuntimeException("批量保存客户端日志失败: " + e.getMessage());
}
}
/**
* 获取1688爬取风控数据统计
*/
@Override
public Map<String, Object> getAlibaba1688Statistics() {
// 记录API调用日志
logApiCall("getAlibaba1688Statistics", null);
// 获取统计数据
Map<String, Object> stats = clientMonitorMapper.selectAlibaba1688Statistics();
if (stats == null) {
stats = new HashMap<>();
}
// 如果没有数据,设置默认值
if (!stats.containsKey("totalEvents")) {
stats.put("totalEvents", 0);
}
if (!stats.containsKey("mobileBlockedCount")) {
stats.put("mobileBlockedCount", 0);
}
if (!stats.containsKey("desktopBlockedCount")) {
stats.put("desktopBlockedCount", 0);
}
if (!stats.containsKey("avgMobileDuration")) {
stats.put("avgMobileDuration", 0);
}
if (!stats.containsKey("avgDesktopDuration")) {
stats.put("avgDesktopDuration", 0);
}
if (!stats.containsKey("uniqueIpCount")) {
stats.put("uniqueIpCount", 0);
}
return stats;
}
/**
* 从客户端信息中获取IP地址
*/
private String getClientIp(Map<String, Object> clientInfo) {
return clientInfo != null && clientInfo.containsKey("ipAddress")
? (String) clientInfo.get("ipAddress")
: "";
}
/** /**
* 生成会话令牌 * 生成会话令牌
@@ -1043,8 +680,8 @@ public class ClientMonitorServiceImpl implements IClientMonitorService {
* 新增客户端信息 * 新增客户端信息
*/ */
@Override @Override
public int insertClientInfo(ClientInfo clientInfo) { public int insertClientDevice(ClientDevice clientDevice) {
return clientMonitorMapper.insertClientInfo(clientInfo); return clientDeviceMapper.insert(clientDevice);
} }
/** /**
@@ -1062,4 +699,37 @@ public class ClientMonitorServiceImpl implements IClientMonitorService {
public int insertDataReport(ClientDataReport clientDataReport) { public int insertDataReport(ClientDataReport clientDataReport) {
return clientMonitorMapper.insertDataReport(clientDataReport); return clientMonitorMapper.insertDataReport(clientDataReport);
} }
/**
* 查询客户端信息列表
*/
@Override
public List<ClientInfo> selectClientInfoList(ClientInfo clientInfo) {
return clientMonitorMapper.selectClientInfoList(clientInfo);
}
/**
* 清理过期数据
*/
@Override
public void cleanExpiredData() {
try {
// 清理过期的客户端(设置为离线状态)
clientMonitorMapper.updateExpiredClientsOffline();
// 清理过期的设备(设置为离线状态)
clientMonitorMapper.updateExpiredDevicesOffline();
// 删除过期的错误报告
clientMonitorMapper.deleteExpiredErrorReports();
// 删除过期的事件日志
clientMonitorMapper.deleteExpiredEventLogs();
logger.info("过期数据清理完成");
} catch (Exception e) {
logger.error("清理过期数据失败: {}", e.getMessage(), e);
throw new RuntimeException("清理过期数据失败", e);
}
}
} }

View File

@@ -1,3 +0,0 @@
api.datacenterId=1
api.workerId=1
api.domainName=http://xxx.com

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