From 76957e637df8a9fd064689077961bf98df178786 Mon Sep 17 00:00:00 2001 From: ZhangAo Date: Sat, 11 Oct 2025 17:31:56 +0800 Subject: [PATCH] u --- code/link/link-backend/.gitignore | 38 - code/link/link-backend/.idea/.gitignore | 4 +- code/link/link-backend/.idea/compiler.xml | 18 + code/link/link-backend/.idea/encodings.xml | 1 - .../link-backend/.idea/jarRepositories.xml | 30 + code/link/link-backend/.idea/misc.xml | 4 +- .../link-backend/.idea/runConfigurations.xml | 10 + code/link/link-backend/.idea/vcs.xml | 1 + code/link/link-backend/pom.xml | 255 +- .../main/java/com/link/LinkApplication.java | 9 +- .../com/link/api/config/WebCorsConfig.java | 33 - .../config/datasource/DataSourceConfig.java | 22 - .../config/datasource/DataSourceManager.java | 231 - .../api/config/datasource/DruidConfig.java | 67 - .../datasource/LoadDataSourceRunner.java | 21 - .../constants/ResponseStatusConstants.java | 28 - .../api/context/DataSourceContextHolder.java | 17 - .../link/api/controller/AuthController.java | 40 + .../controller/ColumnConfigController.java | 59 + .../link/api/controller/ConfigController.java | 20 - .../api/controller/DataListController.java | 175 + .../api/controller/DataObjController.java | 91 + .../api/controller/DataSaveController.java | 112 + .../com/link/api/controller/FilterConfig.java | 25 + .../controller/SearchConfigController.java | 32 + .../api/controller/StudentController.java | 139 + .../link/api/controller/TestController.java | 53 - .../link/api/controller/TreeController.java | 25 + .../com/link/api/controller/WebConfig.java | 16 + .../java/com/link/api/enums/ResponseEnum.java | 18 - .../com/link/api/exception/BaseException.java | 59 - .../api/exception/GlobalExceptionHandler.java | 45 - .../api/exception/auth/AuthException.java | 19 - .../com/link/api/manager/UserManager.java | 51 + .../java/com/link/api/model/ChatMessage.java | 59 + .../com/link/api/model/ExceptionEntity.java | 45 - .../main/java/com/link/api/model/Result.java | 126 - .../main/java/com/link/api/model/Student.java | 41 + .../java/com/link/api/model/UserInfo.java | 33 + .../api/repository/MultiTenantUserDao.java | 53 + .../com/link/api/repository/StudentDAO.java | 36 + .../link/api/repository/StudentDaoJDBC.java | 88 + .../link/api/repository/UserRepository.java | 42 + .../java/com/link/api/util/BatchColumn.java | 126 + .../java/com/link/api/util/BatchTable.java | 269 ++ .../com/link/api/util/BatchTableDelete.java | 198 + .../java/com/link/api/util/BatchTableV2.java | 280 ++ .../link/api/util/BatchTableV220250829.java | 201 + .../com/link/api/util/BatchTableV2bk.java | 238 + .../java/com/link/api/util/BatchTableV3.java | 209 + .../main/java/com/link/api/util/BuildSql.java | 186 + .../com/link/api/util/ConnectionManager.java | 19 + .../link/api/util/DruidDataSourceManager.java | 61 + .../java/com/link/api/util/JsonIMUtil.java | 26 + .../java/com/link/api/util/JsonUtils.java | 231 + .../api/util/JwtAuthenticationFilter.java | 78 + .../java/com/link/api/util/JwtPayload.java | 29 + .../main/java/com/link/api/util/JwtUtil.java | 38 + .../com/link/api/util/PaginationUtils.java | 474 ++ .../com/link/api/util/QueryByPkUtils.java | 77 + .../com/link/api/util/QueryTreeUtils.java | 66 + .../main/java/com/link/api/util/Result.java | 40 + .../com/link/api/util/TableDataService.java | 176 + .../main/java/com/link/api/util/Tools.java | 121 + .../java/com/link/api/util/UserContext.java | 47 + .../com/link/api/utils/ExceptionUtils.java | 98 - .../java/com/link/api/utils/JsonUtils.java | 40 - .../com/link/api/utils/SnowflakeIdUtils.java | 89 - .../java/com/link/api/utils/SpringUtils.java | 157 - .../src/main/java/com/link/im/ImServer.java | 1 + .../java/com/link/im/ImWebSocketHandler.java | 120 - .../link/im/handler/ImWebSocketHandler.java | 70 + .../com/link/im/manager/GroupManager.java | 71 + .../link/im/manager/UserChannelManager.java | 64 + .../com/link/im/manager/WriteManager.java | 57 + .../java/com/link/im/model/ChatMessage.java | 40 + .../src/main/resources/application.properties | 17 + .../src/main/resources/application.yml | 25 - .../src/main/resources/backup/application.yml | 8 + .../src/main/resources/datasource.yml | 22 - .../main/resources/dbconfigs/G3HD.properties | 4 + .../main/resources/dbconfigs/G3HD2.properties | 4 + .../src/main/resources/spy.properties | 12 + .../link-backend/src/main/resources/test.html | 322 +- .../target/classes/application.properties | 17 + .../target/classes/backup/application.yml | 8 + .../classes/com/link/LinkApplication.class | Bin 0 -> 742 bytes .../link/api/controller/AuthController.class | Bin 0 -> 2295 bytes .../controller/ColumnConfigController.class | Bin 0 -> 2896 bytes .../api/controller/DataListController.class | Bin 0 -> 6429 bytes .../api/controller/DataObjController.class | Bin 0 -> 4954 bytes .../api/controller/DataSaveController$1.class | Bin 0 -> 819 bytes .../api/controller/DataSaveController.class | Bin 0 -> 5467 bytes .../link/api/controller/FilterConfig.class | Bin 0 -> 1467 bytes .../controller/SearchConfigController.class | Bin 0 -> 2229 bytes .../api/controller/StudentController.class | Bin 0 -> 5578 bytes .../link/api/controller/TreeController.class | Bin 0 -> 1944 bytes .../com/link/api/controller/WebConfig.class | Bin 0 -> 1540 bytes .../com/link/api/manager/UserManager.class | Bin 0 -> 2418 bytes .../com/link/api/model/ChatMessage.class | Bin 0 -> 2917 bytes .../classes/com/link/api/model/Student.class | Bin 0 -> 1232 bytes .../classes/com/link/api/model/UserInfo.class | Bin 0 -> 1022 bytes .../api/repository/MultiTenantUserDao.class | Bin 0 -> 5365 bytes .../com/link/api/repository/StudentDAO.class | Bin 0 -> 2006 bytes .../link/api/repository/StudentDaoJDBC.class | Bin 0 -> 5863 bytes .../link/api/repository/UserRepository.class | Bin 0 -> 3105 bytes .../com/link/api/util/BatchColumn.class | Bin 0 -> 4816 bytes .../com/link/api/util/BatchTable$1.class | Bin 0 -> 621 bytes .../com/link/api/util/BatchTable$2.class | Bin 0 -> 603 bytes .../com/link/api/util/BatchTable.class | Bin 0 -> 14152 bytes .../com/link/api/util/BatchTableDelete.class | Bin 0 -> 13012 bytes .../com/link/api/util/BatchTableV2.class | Bin 0 -> 14669 bytes .../link/api/util/BatchTableV220250829.class | Bin 0 -> 10688 bytes .../com/link/api/util/BatchTableV2bk.class | Bin 0 -> 12191 bytes .../com/link/api/util/BatchTableV3.class | Bin 0 -> 10891 bytes .../classes/com/link/api/util/BuildSql.class | Bin 0 -> 8022 bytes .../com/link/api/util/ConnectionManager.class | Bin 0 -> 718 bytes .../api/util/DruidDataSourceManager.class | Bin 0 -> 3814 bytes .../com/link/api/util/JsonIMUtil.class | Bin 0 -> 1343 bytes .../classes/com/link/api/util/JsonUtils.class | Bin 0 -> 12043 bytes .../api/util/JwtAuthenticationFilter.class | Bin 0 -> 4331 bytes .../com/link/api/util/JwtPayload.class | Bin 0 -> 933 bytes .../classes/com/link/api/util/JwtUtil.class | Bin 0 -> 2349 bytes .../com/link/api/util/PaginationUtils.class | Bin 0 -> 20185 bytes .../com/link/api/util/QueryByPkUtils.class | Bin 0 -> 4817 bytes .../com/link/api/util/QueryTreeUtils.class | Bin 0 -> 4178 bytes .../classes/com/link/api/util/Result.class | Bin 0 -> 2338 bytes .../util/TableDataService$FieldConfig.class | Bin 0 -> 620 bytes .../com/link/api/util/TableDataService.class | Bin 0 -> 8432 bytes .../com/link/api/util/Tools$Snowflake.class | Bin 0 -> 1989 bytes .../classes/com/link/api/util/Tools.class | Bin 0 -> 1585 bytes .../com/link/api/util/UserContext.class | Bin 0 -> 1373 bytes .../classes/com/link/im/ImServer$1.class | Bin 0 -> 2099 bytes .../target/classes/com/link/im/ImServer.class | Bin 0 -> 4370 bytes .../link/im/handler/ImWebSocketHandler.class | Bin 0 -> 4096 bytes .../com/link/im/manager/GroupManager.class | Bin 0 -> 4141 bytes .../link/im/manager/UserChannelManager.class | Bin 0 -> 3262 bytes .../com/link/im/manager/WriteManager.class | Bin 0 -> 2718 bytes .../com/link/im/model/ChatMessage.class | Bin 0 -> 1234 bytes .../target/classes/dbconfigs/G3HD.properties | 4 + .../target/classes/dbconfigs/G3HD2.properties | 4 + .../target/classes/spy.properties | 12 + .../link-backend/target/classes/test.html | 288 ++ .../link/link-fronted/.vscode/extensions.json | 3 - code/link/link-fronted/README.md | 5 - code/link/link-fronted/package.json | 22 - code/link/link-fronted/src/main.js | 6 - code/link/link-fronted/src/router/index.js | 21 - code/link/link-fronted/src/style.css | 0 code/link/link-fronted/src/views/im/index.vue | 13 - code/link/link-fronted/test.html | 329 -- .../.gitignore | 2 - code/link/link-frontend/.vscode/settings.json | 51 + .../link-frontend/.vscode/vue3.code-snippets | 16 + .../eslint.config.js | 4 +- .../index.html | 5 +- code/link/link-frontend/package-lock.json | 4022 +++++++++++++++++ code/link/link-frontend/package.json | 36 + .../pnpm-lock.yaml | 1118 ++++- code/link/link-frontend/postcss.config.cjs | 7 + code/link/link-frontend/public/Thumbs.db | Bin 0 -> 3584 bytes code/link/link-frontend/public/logo.ico | Bin 0 -> 2324 bytes .../public/vite.svg | 0 .../src/App.vue | 3 - .../src/assets/vue.svg | 0 .../link-frontend/src/components/avatar.vue | 59 + .../link-frontend/src/components/icon.vue | 58 + code/link/link-frontend/src/main.js | 18 + code/link/link-frontend/src/router/index.js | 32 + .../link-frontend/src/stores/userStore.js | 43 + code/link/link-frontend/src/style.css | 11 + code/link/link-frontend/src/utils/axios.js | 34 + .../link-frontend/src/utils/dataDiffUtils.js | 493 ++ code/link/link-frontend/src/utils/dodata.js | 203 + .../link-frontend/src/utils/feeSummary.js | 50 + .../src/utils/useTableHeaderSortable.js | 24 + .../link/link-frontend/src/views/im/index.vue | 227 + code/link/link-frontend/test.html | 125 + .../vite.config.js | 4 +- 179 files changed, 11901 insertions(+), 2178 deletions(-) delete mode 100644 code/link/link-backend/.gitignore create mode 100644 code/link/link-backend/.idea/compiler.xml create mode 100644 code/link/link-backend/.idea/jarRepositories.xml create mode 100644 code/link/link-backend/.idea/runConfigurations.xml delete mode 100644 code/link/link-backend/src/main/java/com/link/api/config/WebCorsConfig.java delete mode 100644 code/link/link-backend/src/main/java/com/link/api/config/datasource/DataSourceConfig.java delete mode 100644 code/link/link-backend/src/main/java/com/link/api/config/datasource/DataSourceManager.java delete mode 100644 code/link/link-backend/src/main/java/com/link/api/config/datasource/DruidConfig.java delete mode 100644 code/link/link-backend/src/main/java/com/link/api/config/datasource/LoadDataSourceRunner.java delete mode 100644 code/link/link-backend/src/main/java/com/link/api/constants/ResponseStatusConstants.java delete mode 100644 code/link/link-backend/src/main/java/com/link/api/context/DataSourceContextHolder.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/controller/AuthController.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/controller/ColumnConfigController.java delete mode 100644 code/link/link-backend/src/main/java/com/link/api/controller/ConfigController.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/controller/DataListController.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/controller/DataObjController.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/controller/DataSaveController.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/controller/FilterConfig.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/controller/SearchConfigController.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/controller/StudentController.java delete mode 100644 code/link/link-backend/src/main/java/com/link/api/controller/TestController.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/controller/TreeController.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/controller/WebConfig.java delete mode 100644 code/link/link-backend/src/main/java/com/link/api/enums/ResponseEnum.java delete mode 100644 code/link/link-backend/src/main/java/com/link/api/exception/BaseException.java delete mode 100644 code/link/link-backend/src/main/java/com/link/api/exception/GlobalExceptionHandler.java delete mode 100644 code/link/link-backend/src/main/java/com/link/api/exception/auth/AuthException.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/manager/UserManager.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/model/ChatMessage.java delete mode 100644 code/link/link-backend/src/main/java/com/link/api/model/ExceptionEntity.java delete mode 100644 code/link/link-backend/src/main/java/com/link/api/model/Result.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/model/Student.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/model/UserInfo.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/repository/MultiTenantUserDao.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/repository/StudentDAO.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/repository/StudentDaoJDBC.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/repository/UserRepository.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/util/BatchColumn.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/util/BatchTable.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/util/BatchTableDelete.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/util/BatchTableV2.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/util/BatchTableV220250829.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/util/BatchTableV2bk.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/util/BatchTableV3.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/util/BuildSql.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/util/ConnectionManager.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/util/DruidDataSourceManager.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/util/JsonIMUtil.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/util/JsonUtils.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/util/JwtAuthenticationFilter.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/util/JwtPayload.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/util/JwtUtil.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/util/PaginationUtils.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/util/QueryByPkUtils.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/util/QueryTreeUtils.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/util/Result.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/util/TableDataService.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/util/Tools.java create mode 100644 code/link/link-backend/src/main/java/com/link/api/util/UserContext.java delete mode 100644 code/link/link-backend/src/main/java/com/link/api/utils/ExceptionUtils.java delete mode 100644 code/link/link-backend/src/main/java/com/link/api/utils/JsonUtils.java delete mode 100644 code/link/link-backend/src/main/java/com/link/api/utils/SnowflakeIdUtils.java delete mode 100644 code/link/link-backend/src/main/java/com/link/api/utils/SpringUtils.java delete mode 100644 code/link/link-backend/src/main/java/com/link/im/ImWebSocketHandler.java create mode 100644 code/link/link-backend/src/main/java/com/link/im/handler/ImWebSocketHandler.java create mode 100644 code/link/link-backend/src/main/java/com/link/im/manager/GroupManager.java create mode 100644 code/link/link-backend/src/main/java/com/link/im/manager/UserChannelManager.java create mode 100644 code/link/link-backend/src/main/java/com/link/im/manager/WriteManager.java create mode 100644 code/link/link-backend/src/main/java/com/link/im/model/ChatMessage.java create mode 100644 code/link/link-backend/src/main/resources/application.properties delete mode 100644 code/link/link-backend/src/main/resources/application.yml create mode 100644 code/link/link-backend/src/main/resources/backup/application.yml delete mode 100644 code/link/link-backend/src/main/resources/datasource.yml create mode 100644 code/link/link-backend/src/main/resources/dbconfigs/G3HD.properties create mode 100644 code/link/link-backend/src/main/resources/dbconfigs/G3HD2.properties create mode 100644 code/link/link-backend/src/main/resources/spy.properties create mode 100644 code/link/link-backend/target/classes/application.properties create mode 100644 code/link/link-backend/target/classes/backup/application.yml create mode 100644 code/link/link-backend/target/classes/com/link/LinkApplication.class create mode 100644 code/link/link-backend/target/classes/com/link/api/controller/AuthController.class create mode 100644 code/link/link-backend/target/classes/com/link/api/controller/ColumnConfigController.class create mode 100644 code/link/link-backend/target/classes/com/link/api/controller/DataListController.class create mode 100644 code/link/link-backend/target/classes/com/link/api/controller/DataObjController.class create mode 100644 code/link/link-backend/target/classes/com/link/api/controller/DataSaveController$1.class create mode 100644 code/link/link-backend/target/classes/com/link/api/controller/DataSaveController.class create mode 100644 code/link/link-backend/target/classes/com/link/api/controller/FilterConfig.class create mode 100644 code/link/link-backend/target/classes/com/link/api/controller/SearchConfigController.class create mode 100644 code/link/link-backend/target/classes/com/link/api/controller/StudentController.class create mode 100644 code/link/link-backend/target/classes/com/link/api/controller/TreeController.class create mode 100644 code/link/link-backend/target/classes/com/link/api/controller/WebConfig.class create mode 100644 code/link/link-backend/target/classes/com/link/api/manager/UserManager.class create mode 100644 code/link/link-backend/target/classes/com/link/api/model/ChatMessage.class create mode 100644 code/link/link-backend/target/classes/com/link/api/model/Student.class create mode 100644 code/link/link-backend/target/classes/com/link/api/model/UserInfo.class create mode 100644 code/link/link-backend/target/classes/com/link/api/repository/MultiTenantUserDao.class create mode 100644 code/link/link-backend/target/classes/com/link/api/repository/StudentDAO.class create mode 100644 code/link/link-backend/target/classes/com/link/api/repository/StudentDaoJDBC.class create mode 100644 code/link/link-backend/target/classes/com/link/api/repository/UserRepository.class create mode 100644 code/link/link-backend/target/classes/com/link/api/util/BatchColumn.class create mode 100644 code/link/link-backend/target/classes/com/link/api/util/BatchTable$1.class create mode 100644 code/link/link-backend/target/classes/com/link/api/util/BatchTable$2.class create mode 100644 code/link/link-backend/target/classes/com/link/api/util/BatchTable.class create mode 100644 code/link/link-backend/target/classes/com/link/api/util/BatchTableDelete.class create mode 100644 code/link/link-backend/target/classes/com/link/api/util/BatchTableV2.class create mode 100644 code/link/link-backend/target/classes/com/link/api/util/BatchTableV220250829.class create mode 100644 code/link/link-backend/target/classes/com/link/api/util/BatchTableV2bk.class create mode 100644 code/link/link-backend/target/classes/com/link/api/util/BatchTableV3.class create mode 100644 code/link/link-backend/target/classes/com/link/api/util/BuildSql.class create mode 100644 code/link/link-backend/target/classes/com/link/api/util/ConnectionManager.class create mode 100644 code/link/link-backend/target/classes/com/link/api/util/DruidDataSourceManager.class create mode 100644 code/link/link-backend/target/classes/com/link/api/util/JsonIMUtil.class create mode 100644 code/link/link-backend/target/classes/com/link/api/util/JsonUtils.class create mode 100644 code/link/link-backend/target/classes/com/link/api/util/JwtAuthenticationFilter.class create mode 100644 code/link/link-backend/target/classes/com/link/api/util/JwtPayload.class create mode 100644 code/link/link-backend/target/classes/com/link/api/util/JwtUtil.class create mode 100644 code/link/link-backend/target/classes/com/link/api/util/PaginationUtils.class create mode 100644 code/link/link-backend/target/classes/com/link/api/util/QueryByPkUtils.class create mode 100644 code/link/link-backend/target/classes/com/link/api/util/QueryTreeUtils.class create mode 100644 code/link/link-backend/target/classes/com/link/api/util/Result.class create mode 100644 code/link/link-backend/target/classes/com/link/api/util/TableDataService$FieldConfig.class create mode 100644 code/link/link-backend/target/classes/com/link/api/util/TableDataService.class create mode 100644 code/link/link-backend/target/classes/com/link/api/util/Tools$Snowflake.class create mode 100644 code/link/link-backend/target/classes/com/link/api/util/Tools.class create mode 100644 code/link/link-backend/target/classes/com/link/api/util/UserContext.class create mode 100644 code/link/link-backend/target/classes/com/link/im/ImServer$1.class create mode 100644 code/link/link-backend/target/classes/com/link/im/ImServer.class create mode 100644 code/link/link-backend/target/classes/com/link/im/handler/ImWebSocketHandler.class create mode 100644 code/link/link-backend/target/classes/com/link/im/manager/GroupManager.class create mode 100644 code/link/link-backend/target/classes/com/link/im/manager/UserChannelManager.class create mode 100644 code/link/link-backend/target/classes/com/link/im/manager/WriteManager.class create mode 100644 code/link/link-backend/target/classes/com/link/im/model/ChatMessage.class create mode 100644 code/link/link-backend/target/classes/dbconfigs/G3HD.properties create mode 100644 code/link/link-backend/target/classes/dbconfigs/G3HD2.properties create mode 100644 code/link/link-backend/target/classes/spy.properties create mode 100644 code/link/link-backend/target/classes/test.html delete mode 100644 code/link/link-fronted/.vscode/extensions.json delete mode 100644 code/link/link-fronted/README.md delete mode 100644 code/link/link-fronted/package.json delete mode 100644 code/link/link-fronted/src/main.js delete mode 100644 code/link/link-fronted/src/router/index.js delete mode 100644 code/link/link-fronted/src/style.css delete mode 100644 code/link/link-fronted/src/views/im/index.vue delete mode 100644 code/link/link-fronted/test.html rename code/link/{link-fronted => link-frontend}/.gitignore (86%) create mode 100644 code/link/link-frontend/.vscode/settings.json create mode 100644 code/link/link-frontend/.vscode/vue3.code-snippets rename code/link/{link-fronted => link-frontend}/eslint.config.js (72%) rename code/link/{link-fronted => link-frontend}/index.html (67%) create mode 100644 code/link/link-frontend/package-lock.json create mode 100644 code/link/link-frontend/package.json rename code/link/{link-fronted => link-frontend}/pnpm-lock.yaml (77%) create mode 100644 code/link/link-frontend/postcss.config.cjs create mode 100644 code/link/link-frontend/public/Thumbs.db create mode 100644 code/link/link-frontend/public/logo.ico rename code/link/{link-fronted => link-frontend}/public/vite.svg (100%) rename code/link/{link-fronted => link-frontend}/src/App.vue (72%) rename code/link/{link-fronted => link-frontend}/src/assets/vue.svg (100%) create mode 100644 code/link/link-frontend/src/components/avatar.vue create mode 100644 code/link/link-frontend/src/components/icon.vue create mode 100644 code/link/link-frontend/src/main.js create mode 100644 code/link/link-frontend/src/router/index.js create mode 100644 code/link/link-frontend/src/stores/userStore.js create mode 100644 code/link/link-frontend/src/style.css create mode 100644 code/link/link-frontend/src/utils/axios.js create mode 100644 code/link/link-frontend/src/utils/dataDiffUtils.js create mode 100644 code/link/link-frontend/src/utils/dodata.js create mode 100644 code/link/link-frontend/src/utils/feeSummary.js create mode 100644 code/link/link-frontend/src/utils/useTableHeaderSortable.js create mode 100644 code/link/link-frontend/src/views/im/index.vue create mode 100644 code/link/link-frontend/test.html rename code/link/{link-fronted => link-frontend}/vite.config.js (80%) diff --git a/code/link/link-backend/.gitignore b/code/link/link-backend/.gitignore deleted file mode 100644 index 5ff6309b..00000000 --- a/code/link/link-backend/.gitignore +++ /dev/null @@ -1,38 +0,0 @@ -target/ -!.mvn/wrapper/maven-wrapper.jar -!**/src/main/**/target/ -!**/src/test/**/target/ - -### IntelliJ IDEA ### -.idea/modules.xml -.idea/jarRepositories.xml -.idea/compiler.xml -.idea/libraries/ -*.iws -*.iml -*.ipr - -### Eclipse ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -build/ -!**/src/main/**/build/ -!**/src/test/**/build/ - -### VS Code ### -.vscode/ - -### Mac OS ### -.DS_Store \ No newline at end of file diff --git a/code/link/link-backend/.idea/.gitignore b/code/link/link-backend/.idea/.gitignore index 13566b81..73f69e09 100644 --- a/code/link/link-backend/.idea/.gitignore +++ b/code/link/link-backend/.idea/.gitignore @@ -1,8 +1,8 @@ # Default ignored files /shelf/ /workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ # Datasource local storage ignored files /dataSources/ /dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/code/link/link-backend/.idea/compiler.xml b/code/link/link-backend/.idea/compiler.xml new file mode 100644 index 00000000..37f12852 --- /dev/null +++ b/code/link/link-backend/.idea/compiler.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/code/link/link-backend/.idea/encodings.xml b/code/link/link-backend/.idea/encodings.xml index aa00ffab..63e90019 100644 --- a/code/link/link-backend/.idea/encodings.xml +++ b/code/link/link-backend/.idea/encodings.xml @@ -2,6 +2,5 @@ - \ No newline at end of file diff --git a/code/link/link-backend/.idea/jarRepositories.xml b/code/link/link-backend/.idea/jarRepositories.xml new file mode 100644 index 00000000..841afb50 --- /dev/null +++ b/code/link/link-backend/.idea/jarRepositories.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/code/link/link-backend/.idea/misc.xml b/code/link/link-backend/.idea/misc.xml index 132404bc..d5cd6143 100644 --- a/code/link/link-backend/.idea/misc.xml +++ b/code/link/link-backend/.idea/misc.xml @@ -8,7 +8,5 @@ - - - + \ No newline at end of file diff --git a/code/link/link-backend/.idea/runConfigurations.xml b/code/link/link-backend/.idea/runConfigurations.xml new file mode 100644 index 00000000..797acea5 --- /dev/null +++ b/code/link/link-backend/.idea/runConfigurations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/code/link/link-backend/.idea/vcs.xml b/code/link/link-backend/.idea/vcs.xml index c2365ab1..ba43f699 100644 --- a/code/link/link-backend/.idea/vcs.xml +++ b/code/link/link-backend/.idea/vcs.xml @@ -2,5 +2,6 @@ + \ No newline at end of file diff --git a/code/link/link-backend/pom.xml b/code/link/link-backend/pom.xml index 019a253e..676dcab2 100644 --- a/code/link/link-backend/pom.xml +++ b/code/link/link-backend/pom.xml @@ -1,206 +1,115 @@ - + 4.0.0 - - cn.g3soft + + org.springframework.boot + spring-boot-starter-parent + 2.7.18 + + + com.example link-backend - 1.0-SNAPSHOT + 0.0.1-SNAPSHOT + backend + backend 1.8 - UTF-8 - UTF-8 - 3.1.1 - 2.5.15 - 1.2.23 - 3.10.3 - 1.3.1 - 3.12.0 - - - 9.0.96 - 1.2.13 - 5.3.39 + 4.1.86.Final - - - - - org.springframework.boot - spring-boot-dependencies - ${spring-boot.version} - pom - import - - - - org.springframework - spring-framework-bom - ${spring-framework.version} - pom - import - - - - - - com.squareup.okhttp3 - okhttp - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.boot spring-boot-starter-web - - - - org.springframework.boot - spring-boot-starter-aop - - - - - org.projectlombok - lombok - - - - - org.springframework.boot - spring-boot-starter-validation - - - - - ch.qos.logback - logback-core - ${logback.version} - - - - ch.qos.logback - logback-classic - ${logback.version} - - - - - org.apache.tomcat.embed - tomcat-embed-core - ${tomcat.version} - - - - org.apache.tomcat.embed - tomcat-embed-el - ${tomcat.version} - - - - org.apache.tomcat.embed - tomcat-embed-websocket - ${tomcat.version} - - - - - org.yaml - snakeyaml - - - - - com.alibaba - druid-spring-boot-starter - ${druid.version} - - - - - com.auth0 - java-jwt - ${jwt.version} - - - - net.sourceforge.jtds - jtds - ${jtds.version} - - org.springframework.boot spring-boot-starter-jdbc - - org.apache.commons - commons-lang3 - ${commons.version} + com.alibaba + druid + 1.2.18 + + + com.microsoft.sqlserver + mssql-jdbc + 6.2.2.jre8 + + + io.jsonwebtoken + jjwt + 0.9.1 + + + com.fasterxml.jackson.core + jackson-databind + + + + p6spy + p6spy + 3.9.1 + + + org.springframework.boot + spring-boot-starter-logging + + org.springframework.boot + spring-boot-devtools + true + + + + org.junit.jupiter + junit-jupiter + 5.9.3 + test + + + junit + junit + + io.netty netty-all - 4.1.112.Final + ${netty.version} + - - jar - - - - public - aliyun nexus - https://maven.aliyun.com/repository/public - - true - - - - - - - public - aliyun nexus - https://maven.aliyun.com/repository/public - - true - - - false - - - - org.springframework.boot spring-boot-maven-plugin - 2.5.15 - - true - - - - - repackage - - - - \ No newline at end of file + + + + + aliyun + https://maven.aliyun.com/repository/public + + true + + + true + + + + + + + aliyun + https://maven.aliyun.com/repository/public + + + + diff --git a/code/link/link-backend/src/main/java/com/link/LinkApplication.java b/code/link/link-backend/src/main/java/com/link/LinkApplication.java index ca9637eb..19884ded 100644 --- a/code/link/link-backend/src/main/java/com/link/LinkApplication.java +++ b/code/link/link-backend/src/main/java/com/link/LinkApplication.java @@ -1,16 +1,11 @@ package com.link; -import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; -@SpringBootApplication(exclude = { - DataSourceAutoConfiguration.class, - DruidDataSourceAutoConfigure.class , -}) +@SpringBootApplication public class LinkApplication { public static void main(String[] args) { - SpringApplication.run(LinkApplication.class,args); + SpringApplication.run(LinkApplication.class); } } diff --git a/code/link/link-backend/src/main/java/com/link/api/config/WebCorsConfig.java b/code/link/link-backend/src/main/java/com/link/api/config/WebCorsConfig.java deleted file mode 100644 index 8dc340e4..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/config/WebCorsConfig.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.link.api.config; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import org.springframework.web.filter.CorsFilter; - -@Configuration -public class WebCorsConfig { - @Bean - public CorsFilter corsFilter() { - // 1.创建 CORS 配置对象 - CorsConfiguration config = new CorsConfiguration(); - // 支持域 - config.addAllowedOriginPattern("*"); - // 是否发送 Cookie - config.setAllowCredentials(true); - // 支持请求方式 - config.addAllowedMethod("*"); - // 允许的原始请求头部信息 - config.addAllowedHeader("*"); - // 暴露的头部信息 - config.addExposedHeader("*"); - // 有效期 1800秒,设置预检请求(OPTIONS请求)的缓存时间,1800秒意味着客户端可以缓存这个设置1800秒 - config.setMaxAge(1800L); - // 2.添加地址映射 - UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource(); - corsConfigurationSource.registerCorsConfiguration("/**", config); - // 3.返回 CorsFilter 对象 - return new CorsFilter(corsConfigurationSource); - } -} diff --git a/code/link/link-backend/src/main/java/com/link/api/config/datasource/DataSourceConfig.java b/code/link/link-backend/src/main/java/com/link/api/config/datasource/DataSourceConfig.java deleted file mode 100644 index 0c6b2096..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/config/datasource/DataSourceConfig.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.link.api.config.datasource; - -import lombok.*; - -import java.util.Map; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class DataSourceConfig { - private Map datasource; - - @Setter - @Getter - public static class DbConfig { - // getters & setters - private String url; - private String username; - private String password; - private String driverClassName; - } -} diff --git a/code/link/link-backend/src/main/java/com/link/api/config/datasource/DataSourceManager.java b/code/link/link-backend/src/main/java/com/link/api/config/datasource/DataSourceManager.java deleted file mode 100644 index 449f4dee..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/config/datasource/DataSourceManager.java +++ /dev/null @@ -1,231 +0,0 @@ -package com.link.api.config.datasource; - -import com.link.api.context.DataSourceContextHolder; -import com.alibaba.druid.filter.Filter; -import com.alibaba.druid.pool.DruidDataSource; -import com.alibaba.druid.wall.WallConfig; -import com.alibaba.druid.wall.WallFilter; -import com.zaxxer.hikari.HikariConfig; -import com.zaxxer.hikari.HikariDataSource; -import org.springframework.core.io.Resource; -import org.springframework.core.io.ResourceLoader; -import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; -import org.springframework.stereotype.Component; -import org.yaml.snakeyaml.Yaml; - -import java.io.InputStream; -import java.sql.DriverManager; -import java.sql.SQLException; -import java.util.*; - -@Component -public class DataSourceManager extends AbstractRoutingDataSource { - - private static final String EXTERNAL_CONFIG_PATH = "file:config/datasource.yml"; - private static final String CLASSPATH_CONFIG_PATH = "classpath:datasource.yml"; - - @javax.annotation.Resource - private ResourceLoader resourceLoader; - - private final Map targetDataSourceMap = new HashMap<>(); - - public DataSourceManager() { - super.setTargetDataSources(targetDataSourceMap); - } - - @Override - protected Object determineCurrentLookupKey() { - return DataSourceContextHolder.getDataSourceKey(); - } - - public void loadDatasource() { - try { - // 从配置文件中解析出数据源配置对象 - DataSourceConfig config = resolveConfigResource(); - Map newDsMap = config.getDatasource(); - Set newKeys = newDsMap.keySet(); - - // 关闭并移除已经被删除(配置文件中不再存在)的旧数据源 - Set existingKeys = new HashSet<>(targetDataSourceMap.keySet()); - for (Object key : existingKeys) { - // 如果旧数据源中有但新配置中没有,说明该数据源被删除了 - if (!newKeys.contains((String) key)) { - // 移除旧数据源并关闭连接池资源 - DruidDataSource ds = (DruidDataSource) targetDataSourceMap.remove(key); - ds.close(); - System.out.println("移除并关闭旧数据源 [" + key + "]"); - } - } - - // 遍历新配置,进行新增或更新处理 - for (Map.Entry entry : newDsMap.entrySet()) { - String key = entry.getKey(); - DataSourceConfig.DbConfig newConfig = entry.getValue(); - - // 获取已有的数据源实例(如果存在) - DruidDataSource existingDS = (DruidDataSource) targetDataSourceMap.get(key); - - // 如果已有数据源配置和新配置相同,则跳过初始化 - if (existingDS != null && isSameConfig(existingDS, newConfig)) { - System.out.println("数据源 [" + key + "] 配置未变更,跳过初始化"); - continue; - } - - // 如果已有数据源但配置不同,关闭旧数据源 - if (existingDS != null) { - existingDS.close(); - System.out.println("关闭已变更数据源 [" + key + "]"); - } - - try { - // 预先验证驱动和连接是否可用,防止错误配置 - Class.forName(newConfig.getDriverClassName()); - DriverManager.getConnection(newConfig.getUrl(), newConfig.getUsername(), newConfig.getPassword()).close(); - - // 创建并初始化新的 DruidDataSource 实例 -// DruidDataSource newDS = buildDruidDataSource(newConfig); - // 创建并初始化新的 HikariDataSource 实例 - HikariDataSource newDS = buildHikariDataSource(newConfig); - - // 放入数据源映射中,替换旧数据源(或新增) - targetDataSourceMap.put(key, newDS); - System.out.println("数据源 [" + key + "] 初始化成功"); - - } catch (Exception e) { - // 初始化失败时输出错误日志,不影响其他数据源加载 - System.err.printf("数据源 [%s] 初始化失败:%s%n", key, e.getMessage()); - } - } - - // 通知父类更新数据源路由配置 - super.setTargetDataSources(targetDataSourceMap); - super.afterPropertiesSet(); - - } catch (Exception e) { - // 捕获整个过程中的异常,打印错误日志 - System.err.println("加载数据源配置异常:" + e.getMessage()); - } - } - - private boolean isSameConfig(DruidDataSource oldDS, DataSourceConfig.DbConfig newConfig) { - return Objects.equals(oldDS.getUrl(), newConfig.getUrl()) && - Objects.equals(oldDS.getUsername(), newConfig.getUsername()) && - Objects.equals(oldDS.getPassword(), newConfig.getPassword()) && - Objects.equals(oldDS.getDriverClassName(), newConfig.getDriverClassName()); - } - - private DataSourceConfig resolveConfigResource() throws Exception { - Resource resource = resourceLoader.getResource(EXTERNAL_CONFIG_PATH); - if (!resource.exists()) { - resource = resourceLoader.getResource(CLASSPATH_CONFIG_PATH); - } - if (!resource.exists()) { - throw new Exception("配置文件未找到:" + EXTERNAL_CONFIG_PATH + " 或 " + CLASSPATH_CONFIG_PATH); - } - - try (InputStream in = resource.getInputStream()) { - return new Yaml().loadAs(in, DataSourceConfig.class); - } - } - - private HikariDataSource buildHikariDataSource(DataSourceConfig.DbConfig config){ - HikariConfig hikariConfig = new HikariConfig(); - hikariConfig.setDriverClassName(config.getDriverClassName()); - hikariConfig.setJdbcUrl(config.getUrl()); - hikariConfig.setUsername(config.getUsername()); - hikariConfig.setPassword(config.getPassword()); - hikariConfig.setConnectionTestQuery("SELECT 1"); // SQL Server 可执行语句 - - return new HikariDataSource(hikariConfig); - } - - private DruidDataSource buildDruidDataSource(DataSourceConfig.DbConfig config) throws Exception { - DruidDataSource ds = getDefaultDruidDataSource(); - - ds.setDriverClassName(config.getDriverClassName()); - ds.setUrl(config.getUrl()); - ds.setUsername(config.getUsername()); - ds.setPassword(config.getPassword()); - - ds.init(); - return ds; - } - - private DruidDataSource getDefaultDruidDataSource() throws SQLException { - DruidDataSource dataSource = new DruidDataSource(); - // 启动程序时,在连接池中初始化多少个连接(10-50已足够) - dataSource.setInitialSize(5); - // 回收空闲连接时,将保证至少有 minIdle 个连接(与 initialSize 相同) - dataSource.setMinIdle(5); - // 连接池中最多支持多少个活动会话 - dataSource.setMaxActive(100); - // 程序向连接池中请求连接时,超过 maxWait 的值后,认为本次请求失败,即连接池,没有可用连接,单位毫秒,设置 -1 时表示无限等待(建议值为100) - dataSource.setMaxWait(100); - /* - 缓存通过以下两个方法发起的 SQL: - public PreparedStatement prepareStatement(String sql) - public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) - (建议值为 true) - */ - dataSource.setPoolPreparedStatements(true); - // 每个连接最多缓存多少个 SQL(建议值为 20) - dataSource.setMaxPoolPreparedStatementPerConnectionSize(20); - // 检查空闲连接的频率,单位毫秒,非正整数时表示不进行检查(建议值:2000) - dataSource.setTimeBetweenEvictionRunsMillis(2000); - // 连接池中某个连接的空闲时长达到 N 毫秒后, 连接池在下次检查空闲连接时,将回收该连接,要小于防火墙超时设置 net.netfilter.nf_conntrack_tcp_timeout_established 的设置 - dataSource.setMinEvictableIdleTimeMillis(600000); - // 配置一个连接在池中最大生存的时间,单位是毫秒 - dataSource.setMaxEvictableIdleTimeMillis(900000); - // 程序没有 close 连接且空闲时长超过 minEvictableIdleTimeMillis,则会执行 validationQuery 指定的 SQL,以保证该程序连接不会池 kill 掉,其范围不超过 minIdle 指定的连接个数(建议值为 true) - dataSource.setKeepAlive(true); - // 检查池中的连接是否仍可用的 SQL 语句,druid 会连接到数据库执行该 SQL,如果正常返回,则表示连接可用,否则表示连接不可用 - dataSource.setValidationQuery("SELECT 1"); - // 当程序请求连接,池在分配连接时,是否先检查该连接是否有效(高效,并且保证安全性;建议值为 true) - dataSource.setTestWhileIdle(true); - // 程序申请连接时,进行连接有效性检查(低效,影响性能;建议值为 false) - dataSource.setTestOnBorrow(false); - // 程序返还连接时,进行连接有效性检查(低效,影响性能;建议值为 false) - dataSource.setTestOnReturn(false); - // 物理连接初始化的时候执行的 sql -// Collection connectionInitSqls = new ArrayList<>(10); -// connectionInitSqls.add("SELECT 1 FROM DUAL"); -// dataSource.setConnectionInitSqls(connectionInitSqls); - /* - 这里配置的是插件,常用的插件有: - 监控统计:stat - 日志监控:log4j2 - 防御 SQL 注入:wall - */ - dataSource.setFilters("stat"); - // 是否合并多个 DruidDataSource 的监控数据 - dataSource.setUseGlobalDataSourceStat(true); - // 监控统计 - // 是否启用慢 SQL 记录 - dataSource.addConnectionProperty("druid.stat.logSlowSql", "true"); - // 执行时间超过 slowSqlMillis 的就是慢,单位毫秒(建议值 500) - dataSource.addConnectionProperty("druid.stat.slowSqlMillis", "500"); - // 要求程序从池中 get 到连接后,N 秒后必须 close,否则 druid 会强制回收该连接,不管该连接中是活动还是空闲,以防止进程不会进行 close 而霸占连接(建议值为 false,当发现程序有未正常 close 连接时设置为 true) -// dataSource.setRemoveAbandoned(false); - // 设置 druid 强制回收连接的时限,当程序从池中 get 到连接开始算起,超过此值后,druid将强制回收该连接,单位秒(应大于业务运行最长时间) -// dataSource.setRemoveAbandonedTimeout(); - // 当 druid 强制回收连接后,是否将 stack trace 记录到日志中(建议值为 true) -// dataSource.setLogAbandoned(true); - // 连接属性。比如设置一些连接池统计方面的配置 druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 -// dataSource.setConnectProperties(); - - // 防御 SQL 注入 - WallFilter wallFilter = new WallFilter(); - WallConfig config = new WallConfig(); - // 是否允许执行 DELETE 语句(建议值为 false) - config.setDeleteAllow(false); - // 是否允许删除表(建议值为 false) - config.setDropTableAllow(false); - wallFilter.setConfig(config); - - // 插件代理 - List proxyFilters = new ArrayList<>(10); - proxyFilters.add(wallFilter); - dataSource.setProxyFilters(proxyFilters); - return dataSource; - } -} diff --git a/code/link/link-backend/src/main/java/com/link/api/config/datasource/DruidConfig.java b/code/link/link-backend/src/main/java/com/link/api/config/datasource/DruidConfig.java deleted file mode 100644 index 316014aa..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/config/datasource/DruidConfig.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.link.api.config.datasource; - -import com.alibaba.druid.support.http.StatViewServlet; -import com.alibaba.druid.support.http.WebStatFilter; -import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.boot.web.servlet.ServletRegistrationBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import java.util.*; - -@Configuration -public class DruidConfig { - - @Bean - public ServletRegistrationBean statViewServlet() { - // 监控信息显示页面 - StatViewServlet statViewServlet = new StatViewServlet(); - // 访问监控信息显示页面的 url 路径(建议值为 /druid/* ) - String urlPattern = "/druid/*"; - - ServletRegistrationBean bean = new ServletRegistrationBean<>(statViewServlet, urlPattern); - - Map initParams = new HashMap<>(); - // 是否允许清空统计数据 - initParams.put("resetEnable", "false"); - // 登录监控信息显示页面的用户名 - initParams.put("loginUsername", "admin"); - // 登录监控信息显示页面的密码 - initParams.put("loginPassword", "admin"); - // 允许访问控制(格式:ip地址、ip地址/子网掩码位数)逗号分隔多个地址 - initParams.put("allow", "127.0.0.1"); - // 拒绝访问控制(格式:ip地址、ip地址/子网掩码位数)逗号分隔多个地址 -// initParams.put("deny", ""); - - bean.setInitParameters(initParams); - return bean; - } - - @Bean - public FilterRegistrationBean webStatFilter() { - FilterRegistrationBean bean = new FilterRegistrationBean<>(); - // 网络监控过滤器(用于采集 web-jdbc 关联监控的数据) - bean.setFilter(new WebStatFilter()); - // 过滤所有的 url 路径 - Collection urlPatterns = new ArrayList<>(); - urlPatterns.add("/*"); - bean.setUrlPatterns(urlPatterns); - - Map initParams = new HashMap<>(); - // 排除不必要采集的 url 路径,以逗号“,”分割 - initParams.put("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,*.map,/druid/*"); - // 是否使用 session 监控功能 - initParams.put("sessionStatEnable", "true"); - // 是否使用 session 监控最大数量(默认是1000) - initParams.put("sessionStatMaxCount", "1000"); - // 使得 druid 能够知道当前的 session 的用户是谁,根据需要,把改值修改为你 user 信息保存在 session 中的 sessionName - initParams.put("principalSessionName", "session_user_key"); - // 如果你的 user 信息保存在 cookie 中,你可以配置 principalCookieName,使得 druid 知道当前的 user 是谁,根据需要,把该值修改为你 user 信息保存在 cookie 中的 cookieName - initParams.put("principalCookieName", "cookie_user_key"); - // 是否监控单个 url 调用的 sql 列表 - initParams.put("profileEnable", "true"); - bean.setInitParameters(initParams); - return bean; - } - -} diff --git a/code/link/link-backend/src/main/java/com/link/api/config/datasource/LoadDataSourceRunner.java b/code/link/link-backend/src/main/java/com/link/api/config/datasource/LoadDataSourceRunner.java deleted file mode 100644 index ad92504d..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/config/datasource/LoadDataSourceRunner.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.link.api.config.datasource; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.CommandLineRunner; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - - -@Slf4j -@Component -public class LoadDataSourceRunner implements CommandLineRunner { - @Resource - private DataSourceManager dataSourceManager; - - @Override - public void run(String... args) throws Exception { - dataSourceManager.loadDatasource(); - } -} - diff --git a/code/link/link-backend/src/main/java/com/link/api/constants/ResponseStatusConstants.java b/code/link/link-backend/src/main/java/com/link/api/constants/ResponseStatusConstants.java deleted file mode 100644 index 796884c0..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/constants/ResponseStatusConstants.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.link.api.constants; - -public class ResponseStatusConstants { - /** - * 操作成功 - */ - public static final Integer SUCCESS = 200; - - /** - * 系统内部错误 - */ - public static final Integer ERROR = 500; - - /** - * 缺少参数,参数格式有误,请求路径有误等 - */ - public static final Integer BAD_REQUEST = 400; - - /** - * 未找到资源,或者该资源已丢失 - */ - public static final Integer NOT_FOUND = 404; - - /** - * 登录失效或已过期 - */ - public static final Integer UNAUTHORIZED = 401; -} diff --git a/code/link/link-backend/src/main/java/com/link/api/context/DataSourceContextHolder.java b/code/link/link-backend/src/main/java/com/link/api/context/DataSourceContextHolder.java deleted file mode 100644 index 6169f6a6..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/context/DataSourceContextHolder.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.link.api.context; - -public class DataSourceContextHolder { - private static final ThreadLocal contextHolder = new ThreadLocal<>(); - - public static void setDataSourceKey(String dataSourceKey) { - contextHolder.set(dataSourceKey); - } - - public static String getDataSourceKey() { - return contextHolder.get(); - } - - public static void clearDataSourceKey() { - contextHolder.remove(); - } -} diff --git a/code/link/link-backend/src/main/java/com/link/api/controller/AuthController.java b/code/link/link-backend/src/main/java/com/link/api/controller/AuthController.java new file mode 100644 index 00000000..4842e6c5 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/controller/AuthController.java @@ -0,0 +1,40 @@ +package com.link.api.controller; + + +import com.link.api.model.UserInfo; +import com.link.api.repository.UserRepository; +import com.link.api.util.JwtUtil; + +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.Map; + +@CrossOrigin +@RestController +public class AuthController { + @PostMapping("/api/login") + public Map login(@RequestBody Map body) { + Map response = new HashMap<>(); + String userId = body.get("userid"); + String password = body.get("password"); + String orgId = body.get("orgid"); + + if (userId == null || password == null || orgId == null) { + response.put("message", "Missing parameters"); + return response; + } + UserInfo userInfo = UserRepository.getUserPasswordById(orgId, userId); + if (userInfo != null && userInfo.getPassword().equals(password)) { + String token = JwtUtil.generateToken(userId, orgId); + response.put("token", token); + response.put("userInfo", userInfo); + response.put("message", "Login successful"); + } else { + response.put("message", "Invalid credentials"); + } + return response; + } + + +} diff --git a/code/link/link-backend/src/main/java/com/link/api/controller/ColumnConfigController.java b/code/link/link-backend/src/main/java/com/link/api/controller/ColumnConfigController.java new file mode 100644 index 00000000..f8520309 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/controller/ColumnConfigController.java @@ -0,0 +1,59 @@ +package com.link.api.controller; + +import com.link.api.util.JsonUtils; +import com.link.api.util.Result; +import com.link.api.util.UserContext; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.sql.SQLException; +import java.util.List; +import java.util.Map; + +@RestController +@CrossOrigin +public class ColumnConfigController { + + /** + * 获取某个表的列配置,返回统一结构的 JSON + */ + @GetMapping("/api/columns") + public Result getColumnConfig(@RequestParam String tableName, HttpServletRequest request) { + + + try { + + String orgId = UserContext.getOrgId(); + System.out.println("api/columns:"+orgId); + List> data = JsonUtils.queryColumnConfig(orgId, tableName); + return Result.ok("获取字段配置成功", data); + } catch (SQLException e) { + return Result.fail("获取字段配置失败:" + e.getMessage()); + } + } + + + @GetMapping("/api/columnsEdit") + public Result getColumnEditConfig(@RequestParam String tableName, HttpServletRequest request) { + + try { + String orgId = UserContext.getOrgId(); + List> data = JsonUtils.queryColumnEditConfig(orgId, tableName); + return Result.ok("获取字段配置成功", data); + } catch (SQLException e) { + return Result.fail("获取字段配置失败:" + e.getMessage()); + } + } + + @GetMapping("/api/columnsSelect") + public Result ColumnsSelect(@RequestParam String tableName, HttpServletRequest request) { + + try { + String orgId = UserContext.getOrgId(); + List> data = JsonUtils.queryColumnsSelectConfig(orgId, tableName); + return Result.ok("获取字段配置成功", data); + } catch (SQLException e) { + return Result.fail("获取字段配置失败:" + e.getMessage()); + } + } +} diff --git a/code/link/link-backend/src/main/java/com/link/api/controller/ConfigController.java b/code/link/link-backend/src/main/java/com/link/api/controller/ConfigController.java deleted file mode 100644 index 2418b6cb..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/controller/ConfigController.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.link.api.controller; - - -import com.link.api.config.datasource.DataSourceManager; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.Resource; - -@RestController -@RequestMapping("/config") -public class ConfigController { - @Resource - private DataSourceManager dataSourceManager; - @GetMapping("/refreshDb") - public void refreshDb(){ - dataSourceManager.loadDatasource(); - } -} diff --git a/code/link/link-backend/src/main/java/com/link/api/controller/DataListController.java b/code/link/link-backend/src/main/java/com/link/api/controller/DataListController.java new file mode 100644 index 00000000..a98c2160 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/controller/DataListController.java @@ -0,0 +1,175 @@ +package com.link.api.controller; + +import com.link.api.util.PaginationUtils; +import com.link.api.util.Result; +import com.link.api.util.UserContext; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.web.bind.annotation.*; + +@RestController +@CrossOrigin +public class DataListController { + + @GetMapping("/api/pagtable") + public Result getPagedTableData( + @RequestParam String tableName, + @RequestParam(defaultValue = "b_id") String pkColumn, + @RequestParam int page, + @RequestParam int size, + @RequestParam(required = false) String whereClause // 可选条件 + ) { + try { + String orgId = UserContext.getOrgId(); + System.out.println("api/pagtable:"+orgId); + + String json = PaginationUtils.paginateSqlServer2008( + orgId, tableName, pkColumn, page, size, whereClause + ); + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(json, Result.class); + } catch (Exception e) { + return Result.fail("分页接口异常:" + e.getMessage()); + } + } + + @GetMapping("/api/pagdetail") + public Result getPagedTableData( + @RequestParam String tableName, + @RequestParam(defaultValue = "subid") String pkColumn, + @RequestParam(required = false) String whereClause // 可选条件 + ) { + try { + + String orgId = UserContext.getOrgId(); + System.out.println("api/pagdetail:"+orgId); + String json = PaginationUtils.detailSqlServer2008( + orgId, tableName, pkColumn, whereClause + ); + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(json, Result.class); + } catch (Exception e) { + return Result.fail("分页接口异常:" + e.getMessage()); + } + } + + + @GetMapping("/api/selectcompany") + public Result getCompanyData( + @RequestParam String tableName, // ✅ 新增表名参数 + @RequestParam String query + + ) { + try { + + String orgId = UserContext.getOrgId(); + System.out.println("api/selectcompany:"+orgId); + String json = PaginationUtils.getbasicdataSqlServer2008(orgId,tableName,query); + + + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(json, Result.class); + } catch (Exception e) { + return Result.fail("分页接口异常:" + e.getMessage()); + } + } + + + @GetMapping("/api/selectbasicbyfieldname") + public Result getPagedTableDataname( + @RequestParam String tableName, // ✅ 新增表名参数 + @RequestParam String fieldName, + @RequestParam String query + + ) { + try { + + String orgId = UserContext.getOrgId(); + System.out.println("api/selectbasicbyfieldname:"+orgId); + String json = PaginationUtils.getbasicdatabyfieldnameSqlServer2008(orgId,tableName,fieldName,query); + + + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(json, Result.class); + } catch (Exception e) { + return Result.fail("分页接口异常:" + e.getMessage()); + } + } + + @GetMapping("/api/selectlistbysql") + public Result getDataBySql(@RequestParam String sql){ + try { + String orgId = UserContext.getOrgId(); + String json = PaginationUtils.getDataBySql(orgId,sql); + + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(json, Result.class); + } catch (Exception e) { + return Result.fail("分页接口异常:" + e.getMessage()); + } + } + + @GetMapping("/api/selectbasicbyID") + public Result getPagedTableDataID( + @RequestParam String tableName, // ✅ 新增表名参数 + @RequestParam String fieldName, + @RequestParam String query + + ) { + try { + + String orgId = UserContext.getOrgId(); + System.out.println("api/selectbasicbyID:"+orgId); + String json = PaginationUtils.getbasicdatabyidSqlServer2008(orgId,tableName,fieldName,query); + + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(json, Result.class); + } catch (Exception e) { + return Result.fail("分页接口异常:" + e.getMessage()); + } + } + + @GetMapping("/api/selectbasicbyquery") + public Result getPagedTableDataSelectQuery( + @RequestParam String tableName, // ✅ 新增表名参数 + @RequestParam String query, + @RequestParam String orderBy + + ) { + try { + String orgId = UserContext.getOrgId(); + String json = PaginationUtils.getbasicdatabyselectquerySqlServer2008(orgId,tableName,query,orderBy); + + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(json, Result.class); + } catch (Exception e) { + return Result.fail("分页接口异常:" + e.getMessage()); + } + } + + @GetMapping("/api/pagtableSelect") + public Result getPagedTableSelectData( + @RequestParam String selectid, + @RequestParam(defaultValue = "b_id") String pkColumn, + @RequestParam int page, + @RequestParam int size, + @RequestParam(required = false) String whereClause, // 可选条件 + @RequestParam String psparams + ) { + + String[] psparamsArr = psparams.split(","); // ✅ 转换为数组 + + try { + String orgId = UserContext.getOrgId(); + + String json = PaginationUtils.paginateSelectSqlServer2008( + orgId, selectid, pkColumn, page, size, whereClause,psparamsArr + ); + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(json, Result.class); + } catch (Exception e) { + return Result.fail("分页接口异常:" + e.getMessage()); + } + } + +} + diff --git a/code/link/link-backend/src/main/java/com/link/api/controller/DataObjController.java b/code/link/link-backend/src/main/java/com/link/api/controller/DataObjController.java new file mode 100644 index 00000000..f636632e --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/controller/DataObjController.java @@ -0,0 +1,91 @@ +package com.link.api.controller; + +import com.link.api.util.*; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.web.bind.annotation.*; + +import java.sql.SQLException; +import java.util.Map; + +@RestController +@CrossOrigin +public class DataObjController { + @GetMapping("/api/getobject") + public Result getPagedTableData( + @RequestParam String tableName, + @RequestParam(defaultValue = "b_id") String pkColumn, + @RequestParam String pkColumnValue + + ) { + try { + + String orgId = UserContext.getOrgId(); + System.out.println("api/pagtable:" + orgId); + String json = QueryByPkUtils.queryByPrimaryKey( + orgId, tableName, pkColumn, pkColumnValue + ); + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(json, Result.class); + } catch (Exception e) { + return Result.fail("分页接口异常:" + e.getMessage()); + } + } + + @GetMapping("/api/deleteobject") + public Result deleteobject( + @RequestParam String moduleid, + @RequestParam String ID + + ) throws SQLException { + //String orgCode = "G3HD"; + String recordId = ID; // 要删除的主键ID + String orgCode = UserContext.getOrgId(); + + Result> result = BatchTableDelete.executdeletetable(orgCode, moduleid, recordId); + + if (result.isSuccess()) { + System.out.println("删除成功!"); + result.getData().forEach((k, v) -> + System.out.println("表/统计项: " + k + " -> 删除条数: " + v) + ); + } else { + System.out.println("删除失败: " + result.getMessage()); + } + return result; + } + + + @GetMapping("/api/getDefaultvalue") + public Result getDefaultvalue( + @RequestParam String tableName + ) { + try { + String orgId = UserContext.getOrgId(); + // 获取JSON字符串 + String jsonResult = TableDataService.generateTableDataWithDefaults(orgId, tableName); + + // 将JSON字符串反序列化为Result对象 + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(jsonResult, Result.class); + } catch (Exception e) { + return Result.fail("获取默认值异常:" + e.getMessage()); + } + } + + @GetMapping("/api/getMustfield") + public Result getMustFields( + @RequestParam String tableName + ) { + try { + String orgId = UserContext.getOrgId(); + // 获取JSON字符串 + String jsonResult = TableDataService.getMustFields(orgId, tableName); + + // 将JSON字符串反序列化为Result对象 + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(jsonResult, Result.class); + } catch (Exception e) { + return Result.fail("获取默认值异常:" + e.getMessage()); + } + } +} diff --git a/code/link/link-backend/src/main/java/com/link/api/controller/DataSaveController.java b/code/link/link-backend/src/main/java/com/link/api/controller/DataSaveController.java new file mode 100644 index 00000000..8b9c2009 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/controller/DataSaveController.java @@ -0,0 +1,112 @@ +package com.link.api.controller; + + +import com.link.api.util.*; +import com.link.api.util.Result; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping +@CrossOrigin +public class DataSaveController { + + @PostMapping("/api/saveobject") + public Result saveObject(@RequestBody Map request) { + try { + + String orgId = UserContext.getOrgId(); + System.out.println("api/saveobject:" + orgId); + + ObjectMapper mapper = new ObjectMapper(); + + String[] tableNames = mapper.convertValue(request.get("tableNames"), String[].class); + String[] keyFields = mapper.convertValue(request.get("keyFields"), String[].class); + + // 把对象数组转为字符串数组(再传给 batchChanges) + String[] inserts = convertToJsonArrayStrings(request.get("inserts")); + String[] updates = convertToJsonArrayStrings(request.get("updates")); + String[] deletes = convertToJsonArrayStrings(request.get("deletes")); + + return BatchTable.batchChanges(orgId, tableNames, keyFields, inserts, updates, deletes); + } catch (Exception e) { + return Result.fail("参数处理失败:" + e.getMessage()); + } + } + + @PostMapping("/api/saveobjectV2") + public Result saveObjectV2(@RequestBody List> requestDataList) { + try { + + String orgId = UserContext.getOrgId(); + // 遍历处理每个表的数据 + for (Map tableData : requestDataList) { + String tableName = (String) tableData.get("tableName"); + String keyField = (String) tableData.get("keyField"); + List> inserts = (List>) tableData.get("inserts"); + List> updates = (List>) tableData.get("updates"); + List> deletes = (List>) tableData.get("deletes"); + + // System.out.println("处理表: " + tableName); + // System.out.println("插入数据: " + inserts); + // System.out.println("updates: " + updates); + // System.out.println("deletes: " + deletes); + } + return BatchTableV2.executeDatabaseOperations(orgId, requestDataList); + // return Result.ok("处理成功"); + } catch (Exception e) { + return Result.fail("参数处理失败:" + e.getMessage()); + } + } + @PostMapping("/api/saveobjectV3") + public Result saveobjectV3(@RequestBody List> requestDataList) { + try { + + String orgId = UserContext.getOrgId(); + + return BatchTableV3.executeDatabaseOperations(orgId, requestDataList); + // return Result.ok("处理成功"); + } catch (Exception e) { + return Result.fail("参数处理失败:" + e.getMessage()); + } + } + + + @PostMapping("/api/saveColumn") + public Result postSaveColumn(@RequestBody List> requestDataList) { + try { + String orgId = UserContext.getOrgId(); + + return BatchColumn.postSaveColumn(orgId, requestDataList); + } catch (Exception e) { + return Result.fail("参数处理失败:" + e.getMessage()); + } + } + + @PostMapping("/api/saveColumnEdit") + public Result postSaveColumnEdit(@RequestBody List> requestDataList) { + try { + String orgId = UserContext.getOrgId(); + + return BatchColumn.postSaveColumnEdit(orgId, requestDataList); + } catch (Exception e) { + return Result.fail("参数处理失败:" + e.getMessage()); + } + } + + private String[] convertToJsonArrayStrings(Object listObject) throws Exception { + ObjectMapper mapper = new ObjectMapper(); + List list = mapper.convertValue(listObject, new TypeReference>() { + }); + String[] result = new String[list.size()]; + for (int i = 0; i < list.size(); i++) { + result[i] = mapper.writeValueAsString(list.get(i)); + } + return result; + } + +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/controller/FilterConfig.java b/code/link/link-backend/src/main/java/com/link/api/controller/FilterConfig.java new file mode 100644 index 00000000..156b36ad --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/controller/FilterConfig.java @@ -0,0 +1,25 @@ +package com.link.api.controller; + +import com.link.api.util.JwtAuthenticationFilter; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + + +@Configuration +public class FilterConfig { + + @Bean + public FilterRegistrationBean jwtFilter() { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + + JwtAuthenticationFilter filter = new JwtAuthenticationFilter(); + registrationBean.setFilter(filter); + + registrationBean.addUrlPatterns("/*"); // 拦截所有请求 + registrationBean.setName("jwtAuthenticationFilter"); + registrationBean.setOrder(1); // 优先级,越小越靠前执行 + + return registrationBean; + } +} diff --git a/code/link/link-backend/src/main/java/com/link/api/controller/SearchConfigController.java b/code/link/link-backend/src/main/java/com/link/api/controller/SearchConfigController.java new file mode 100644 index 00000000..bcfc3564 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/controller/SearchConfigController.java @@ -0,0 +1,32 @@ +package com.link.api.controller; + +import com.link.api.util.*; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import java.sql.SQLException; +import java.util.List; +import java.util.Map; + +@RestController +@CrossOrigin +public class SearchConfigController { + @GetMapping("/api/Searchconfig") + public Result getColumnConfig(@RequestParam String tableName, HttpServletRequest request) { + + + try { + + String orgId = UserContext.getOrgId(); + System.out.println("api/Searchconfig:"+orgId); + List> data = JsonUtils.querySearchConfig(orgId, tableName); + return Result.ok("获取字段配置成功", data); + } catch (SQLException e) { + return Result.fail("获取字段配置失败:" + e.getMessage()); + } + } + +} diff --git a/code/link/link-backend/src/main/java/com/link/api/controller/StudentController.java b/code/link/link-backend/src/main/java/com/link/api/controller/StudentController.java new file mode 100644 index 00000000..2a3920dc --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/controller/StudentController.java @@ -0,0 +1,139 @@ +package com.link.api.controller; + +import com.link.api.model.Student; +import com.link.api.repository.StudentDaoJDBC; +import com.link.api.util.JwtPayload; +import com.link.api.util.JwtUtil; +import com.link.api.util.Result; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; + +@CrossOrigin +@RestController +public class StudentController { + + @Autowired + private StudentDaoJDBC studentDAO; + + + private JwtPayload extractPayload(HttpServletRequest request) { + String authHeader = request.getHeader("Authorization"); + + if (authHeader == null || !authHeader.startsWith("Bearer ")) { + return null; + } + + String token = authHeader.substring(7); + return JwtUtil.parseToken(token); + } + + + + @GetMapping + public Result getAll(HttpServletRequest request) { + + JwtPayload payload = extractPayload(request); + if (payload == null) { + return Result.fail("未授权访问,请先登录"); + } + + // 可使用 payload.getUsername()、payload.getDb() 进行记录或业务处理 + System.out.println("操作用户:" + payload.getUserid()); + System.out.println("操作库:" + payload.getOrgid()); + + try { + List data = studentDAO.findAll(); + if (data != null) { + return Result.ok("新增成功", data); + } else { + return Result.fail("新增失败"); + } + } catch (Exception e) { + return Result.fail("发生异常: " + e.getMessage()); + } + + + } + + @PostMapping + public Result create(@RequestBody Student student, HttpServletRequest request) { + + JwtPayload payload = extractPayload(request); + if (payload == null) { + return Result.fail("未授权访问,请先登录"); + } + + // 可使用 payload.getUsername()、payload.getDb() 进行记录或业务处理 + System.out.println("操作用户:" + payload.getUserid()); + System.out.println("操作库:" + payload.getOrgid()); + + try { + int newId = studentDAO.insert(student); + if (newId > 0) { + return Result.ok("新增成功", newId); + } else { + return Result.fail("新增失败"); + } + } catch (Exception e) { + return Result.fail("发生异常: " + e.getMessage()); + } + + + } + + @PutMapping("/{id}") + public Result update(@PathVariable Integer id, @RequestBody Student student, HttpServletRequest request) { + + JwtPayload payload = extractPayload(request); + if (payload == null) { + return Result.fail("未授权访问,请先登录"); + } + + // 可使用 payload.getUsername()、payload.getDb() 进行记录或业务处理 + System.out.println("操作用户:" + payload.getUserid()); + System.out.println("操作库:" + payload.getOrgid()); + + student.setB_id(id); + + try { + int newId = studentDAO.update(student); + if (newId > 0) { + return Result.ok("更新成功", newId); + } else { + return Result.fail("更新失败"); + } + } catch (Exception e) { + return Result.fail("发生异常: " + e.getMessage()); + } + + } + + @DeleteMapping("/{id}") + public Result delete(@PathVariable Integer id, HttpServletRequest request) { + + JwtPayload payload = extractPayload(request); + if (payload == null) { + return Result.fail("未授权访问,请先登录"); + } + + // 可使用 payload.getUsername()、payload.getDb() 进行记录或业务处理 + System.out.println("操作用户:" + payload.getUserid()); + System.out.println("操作库:" + payload.getOrgid()); + + + try { + int newId = studentDAO.deleteById(id); + if (newId > 0) { + return Result.ok("删除成功", newId); + } else { + return Result.fail("删除失败"); + } + } catch (Exception e) { + return Result.fail("发生异常: " + e.getMessage()); + } + + } +} diff --git a/code/link/link-backend/src/main/java/com/link/api/controller/TestController.java b/code/link/link-backend/src/main/java/com/link/api/controller/TestController.java deleted file mode 100644 index a1c3ea18..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/controller/TestController.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.link.api.controller; - -import com.link.api.context.DataSourceContextHolder; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.Resource; -import java.util.List; -import java.util.Map; - -@RestController -@RequestMapping("/test") -public class TestController { - @Resource - JdbcTemplate jdbcTemplate; - - @GetMapping - public String test() { - return "test"; - } - - @GetMapping("/test1") - public List> test1() { - // 切换数据源为 db1 - DataSourceContextHolder.setDataSourceKey("db1"); - - String sql = "SELECT * FROM user_account"; - - // 执行查询,返回List,每个Map代表一行记录,列名-值对 - List> result = jdbcTemplate.queryForList(sql); - - // 查询完后可以清理上下文,防止影响后续操作 - DataSourceContextHolder.clearDataSourceKey(); - - return result; - } - - @GetMapping("/test2") - public List> test2() { - // 切换数据源为 db1 - DataSourceContextHolder.setDataSourceKey("db2"); - - String sql = "SELECT * FROM sys_admin"; - // 执行查询,返回List,每个Map代表一行记录,列名-值对 - List> result = jdbcTemplate.queryForList(sql); - // 查询完后可以清理上下文,防止影响后续操作 - DataSourceContextHolder.clearDataSourceKey(); - - return result; - } -} diff --git a/code/link/link-backend/src/main/java/com/link/api/controller/TreeController.java b/code/link/link-backend/src/main/java/com/link/api/controller/TreeController.java new file mode 100644 index 00000000..2c682b86 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/controller/TreeController.java @@ -0,0 +1,25 @@ +package com.link.api.controller; +import com.link.api.util.QueryTreeUtils; +import com.link.api.util.Result; +import com.link.api.util.UserContext; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.web.bind.annotation.*; + + +@RestController +@RequestMapping("/api") +public class TreeController { + + @GetMapping("/tree") + public Result getTreeNodes(@RequestParam String tableName, + @RequestParam String id,@RequestParam int js) { + try { + String orgId = UserContext.getOrgId(); + String json = QueryTreeUtils.getTreeNodes(orgId,tableName,id, js); + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(json, Result.class); + } catch (Exception e) { + return Result.fail("分页接口异常:" + e.getMessage()); + } + } +} diff --git a/code/link/link-backend/src/main/java/com/link/api/controller/WebConfig.java b/code/link/link-backend/src/main/java/com/link/api/controller/WebConfig.java new file mode 100644 index 00000000..bf488458 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/controller/WebConfig.java @@ -0,0 +1,16 @@ +package com.link.api.controller; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.*; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOriginPatterns("http://localhost:5173", "http://localhost:8089","http://localhost:5174") // 前端地址 + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") + .allowedHeaders("*") + .allowCredentials(true); + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/enums/ResponseEnum.java b/code/link/link-backend/src/main/java/com/link/api/enums/ResponseEnum.java deleted file mode 100644 index c7c20f3f..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/enums/ResponseEnum.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.link.api.enums; - -import com.link.api.constants.ResponseStatusConstants; -import lombok.AllArgsConstructor; -import lombok.Getter; - -@AllArgsConstructor -@Getter -public enum ResponseEnum { - SUCCESS(ResponseStatusConstants.SUCCESS,"请求成功"), - ERROR(ResponseStatusConstants.ERROR,"服务器错误,请联系管理人员"), - AUTH_SUCCESS(ResponseStatusConstants.SUCCESS,"登录成功"), - AUTH_ERROR(ResponseStatusConstants.ERROR,"登录失败"), - AUTH_EXPIRE(ResponseStatusConstants.UNAUTHORIZED,"尚未登录或登录已过期,请重新登录"); - - private final Integer code; //响应码 - private final String message; //相应信息 -} diff --git a/code/link/link-backend/src/main/java/com/link/api/exception/BaseException.java b/code/link/link-backend/src/main/java/com/link/api/exception/BaseException.java deleted file mode 100644 index 4d14a93d..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/exception/BaseException.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.link.api.exception; - -import com.link.api.enums.ResponseEnum; -import lombok.Data; -import lombok.EqualsAndHashCode; - -@EqualsAndHashCode(callSuper = true) -@Data -public class BaseException extends RuntimeException { - private final boolean showDetail; - private final String message; - private final Integer code; - private final ResponseEnum responseEnum; - - public BaseException(String message) { - this(message, false, null, null); - } - - public BaseException(String message, Integer code) { - this(message, false, code, null); - } - - public BaseException(Integer code, String message) { - this(message, false, code, null); - } - - // 3. 传 ResponseEnum,默认 showDetail=false - public BaseException(ResponseEnum responseEnum) { - super(responseEnum.getMessage()); - this.message = responseEnum.getMessage(); - this.showDetail = false; - this.code = responseEnum.getCode(); - this.responseEnum = responseEnum; - } - - public BaseException(String message, boolean showDetail) { - this(message, showDetail, null, null); - } - - public BaseException(String message, Integer code, boolean showDetail) { - this(message, showDetail, code, null); - } - - public BaseException(ResponseEnum responseEnum, boolean showDetail) { - super(responseEnum.getMessage()); - this.message = responseEnum.getMessage(); - this.showDetail = showDetail; - this.code = responseEnum.getCode(); - this.responseEnum = responseEnum; - } - - private BaseException(String message, boolean showDetail, Integer code, ResponseEnum responseEnum) { - super(message); - this.message = message; - this.showDetail = showDetail; - this.code = code; - this.responseEnum = responseEnum; - } -} diff --git a/code/link/link-backend/src/main/java/com/link/api/exception/GlobalExceptionHandler.java b/code/link/link-backend/src/main/java/com/link/api/exception/GlobalExceptionHandler.java deleted file mode 100644 index 6d78731a..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/exception/GlobalExceptionHandler.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.link.api.exception; - -import com.link.api.model.Result; -import com.link.api.exception.auth.AuthException; -import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.RestControllerAdvice; - -import javax.validation.ConstraintViolation; -import javax.validation.ConstraintViolationException; - -@Slf4j -@RestControllerAdvice -public class GlobalExceptionHandler { - - @ExceptionHandler(ConstraintViolationException.class) - public Result handleConstraintViolationException(ConstraintViolationException ex) { - // 取第一个错误消息 - String errorMessage = ex.getConstraintViolations().stream() - .map(ConstraintViolation::getMessage) - .findFirst() - .orElse("参数校验失败"); - return Result.error(errorMessage); - } - - @ExceptionHandler(AuthException.class) - public Result handleAuthException(AuthException ex){ - return Result.error(ex.getMessage()); - } - -// @ExceptionHandler(Exception.class) -// public Result handleException(Exception ex, HttpServletRequest request) { -// ExceptionDetailInfo info = ExceptionUtils.extractExceptionInfo(ex, request, "com.email"); -// log.error("异常发生在 {}#{}({}:{}),ID: {},信息: {}", -// info.getClassName(), -// info.getMethodName(), -// info.getFileName(), -// info.getLineNumber(), -// info.getId(), -// info.getSummary()); -// ex.printStackTrace(); -// return Result.error(info).withShowDetail(true); -// } - -} diff --git a/code/link/link-backend/src/main/java/com/link/api/exception/auth/AuthException.java b/code/link/link-backend/src/main/java/com/link/api/exception/auth/AuthException.java deleted file mode 100644 index c948fcc0..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/exception/auth/AuthException.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.link.api.exception.auth; - - -import com.link.api.enums.ResponseEnum; -import com.link.api.exception.BaseException; - -public class AuthException extends BaseException { - public AuthException() { - super(ResponseEnum.AUTH_EXPIRE); // 调用父类的默认构造函数 - } - - public AuthException(ResponseEnum responseEnum){ - super(responseEnum); - } - - public AuthException(Integer code,String message){ - super(code,message); - } -} diff --git a/code/link/link-backend/src/main/java/com/link/api/manager/UserManager.java b/code/link/link-backend/src/main/java/com/link/api/manager/UserManager.java new file mode 100644 index 00000000..7eed3974 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/manager/UserManager.java @@ -0,0 +1,51 @@ +package com.link.api.manager; + +import io.netty.channel.Channel; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +public class UserManager { + private static final Map userChannels = new ConcurrentHashMap<>(); + private static final Map channelUsers = new ConcurrentHashMap<>(); + + public static boolean login(String username, Channel channel) { + if (username == null || username.trim().isEmpty() || userChannels.containsKey(username)) { + return false; + } + userChannels.put(username, channel); + channelUsers.put(channel, username); + return true; + } + + public static void logout(Channel channel) { + String username = channelUsers.remove(channel); + if (username != null) { + userChannels.remove(username); + } + } + + public static Channel getUserChannel(String username) { + return userChannels.get(username); + } + + public static String getUsername(Channel channel) { + return channelUsers.get(channel); + } + + public static Set getOnlineUsers() { + return new HashSet<>(userChannels.keySet()); + } + + public static Collection getAllChannels() { + return new ArrayList<>(userChannels.values()); + } + + public static boolean isUserOnline(String username) { + return userChannels.containsKey(username); + } + + public static int getOnlineCount() { + return userChannels.size(); + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/model/ChatMessage.java b/code/link/link-backend/src/main/java/com/link/api/model/ChatMessage.java new file mode 100644 index 00000000..7a2552dc --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/model/ChatMessage.java @@ -0,0 +1,59 @@ +package com.link.api.model; + +import java.util.List; + +public class ChatMessage { + private String type; + private String username; + private String content; + private String targetUser; + private List onlineUsers; + private long timestamp; + + public ChatMessage() { + this.timestamp = System.currentTimeMillis(); + } + + public ChatMessage(String type, String username, String content) { + this(); + this.type = type; + this.username = username; + this.content = content; + } + + public ChatMessage(String type, String username, String content, String targetUser) { + this(type, username, content); + this.targetUser = targetUser; + } + + // Getter和Setter + public String getType() { return type; } + public void setType(String type) { this.type = type; } + + public String getUsername() { return username; } + public void setUsername(String username) { this.username = username; } + + public String getContent() { return content; } + public void setContent(String content) { this.content = content; } + + public String getTargetUser() { return targetUser; } + public void setTargetUser(String targetUser) { this.targetUser = targetUser; } + + public List getOnlineUsers() { return onlineUsers; } + public void setOnlineUsers(List onlineUsers) { this.onlineUsers = onlineUsers; } + + public long getTimestamp() { return timestamp; } + public void setTimestamp(long timestamp) { this.timestamp = timestamp; } + + @Override + public String toString() { + return "ChatMessage{" + + "type='" + type + '\'' + + ", username='" + username + '\'' + + ", content='" + content + '\'' + + ", targetUser='" + targetUser + '\'' + + ", onlineUsers=" + onlineUsers + + ", timestamp=" + timestamp + + '}'; + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/model/ExceptionEntity.java b/code/link/link-backend/src/main/java/com/link/api/model/ExceptionEntity.java deleted file mode 100644 index e8ed5c23..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/model/ExceptionEntity.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.link.api.model; - -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; -import lombok.Builder; -import lombok.Data; - -import java.util.Date; -@Data -@Builder -public class ExceptionEntity { - /** 异常记录的唯一ID */ - @JsonSerialize(using = ToStringSerializer.class) - private Long id; - - /** 异常发生时间 */ - private Date time; - - /** 请求的URI地址 */ - private String uri; - - /** HTTP请求方法,如GET、POST等 */ - private String httpMethod; - - /** 抛出异常的类名 */ - private String className; - - /** 抛出异常的方法名 */ - private String methodName; - - /** 抛出异常的文件名 */ - private String fileName; - - /** 抛出异常的代码行号 */ - private Integer lineNumber; - - /** 异常的简短信息 */ - private String summary; - - /** 异常的详细堆栈信息 */ - private String detailMessage; - - /** 请求参数(可能为JSON字符串等) */ - private String params; -} diff --git a/code/link/link-backend/src/main/java/com/link/api/model/Result.java b/code/link/link-backend/src/main/java/com/link/api/model/Result.java deleted file mode 100644 index 5d6db0f1..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/model/Result.java +++ /dev/null @@ -1,126 +0,0 @@ -package com.link.api.model; - -import com.link.api.enums.ResponseEnum; -import lombok.NonNull; - -import lombok.Builder; -import lombok.Getter; - -@Getter -@Builder(toBuilder = true) -public class Result { - private final Integer code; - private final String message; - private final T data; - private final Boolean showExceptionDetail; - - // 工厂方法建议保留,简化构造调用 - public static Result success() { - return Result.builder() - .code(ResponseEnum.SUCCESS.getCode()) - .message(ResponseEnum.SUCCESS.getMessage()) - .showExceptionDetail(false) - .build(); - } - - public static Result success(String message) { - return Result.builder() - .code(ResponseEnum.SUCCESS.getCode()) - .message(message) - .showExceptionDetail(false) - .build(); - } - - public static Result success(@NonNull T data) { - return Result.builder() - .code(ResponseEnum.SUCCESS.getCode()) - .message(ResponseEnum.SUCCESS.getMessage()) - .data(data) - .showExceptionDetail(false) - .build(); - } - - public static Result success(@NonNull T data, String message) { - return Result.builder() - .code(ResponseEnum.SUCCESS.getCode()) - .message(message) - .data(data) - .showExceptionDetail(false) - .build(); - } - - public static Result success(ResponseEnum responseEnum) { - return Result.builder() - .code(responseEnum.getCode()) - .message(responseEnum.getMessage()) - .showExceptionDetail(false) - .build(); - } - - public static Result success(ResponseEnum responseEnum, @NonNull T data) { - return Result.builder() - .code(responseEnum.getCode()) - .message(responseEnum.getMessage()) - .data(data) - .showExceptionDetail(false) - .build(); - } - - public static Result error() { - return Result.builder() - .code(ResponseEnum.ERROR.getCode()) - .message(ResponseEnum.ERROR.getMessage()) - .showExceptionDetail(false) - .build(); - } - - public static Result error(String message) { - return Result.builder() - .code(ResponseEnum.ERROR.getCode()) - .message(message) - .showExceptionDetail(false) - .build(); - } - - public static Result error(Integer code, String message) { - return Result.builder() - .code(code) - .message(message) - .showExceptionDetail(false) - .build(); - } - - public static Result error(ResponseEnum responseEnum) { - return Result.builder() - .code(responseEnum.getCode()) - .message(responseEnum.getMessage()) - .showExceptionDetail(false) - .build(); - } - - public static Result error(T data) { - return Result.builder() - .code(ResponseEnum.ERROR.getCode()) - .message(ResponseEnum.ERROR.getMessage()) - .data(data) - .showExceptionDetail(false) - .build(); - } - - public static Result error(T data, String message) { - return Result.builder() - .code(ResponseEnum.ERROR.getCode()) - .message(message) - .data(data) - .showExceptionDetail(false) - .build(); - } - - public Result withShowExceptionDetail(boolean show) { - return this.toBuilder().showExceptionDetail(show).build(); - } - - public boolean isSuccess() { - return ResponseEnum.SUCCESS.getCode().equals(this.code); - } -} diff --git a/code/link/link-backend/src/main/java/com/link/api/model/Student.java b/code/link/link-backend/src/main/java/com/link/api/model/Student.java new file mode 100644 index 00000000..95dc155b --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/model/Student.java @@ -0,0 +1,41 @@ +package com.link.api.model; + +public class Student { + private Integer b_id; + private String b_name; + private Integer b_age; + private String b_class; + + public Integer getB_id() { + return b_id; + } + + public void setB_id(Integer b_id) { + this.b_id = b_id; + } + + public String getB_name() { + return b_name; + } + + public void setB_name(String b_name) { + this.b_name = b_name; + } + + public Integer getB_age() { + return b_age; + } + + public void setB_age(Integer b_age) { + this.b_age = b_age; + } + + public String getB_class() { + return b_class; + } + + public void setB_class(String b_class) { + this.b_class = b_class; + } + +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/model/UserInfo.java b/code/link/link-backend/src/main/java/com/link/api/model/UserInfo.java new file mode 100644 index 00000000..bc4f6b30 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/model/UserInfo.java @@ -0,0 +1,33 @@ +package com.link.api.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class UserInfo { + private final String userId; + private final String username; + private final String password; + + @JsonCreator + public UserInfo( + @JsonProperty("userId") String userId, + @JsonProperty("username") String username, + @JsonProperty("password") String password + ) { + this.userId = userId; + this.username = username; + this.password = password; + } + + public String getUserId() { + return userId; + } + + public String getPassword() { + return password; + } + + public String getUsername() { + return username; + } +} diff --git a/code/link/link-backend/src/main/java/com/link/api/repository/MultiTenantUserDao.java b/code/link/link-backend/src/main/java/com/link/api/repository/MultiTenantUserDao.java new file mode 100644 index 00000000..16919331 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/repository/MultiTenantUserDao.java @@ -0,0 +1,53 @@ +package com.link.api.repository; + +import com.link.api.util.DruidDataSourceManager; + +import java.sql.*; +import java.util.ArrayList; +import java.util.List; + +public class MultiTenantUserDao { + + public static void insert(String orgCode, String username, String password) throws SQLException { + String sql = "INSERT INTO chat_users (username, password) VALUES (?, ?)"; + try (Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setString(1, username); + ps.setString(2, password); + ps.executeUpdate(); + } + } + + public static List list(String orgCode) throws SQLException { + String sql = "SELECT id, username, password FROM chat_users"; + List result = new ArrayList<>(); + try (Connection conn = DruidDataSourceManager.getConnection(orgCode); + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(sql)) { + while (rs.next()) { + result.add(rs.getInt("id") + ": " + rs.getString("username") + ", " + rs.getString("password")); + } + } + return result; + } + + public static void update(String orgCode, int id, String username, String password) throws SQLException { + String sql = "UPDATE chat_users SET username = ?, password = ? WHERE id = ?"; + try (Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setString(1, username); + ps.setString(2, password); + ps.setInt(3, id); + ps.executeUpdate(); + } + } + + public static void delete(String orgCode, int id) throws SQLException { + String sql = "DELETE FROM chat_users WHERE id = ?"; + try (Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setInt(1, id); + ps.executeUpdate(); + } + } +} diff --git a/code/link/link-backend/src/main/java/com/link/api/repository/StudentDAO.java b/code/link/link-backend/src/main/java/com/link/api/repository/StudentDAO.java new file mode 100644 index 00000000..c2b339ef --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/repository/StudentDAO.java @@ -0,0 +1,36 @@ +package com.link.api.repository; + +import com.link.api.model.Student; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.BeanPropertyRowMapper; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public class StudentDAO { + + @Autowired + private JdbcTemplate jdbcTemplate; + + public List findAll() { + String sql = "SELECT * FROM Students"; + return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Student.class)); + } + + public int insert(Student s) { + String sql = "INSERT INTO Students (b_name, b_age, b_class) VALUES (?, ?, ?)"; + return jdbcTemplate.update(sql, s.getB_name(), s.getB_age(), s.getB_class()); + } + + public int update(Student s) { + String sql = "UPDATE Students SET b_name = ?, b_age = ?, b_class = ? WHERE b_id = ?"; + return jdbcTemplate.update(sql, s.getB_name(), s.getB_age(), s.getB_class(), s.getB_id()); + } + + public int deleteById(int id) { + String sql = "DELETE FROM Students WHERE b_id = ?"; + return jdbcTemplate.update(sql, id); + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/repository/StudentDaoJDBC.java b/code/link/link-backend/src/main/java/com/link/api/repository/StudentDaoJDBC.java new file mode 100644 index 00000000..8f81398b --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/repository/StudentDaoJDBC.java @@ -0,0 +1,88 @@ +package com.link.api.repository; + +import com.link.api.model.Student; +import com.link.api.util.ConnectionManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; + +import java.sql.*; +import java.util.ArrayList; +import java.util.List; + +@Repository +public class StudentDaoJDBC { + + @Autowired + private ConnectionManager connectionManager; + + public List findAll() { + List list = new ArrayList<>(); + String sql = "SELECT b_id, b_name, b_age, b_class FROM Students"; + + try (Connection conn = connectionManager.getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql); + ResultSet rs = stmt.executeQuery()) { + + while (rs.next()) { + Student s = new Student(); + s.setB_id(rs.getInt("b_id")); + s.setB_name(rs.getString("b_name")); + s.setB_age(rs.getInt("b_age")); + s.setB_class(rs.getString("b_class")); + list.add(s); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + return list; + } + + public int insert(Student s) { + String sql = "INSERT INTO Students (b_name, b_age, b_class) VALUES (?, ?, ?)"; + try (Connection conn = connectionManager.getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setString(1, s.getB_name()); + stmt.setInt(2, s.getB_age()); + stmt.setString(3, s.getB_class()); + return stmt.executeUpdate(); + + } catch (SQLException e) { + e.printStackTrace(); + return 0; + } + } + + public int update(Student s) { + String sql = "UPDATE Students SET b_name = ?, b_age = ?, b_class = ? WHERE b_id = ?"; + try (Connection conn = connectionManager.getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setString(1, s.getB_name()); + stmt.setInt(2, s.getB_age()); + stmt.setString(3, s.getB_class()); + stmt.setInt(4, s.getB_id()); + return stmt.executeUpdate(); + + } catch (SQLException e) { + e.printStackTrace(); + return 0; + } + } + + public int deleteById(int id) { + String sql = "DELETE FROM Students WHERE b_id = ?"; + + try (Connection conn = connectionManager.getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setInt(1, id); + return stmt.executeUpdate(); + + } catch (SQLException e) { + e.printStackTrace(); + return 0; + } + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/repository/UserRepository.java b/code/link/link-backend/src/main/java/com/link/api/repository/UserRepository.java new file mode 100644 index 00000000..e7a9fa91 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/repository/UserRepository.java @@ -0,0 +1,42 @@ +package com.link.api.repository; + +import com.link.api.model.UserInfo; +import com.link.api.util.DruidDataSourceManager; + +import java.sql.*; + +public class UserRepository { + + /** + * 根据 orgCode 和 userId 查询用户信息 + * + * @param orgCode 多租户的 orgCode + * @param userId 用户 ID + * @return 用户信息,如果不存在返回 null + */ + public static UserInfo getUserPasswordById(String orgCode, String userId) { + String sql = "SELECT b_id,b_name, b_password FROM b_user WHERE b_id = ?"; + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql) + ) { + stmt.setString(1, userId); + try (ResultSet rs = stmt.executeQuery()) { + if (rs.next()) { + String id = rs.getString("b_id"); + String username = rs.getString("b_name"); + String password = rs.getString("b_password"); + System.out.println("username=---------------"); + System.out.println(username); + return new UserInfo(id,username, password); + } else { + return null; + } + } + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/util/BatchColumn.java b/code/link/link-backend/src/main/java/com/link/api/util/BatchColumn.java new file mode 100644 index 00000000..1064f75e --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/BatchColumn.java @@ -0,0 +1,126 @@ +package com.link.api.util; + + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.List; +import java.util.Map; + +import static org.springframework.jdbc.support.JdbcUtils.closeStatement; + +public class BatchColumn { + + public static Result postSaveColumn(String orgCode, List> columnMaps) { + + Connection conn = null; + PreparedStatement pstmt = null; + + try { + conn = DruidDataSourceManager.getConnection(orgCode); + conn.setAutoCommit(false); + + String sql = "UPDATE s_columnlib SET col_Width = ?,col_Position=? WHERE col_TableName = ? and col_FieldName = ?"; + pstmt = conn.prepareStatement(sql); + + System.out.println(columnMaps.toString()); + + int i = 2; + for (Map row : columnMaps) { + // 从 Map 取值(要确保 key 和数据库字段对应) + int width = (int) row.get("width"); + String tableName = (String) row.get("col_TableName"); + Object fieldName = row.get("prop"); + + pstmt.setInt(1, width); + pstmt.setInt(2, i); + pstmt.setString(3, tableName); + pstmt.setObject(4, fieldName); + + pstmt.addBatch(); + i += 2; + } + + pstmt.executeBatch(); + conn.commit(); + + System.out.println("批量更新成功,处理了 " + columnMaps.size() + " 条数据"); + return Result.ok("保存成功"); + } catch (SQLException e) { + rollbackTransaction(conn); + e.printStackTrace(); + return Result.fail("保存失败: " + e.getMessage()); + } finally { + closeStatement(pstmt); + closeConnection(conn); + } + } + + public static Result postSaveColumnEdit(String orgCode, List> columnMaps) { + + Connection conn = null; + PreparedStatement pstmt = null; + + try { + conn = DruidDataSourceManager.getConnection(orgCode); + conn.setAutoCommit(false); + + String sql = "UPDATE s_columnlib SET col_edit_width = ?,col_edit_position=? WHERE col_TableName = ? and col_FieldName = ?"; + pstmt = conn.prepareStatement(sql); + + System.out.println(columnMaps.toString()); + + int i = 2; + for (Map row : columnMaps) { + // 从 Map 取值(要确保 key 和数据库字段对应) + int width = (int) row.get("edit_width"); + String tableName = (String) row.get("col_TableName"); + Object fieldName = row.get("prop"); + + pstmt.setInt(1, width); + pstmt.setInt(2, i); + pstmt.setString(3, tableName); + pstmt.setObject(4, fieldName); + + pstmt.addBatch(); + i += 2; + } + + pstmt.executeBatch(); + conn.commit(); + + System.out.println("批量更新成功,处理了 " + columnMaps.size() + " 条数据"); + return Result.ok("保存成功"); + } catch (SQLException e) { + rollbackTransaction(conn); + e.printStackTrace(); + return Result.fail("保存失败: " + e.getMessage()); + } finally { + closeStatement(pstmt); + closeConnection(conn); + } + } + + // 事务回滚 + private static void rollbackTransaction(Connection conn) { + if (conn != null) { + try { + conn.rollback(); + } catch (SQLException ex) { + System.err.println("回滚事务时出错: " + ex.getMessage()); + } + } + } + + // 关闭连接 + private static void closeConnection(Connection conn) { + if (conn != null) { + try { + conn.setAutoCommit(true); + conn.close(); + } catch (SQLException e) { + System.err.println("关闭数据库连接时出错: " + e.getMessage()); + } + } + } +} diff --git a/code/link/link-backend/src/main/java/com/link/api/util/BatchTable.java b/code/link/link-backend/src/main/java/com/link/api/util/BatchTable.java new file mode 100644 index 00000000..7ee4cbae --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/BatchTable.java @@ -0,0 +1,269 @@ +package com.link.api.util; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.*; +import java.util.stream.Collectors; + +public class BatchTable { + + private static final ObjectMapper mapper = new ObjectMapper(); + + public static Result> batchChanges( + String orgCode, + String[] tableNames, + String[] keyFields, + String[] inserts, // JSON字符串数组 + String[] updates, + String[] deletes + ) { + // 验证参数 + if (tableNames == null || keyFields == null || inserts == null || updates == null || deletes == null) { + return Result.fail("参数不能为null"); + } + + Connection conn = null; + Map resultStats = new HashMap<>(); + resultStats.put("totalTables", tableNames.length); + int totalInserts = 0; + int totalUpdates = 0; + int totalDeletes = 0; + + try { + conn = DruidDataSourceManager.getConnection(orgCode); + conn.setAutoCommit(false); + + for (int i = 0; i < tableNames.length; i++) { + String table = tableNames[i]; + String key = keyFields[i]; + + // 安全检查:避免越界 + String insertJson = i < inserts.length ? inserts[i] : "[]"; + String updateJson = i < updates.length ? updates[i] : "[]"; + String deleteJson = i < deletes.length ? deletes[i] : "[]"; + + List> insertList = parseJsonArray(insertJson); + List> updateList = parseJsonArray(updateJson); + List> deleteList = parseJsonArray(deleteJson); + + // 执行插入操作 + for (int j = 0; j < insertList.size(); j++) { + insert(conn, table, insertList.get(j)); + } + totalInserts += insertList.size(); + + // 执行更新操作 + for (int j = 0; j < updateList.size(); j++) { + updateex(conn, table, updateList.get(j), key); + } + totalUpdates += updateList.size(); + + // 执行删除操作 + for (int j = 0; j < deleteList.size(); j++) { + delete(conn, table, deleteList.get(j), key); + } + totalDeletes += deleteList.size(); + } + + conn.commit(); + + // 构建统计结果 + resultStats.put("insertedRecords", totalInserts); + resultStats.put("updatedRecords", totalUpdates); + resultStats.put("deletedRecords", totalDeletes); + + return Result.ok("批量操作成功", resultStats); + + } catch (SQLException e) { + try { + if (conn != null) conn.rollback(); + } catch (SQLException ex) { + return Result.fail("批量操作失败且回滚失败: " + ex.getMessage()); + } + return Result.fail("批量操作失败,事务已回滚: " + e.getMessage()); + } catch (Exception e) { + try { + if (conn != null) conn.rollback(); + } catch (SQLException ex) { + return Result.fail("批量操作失败且回滚失败: " + ex.getMessage()); + } + return Result.fail("批量操作失败: " + e.getMessage()); + } finally { + if (conn != null) { + try { + conn.setAutoCommit(true); + conn.close(); + } catch (SQLException e) { + // 关闭连接时的异常不影响业务结果,可以记录日志 + System.err.println("关闭数据库连接时出错: " + e.getMessage()); + } + } + } + } + + + private static List> parseJsonArray(String json) throws Exception { + if (json == null || json.trim().isEmpty()) return Collections.emptyList(); + + JsonNode node = mapper.readTree(json); + + if (node.isArray()) { + return mapper.readValue(json, new TypeReference>>() { + }); + } else if (node.isObject()) { + Map single = mapper.readValue(json, new TypeReference>() { + }); + return Collections.singletonList(single); // 包成 List 返回 + } else { + throw new IllegalArgumentException("Invalid JSON input: expected array or object"); + } + } + + private static void insert(Connection conn, String table, Map data) throws Exception { + List insertFields = new ArrayList<>(); + String sql = BuildSql.buildInsertSql(conn, table, insertFields); + System.out.println("[SQL-INSERT-buildInsertSql] " + sql); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + for (int i = 0; i < insertFields.size(); i++) { + ps.setObject(i + 1, data.get(insertFields.get(i))); + } + + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("INSERT 失败:未插入任何数据,数据内容:" + data); + } + + System.out.println("[SQL-INSERT] " + sql); + System.out.println("[PARAMS] " + insertFields.stream().map(data::get).collect(java.util.stream.Collectors.toList())); + } + } + + private static void update(Connection conn, String table, Map row, String pkField) throws SQLException { + Object pkValue = row.get(pkField); + if (pkValue == null) { + throw new IllegalArgumentException("缺少主键字段: " + pkField + " in " + row); + } + + List updateFields = new ArrayList<>(); + String sql = BuildSql.buildUpdateSql(conn, table, pkField, updateFields); // 构建 SQL 并获取字段顺序 + + List values = new ArrayList<>(); + for (String field : updateFields) { + Object value = row.get(field); + if (field.equalsIgnoreCase(pkField)) { + if (value == null) throw new IllegalArgumentException("更新时主键不能为空:" + pkField); + } + values.add(value); + } + + System.out.println("[SQL-UPDATE] " + sql); + System.out.println("[PARAMS] " + values); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + for (int i = 0; i < values.size(); i++) { + ps.setObject(i + 1, values.get(i)); + } + + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("UPDATE 失败:未匹配记录,数据内容:" + row); + } + } + } + + private static void delete(Connection conn, String table, Map row, String pkField) throws SQLException { + Object pkValue = row.get(pkField); + if (pkValue == null) { + throw new IllegalArgumentException("缺少主键字段: " + pkField + " in " + row); + } + + String sql = BuildSql.buildDeleteSql(table, pkField); + + System.out.println("[SQL-DELETE] " + sql); + System.out.println("[PARAMS] " + pkValue); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setObject(1, pkValue); + + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("DELETE 失败(未匹配记录): " + row); + } + } + } + + + private static void updateex(Connection conn, String table, Map row, String pkField) throws SQLException { + // 检查主键是否存在 + Object pkValue = row.get(pkField); + if (pkValue == null) { + throw new IllegalArgumentException("缺少主键字段: " + pkField + " in " + row); + } + + // 排除主键字段,只获取需要更新的字段 + Set changedFields = row.keySet().stream() + .filter(field -> !field.equalsIgnoreCase(pkField)) + .collect(Collectors.toSet()); + + if (changedFields.isEmpty()) { + throw new IllegalArgumentException("没有可更新的字段: " + row); + } + + // 构建动态SQL + String sql = buildDynamicUpdateSql(table, pkField, changedFields); + + // 准备参数值 + List values = new ArrayList<>(); + for (String field : changedFields) { + values.add(row.get(field)); + } + // 最后添加主键值作为WHERE条件 + values.add(pkValue); + + System.out.println("[SQL-UPDATE] " + sql); + System.out.println("[PARAMS] " + values); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + for (int i = 0; i < values.size(); i++) { + ps.setObject(i + 1, values.get(i)); + } + + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("UPDATE 失败:未匹配记录,数据内容:" + row); + } + } + } + + /** + * 构建动态UPDATE SQL语句 + * + * @param table 表名 + * @param pkField 主键字段名 + * @param fieldsToUpdate 需要更新的字段集合 + * @return 生成的SQL语句 + */ + private static String buildDynamicUpdateSql(String table, String pkField, Set fieldsToUpdate) { + StringBuilder sql = new StringBuilder("UPDATE ") + .append(table) + .append(" SET "); + + // 添加要更新的字段 + String setClause = fieldsToUpdate.stream() + .map(field -> field + " = ?") + .collect(Collectors.joining(", ")); + sql.append(setClause); + + // 添加WHERE条件 + sql.append(" WHERE ").append(pkField).append(" = ?"); + + return sql.toString(); + } + +} diff --git a/code/link/link-backend/src/main/java/com/link/api/util/BatchTableDelete.java b/code/link/link-backend/src/main/java/com/link/api/util/BatchTableDelete.java new file mode 100644 index 00000000..6075bbee --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/BatchTableDelete.java @@ -0,0 +1,198 @@ +package com.link.api.util; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; + + +public class BatchTableDelete { + private static final ObjectMapper mapper = new ObjectMapper(); + + // === 入口 === + public static Result> executdeletetable(String orgCode, String moduleid, String ID) throws SQLException { + boolean candelete = deletebefore(orgCode, moduleid, ID); + if (candelete) { + List> deletetablelist = getDeletetableList(orgCode, moduleid, ID); + return deleteDatabaseOperations(orgCode, deletetablelist); + } + return Result.fail("存在依赖数据,禁止删除: table=" + moduleid + ", id=" + ID); + } + + // === 删除前检查依赖 === + public static boolean deletebefore(String orgCode, String moduleid, String ID) throws SQLException { + String sql = "SELECT b_JudgeCondition FROM s_module WHERE b_id = ?"; + + try (Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setString(1, moduleid); + + try (ResultSet rs = stmt.executeQuery()) { + if (rs.next()) { + String conditionSql = rs.getString("b_JudgeCondition"); + + if (conditionSql == null || conditionSql.trim().isEmpty()) { + return true; // 没配置依赖条件,默认允许删除 + } + + // 替换占位符 + conditionSql = conditionSql.replace(":b_id", "'" + ID + "'"); + System.out.println("[执行条件SQL] " + conditionSql); + + try (PreparedStatement ps = conn.prepareStatement(conditionSql); + ResultSet crs = ps.executeQuery()) { + + if (crs.next()) { + System.out.println("存在依赖数据,不允许删除: ID=" + ID); + return false; + } else { + System.out.println("未发现依赖数据,可以删除: ID=" + ID); + return true; + } + } + } + } + } catch (Exception e) { + throw new RuntimeException("执行 deletebefore 失败", e); + } + + return false; // 默认不允许删除 + } + + // === 获取需要删除的表集合 === + public static List> getDeletetableList(String orgCode, String moduleid, String ID) throws SQLException { + String sql = "SELECT b_SaveTable FROM s_module WHERE b_LinkTable = ?"; + + try (Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setString(1, moduleid); + + try (ResultSet rs = stmt.executeQuery()) { + List> tablelist = getlinktablelist(rs, ID); + + // 添加主表 + Map map = new LinkedHashMap<>(); + map.put("tableName", moduleid); + map.put("keyField", "b_id"); + map.put("Fieldvalue", ID); + tablelist.add(map); + + return tablelist; + } + } catch (Exception e) { + throw new RuntimeException("查询 s_module 失败", e); + } + } + + private static List> getlinktablelist(ResultSet rs, String ID) throws SQLException { + List> list = new ArrayList<>(); + while (rs.next()) { + Map map = new LinkedHashMap<>(); + map.put("tableName", rs.getString("b_SaveTable")); + map.put("keyField", "father"); + map.put("Fieldvalue", ID); + list.add(map); + } + return list; + } + + // === 执行批量删除 === + public static Result> deleteDatabaseOperations(String orgCode, List> tableMaps) { + Map operationCounts = new LinkedHashMap<>(); + operationCounts.put("total", 0); + + Connection conn = null; + try { + conn = DruidDataSourceManager.getConnection(orgCode); + conn.setAutoCommit(false); + + for (Map tablemap : tableMaps) { + String tableName = (String) tablemap.get("tableName"); + String keyField = (String) tablemap.get("keyField"); + String fieldValue = (String) tablemap.get("Fieldvalue"); + + validateIdentifier(tableName); + validateIdentifier(keyField); + + int count = processDeleteOperations(conn, tableName, keyField, fieldValue); + + // 每个表单独统计 + operationCounts.merge(tableName, count, Integer::sum); + // 总数统计 + operationCounts.merge("total", count, Integer::sum); + + System.out.println("表 [" + tableName + "] 删除记录数: " + count); + } + + conn.commit(); + System.out.println("批量操作成功!操作统计: " + operationCounts); + return Result.ok("批量操作成功", operationCounts); + } catch (Exception e) { + rollbackTransaction(conn); + System.err.println("批量操作失败: " + e.getMessage()); + return Result.fail("批量操作失败: " + e.getMessage()); + } finally { + closeConnection(conn); + } + } + + // === 删除逻辑 === + private static int processDeleteOperations(Connection conn, String tableName, String keyField, String fieldValue) throws SQLException { + return deleteRecord(conn, tableName, keyField, fieldValue); + } + + private static int deleteRecord(Connection conn, String tableName, String pkField, String pkFieldvalue) throws SQLException { + if (pkFieldvalue == null) { + throw new IllegalArgumentException("缺少主键字段: " + pkField + " 值为空"); + } + + String sql = getDeleteSql(tableName, pkField); + System.out.println("[SQL-DELETE] " + sql); + System.out.println("[PARAMS] " + pkFieldvalue); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setObject(1, pkFieldvalue); + int affected = ps.executeUpdate(); + if (affected == 0) { + System.out.println("未找到要删除的记录: " + pkFieldvalue); + } + return affected; + } + } + + // === 辅助方法 === + private static void validateIdentifier(String identifier) { + if (!identifier.matches("^[a-zA-Z0-9_]+$")) { + throw new IllegalArgumentException("非法的表名或字段名: " + identifier); + } + } + + private static void rollbackTransaction(Connection conn) { + if (conn != null) { + try { + conn.rollback(); + } catch (SQLException ex) { + System.err.println("回滚事务时出错: " + ex.getMessage()); + } + } + } + + private static void closeConnection(Connection conn) { + if (conn != null) { + try { + conn.close(); + } catch (SQLException e) { + System.err.println("关闭数据库连接时出错: " + e.getMessage()); + } + } + } + + public static String getDeleteSql(String tableName, String pkName) { + return "DELETE FROM " + tableName + " WHERE " + pkName + " = ?"; + } +} diff --git a/code/link/link-backend/src/main/java/com/link/api/util/BatchTableV2.java b/code/link/link-backend/src/main/java/com/link/api/util/BatchTableV2.java new file mode 100644 index 00000000..1b0fa5c9 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/BatchTableV2.java @@ -0,0 +1,280 @@ +package com.link.api.util; + +import java.sql.*; +import java.util.*; +import java.util.stream.Collectors; + +public class BatchTableV2 { + + // 字段类型缓存:避免每次都查元数据 + private static final Map> columnTypeCache = new HashMap<>(); + + public static Result> executeDatabaseOperations(String orgCode, List> tableMaps) { + // 初始化操作计数器 + Map operationCounts = new HashMap<>(); + operationCounts.put("insert", 0); + operationCounts.put("update", 0); + operationCounts.put("delete", 0); + operationCounts.put("total", 0); + + Connection conn = null; + try { + // 1. 获取数据库连接并开启事务 + conn = DruidDataSourceManager.getConnection(orgCode); + conn.setAutoCommit(false); + + // 2. 遍历所有表操作 + for (Map tablemap : tableMaps) { + String tableName = (String) tablemap.get("tableName"); + String keyField = (String) tablemap.get("keyField"); + List> insertList = (List>) tablemap.get("inserts"); + List> updateList = (List>) tablemap.get("updates"); + List> deleteList = (List>) tablemap.get("deletes"); + + // 执行 INSERT + if (insertList != null && !insertList.isEmpty()) { + int count = processInsertOperations(conn, tableName, insertList); + operationCounts.put("insert", operationCounts.get("insert") + count); + operationCounts.put("total", operationCounts.get("total") + count); + } + + // 执行 UPDATE + if (updateList != null && !updateList.isEmpty()) { + int count = processUpdateOperations(conn, tableName, keyField, updateList); + operationCounts.put("update", operationCounts.get("update") + count); + operationCounts.put("total", operationCounts.get("total") + count); + } + + // 执行 DELETE + if (deleteList != null && !deleteList.isEmpty()) { + int count = processDeleteOperations(conn, tableName, keyField, deleteList); + operationCounts.put("delete", operationCounts.get("delete") + count); + operationCounts.put("total", operationCounts.get("total") + count); + } + } + + conn.commit(); + System.out.println("批量操作成功!操作统计: " + operationCounts); + return Result.ok("批量操作成功", operationCounts); + } catch (SQLException e) { + rollbackTransaction(conn); + System.err.println("批量操作失败,事务已回滚: " + e.getMessage()); + return Result.fail("批量操作失败: " + e.getMessage()); + } catch (Exception e) { + rollbackTransaction(conn); + System.err.println("批量操作失败: " + e.getMessage()); + return Result.fail("批量操作失败: " + e.getMessage()); + } finally { + closeConnection(conn); + } + } + + // 处理插入操作 + private static int processInsertOperations(Connection conn, String tableName, + List> insertList) throws SQLException { + int totalInserted = 0; + for (Map record : insertList) { + totalInserted += insertRecord(conn, tableName, record); + } + return totalInserted; + } + + // 处理更新操作 + private static int processUpdateOperations(Connection conn, String tableName, String keyField, + List> updateList) throws SQLException { + int totalUpdated = 0; + for (Map record : updateList) { + totalUpdated += updateRecord(conn, tableName, keyField, record); + } + return totalUpdated; + } + + // 处理删除操作 + private static int processDeleteOperations(Connection conn, String tableName, String keyField, + List> deleteList) throws SQLException { + int totalDeleted = 0; + for (Map record : deleteList) { + totalDeleted += deleteRecord(conn, tableName, keyField, record); + } + return totalDeleted; + } + + // 插入单条记录 + private static int insertRecord(Connection conn, String tableName, Map data) throws SQLException { + List insertFields = new ArrayList<>(); + String sql = BuildSql.buildInsertSql(conn, tableName, insertFields); + + System.out.println("[SQL-INSERT] " + sql); + System.out.println("[PARAMS] " + insertFields.stream().map(data::get).collect(Collectors.toList())); + + Map columnTypes = getColumnTypes(conn, tableName); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + for (int i = 0; i < insertFields.size(); i++) { + String field = insertFields.get(i); + String colType = columnTypes.getOrDefault(field.toLowerCase(), "varchar"); + Object value = convertValueByColumnType(colType, data.get(field)); + ps.setObject(i + 1, value); + } + + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("INSERT 失败:未插入任何数据,数据内容:" + data); + } + return affected; + } + } + + // 更新单条记录 + private static int updateRecord(Connection conn, String tableName, String pkField, + Map data) throws SQLException { + Object pkValue = data.get(pkField); + if (pkValue == null) { + throw new IllegalArgumentException("缺少主键字段: " + pkField + " in " + data); + } + + Set changedFields = data.keySet().stream() + .filter(field -> !field.equalsIgnoreCase(pkField)) + .collect(Collectors.toSet()); + if (changedFields.isEmpty()) { + throw new IllegalArgumentException("没有可更新的字段: " + data); + } + String sql = buildDynamicUpdateSql(tableName, pkField, changedFields); + Map columnTypes = getColumnTypes(conn, tableName); + + List values = new ArrayList<>(); + for (String field : changedFields) { + String colType = columnTypes.getOrDefault(field.toLowerCase(), "varchar"); + values.add(convertValueByColumnType(colType, data.get(field))); + } + values.add(pkValue); + + System.out.println("[SQL-UPDATE] " + sql); + System.out.println("[PARAMS] " + values); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + for (int i = 0; i < values.size(); i++) { + ps.setObject(i + 1, values.get(i)); + } + + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("UPDATE 失败:未匹配记录,数据内容:" + data); + } + return affected; + } + } + + // 删除单条记录 + private static int deleteRecord(Connection conn, String tableName, String pkField, + Map row) throws SQLException { + Object pkValue = row.get(pkField); + if (pkValue == null) { + throw new IllegalArgumentException("缺少主键字段: " + pkField + " in " + row); + } + + String sql = BuildSql.buildDeleteSql(tableName, pkField); + System.out.println("[SQL-DELETE] " + sql); + System.out.println("[PARAMS] " + pkValue); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setObject(1, pkValue); + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("DELETE 失败(未匹配记录): " + row); + } + return affected; + } + } + + // 事务回滚 + private static void rollbackTransaction(Connection conn) { + if (conn != null) { + try { + conn.rollback(); + } catch (SQLException ex) { + System.err.println("回滚事务时出错: " + ex.getMessage()); + } + } + } + + // 关闭连接 + private static void closeConnection(Connection conn) { + if (conn != null) { + try { + conn.setAutoCommit(true); + conn.close(); + } catch (SQLException e) { + System.err.println("关闭数据库连接时出错: " + e.getMessage()); + } + } + } + + /** + * 构建动态UPDATE SQL语句 + */ + private static String buildDynamicUpdateSql(String table, String pkField, Set fieldsToUpdate) { + StringBuilder sql = new StringBuilder("UPDATE ") + .append(table) + .append(" SET "); + + String setClause = fieldsToUpdate.stream() + .map(field -> field + " = ?") + .collect(Collectors.joining(", ")); + sql.append(setClause); + + sql.append(" WHERE ").append(pkField).append(" = ?"); + + return sql.toString(); + } + + /** + * 获取表字段类型(带缓存) + */ + private static Map getColumnTypes(Connection conn, String tableName) throws SQLException { + if (columnTypeCache.containsKey(tableName)) { + return columnTypeCache.get(tableName); + } + + Map columnTypes = new HashMap<>(); + String sql = "SELECT COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ?"; + try (PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setString(1, tableName); + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + columnTypes.put(rs.getString("COLUMN_NAME").toLowerCase(), + rs.getString("DATA_TYPE").toLowerCase()); + } + } + } + columnTypeCache.put(tableName, columnTypes); + return columnTypes; + } + + /** + * 根据字段类型转换值(数字字段:空字符串转0) + */ + private static Object convertValueByColumnType(String columnType, Object value) { + if (value == null) return null; + + if (value instanceof String && ((String) value).trim().isEmpty()) { + if (isNumericType(columnType)) { + return 0; + } + } + + return value; + } + + /** + * 判断是否为数字类型 + */ + private static boolean isNumericType(String columnType) { + return columnType.contains("int") + || columnType.contains("decimal") + || columnType.contains("numeric") + || columnType.contains("float") + || columnType.contains("real") + || columnType.contains("money"); + } +} diff --git a/code/link/link-backend/src/main/java/com/link/api/util/BatchTableV220250829.java b/code/link/link-backend/src/main/java/com/link/api/util/BatchTableV220250829.java new file mode 100644 index 00000000..707c492b --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/BatchTableV220250829.java @@ -0,0 +1,201 @@ +package com.link.api.util; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class BatchTableV220250829 { + + public static Result> executeDatabaseOperations(String orgCode, List> tableMaps) { + // 初始化操作计数器 + Map operationCounts = new HashMap<>(); + operationCounts.put("insert", 0); + operationCounts.put("update", 0); + operationCounts.put("delete", 0); + operationCounts.put("total", 0); + + Connection conn = null; + try { + // 1. 获取数据库连接并开启事务 + conn = DruidDataSourceManager.getConnection(orgCode); + conn.setAutoCommit(false); + + // 2. 遍历所有表操作 + for (Map tablemap : tableMaps) { + String tableName = (String) tablemap.get("tableName"); + String keyField = (String) tablemap.get("keyField"); + List> insertList = (List>) tablemap.get("inserts"); + List> updateList = (List>) tablemap.get("updates"); + List> deleteList = (List>) tablemap.get("deletes"); + + // 执行 INSERT + if (insertList != null && !insertList.isEmpty()) { + int count = processInsertOperations(conn, tableName, insertList); + operationCounts.put("insert", operationCounts.get("insert") + count); + operationCounts.put("total", operationCounts.get("total") + count); + } + + // 执行 UPDATE + if (updateList != null && !updateList.isEmpty()) { + int count = processUpdateOperations(conn, tableName, keyField, updateList); + operationCounts.put("update", operationCounts.get("update") + count); + operationCounts.put("total", operationCounts.get("total") + count); + } + + // 执行 DELETE + if (deleteList != null && !deleteList.isEmpty()) { + int count = processDeleteOperations(conn, tableName, keyField, deleteList); + operationCounts.put("delete", operationCounts.get("delete") + count); + operationCounts.put("total", operationCounts.get("total") + count); + } + } + + conn.commit(); + System.out.println("批量操作成功!操作统计: " + operationCounts); + return Result.ok("批量操作成功", operationCounts); + } catch (SQLException e) { + rollbackTransaction(conn); + System.err.println("批量操作失败,事务已回滚: " + e.getMessage()); + return Result.fail("批量操作失败: " + e.getMessage()); + } catch (Exception e) { + rollbackTransaction(conn); + System.err.println("批量操作失败: " + e.getMessage()); + return Result.fail("批量操作失败: " + e.getMessage()); + } finally { + closeConnection(conn); + } + } + + // 处理插入操作 + private static int processInsertOperations(Connection conn, String tableName, + List> insertList) throws SQLException { + int totalInserted = 0; + for (Map record : insertList) { + totalInserted += insertRecord(conn, tableName, record); + } + return totalInserted; + } + + // 处理更新操作 + private static int processUpdateOperations(Connection conn, String tableName, String keyField, + List> updateList) throws SQLException { + int totalUpdated = 0; + for (Map record : updateList) { + totalUpdated += updateRecord(conn, tableName, keyField, record); + } + return totalUpdated; + } + + // 处理删除操作 + private static int processDeleteOperations(Connection conn, String tableName, String keyField, + List> deleteList) throws SQLException { + int totalDeleted = 0; + for (Map record : deleteList) { + totalDeleted += deleteRecord(conn, tableName, keyField, record); + } + return totalDeleted; + } + + // 插入单条记录 + private static int insertRecord(Connection conn, String tableName, Map data) throws SQLException { + List insertFields = new ArrayList<>(); + String sql = BuildSql.buildInsertSql(conn, tableName, insertFields); + + System.out.println("[SQL-INSERT] " + sql); + System.out.println("[PARAMS] " + insertFields.stream().map(data::get).collect(Collectors.toList())); + + + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + for (int i = 0; i < insertFields.size(); i++) { + ps.setObject(i + 1, data.get(insertFields.get(i))); + } + + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("INSERT 失败:未插入任何数据,数据内容:" + data); + } + return affected; + } + } + + // 更新单条记录 + private static int updateRecord(Connection conn, String tableName, String pkField, + Map data) throws SQLException { + Object pkValue = data.get(pkField); + if (pkValue == null) { + throw new IllegalArgumentException("缺少主键字段: " + pkField + " in " + data); + } + + List updateFields = new ArrayList<>(); + String sql = BuildSql.buildUpdateSql(conn, tableName, pkField, updateFields); + List values = updateFields.stream() + .map(data::get) + .collect(Collectors.toList()); + + System.out.println("[SQL-UPDATE] " + sql); + System.out.println("[PARAMS] " + values); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + for (int i = 0; i < values.size(); i++) { + ps.setObject(i + 1, values.get(i)); + } + + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("UPDATE 失败:未匹配记录,数据内容:" + data); + } + return affected; + } + } + + // 删除单条记录 + private static int deleteRecord(Connection conn, String tableName, String pkField, + Map row) throws SQLException { + Object pkValue = row.get(pkField); + if (pkValue == null) { + throw new IllegalArgumentException("缺少主键字段: " + pkField + " in " + row); + } + + String sql = BuildSql.buildDeleteSql(tableName, pkField); + System.out.println("[SQL-DELETE] " + sql); + System.out.println("[PARAMS] " + pkValue); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setObject(1, pkValue); + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("DELETE 失败(未匹配记录): " + row); + } + return affected; + } + } + + // 事务回滚 + private static void rollbackTransaction(Connection conn) { + if (conn != null) { + try { + conn.rollback(); + } catch (SQLException ex) { + System.err.println("回滚事务时出错: " + ex.getMessage()); + } + } + } + + // 关闭连接 + private static void closeConnection(Connection conn) { + if (conn != null) { + try { + conn.setAutoCommit(true); + conn.close(); + } catch (SQLException e) { + System.err.println("关闭数据库连接时出错: " + e.getMessage()); + } + } + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/util/BatchTableV2bk.java b/code/link/link-backend/src/main/java/com/link/api/util/BatchTableV2bk.java new file mode 100644 index 00000000..dfc725b2 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/BatchTableV2bk.java @@ -0,0 +1,238 @@ +package com.link.api.util; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.*; +import java.util.stream.Collectors; + +public class BatchTableV2bk { + + public static Result> executeDatabaseOperations(String orgCode, List> tableMaps) { + // 初始化操作计数器 + Map operationCounts = new HashMap<>(); + operationCounts.put("insert", 0); + operationCounts.put("update", 0); + operationCounts.put("delete", 0); + operationCounts.put("total", 0); + + Connection conn = null; + try { + // 1. 获取数据库连接并开启事务 + conn = DruidDataSourceManager.getConnection(orgCode); + conn.setAutoCommit(false); + + // 2. 遍历所有表操作 + for (Map tablemap : tableMaps) { + String tableName = (String) tablemap.get("tableName"); + String keyField = (String) tablemap.get("keyField"); + List> insertList = (List>) tablemap.get("inserts"); + List> updateList = (List>) tablemap.get("updates"); + List> deleteList = (List>) tablemap.get("deletes"); + + // 执行 INSERT + if (insertList != null && !insertList.isEmpty()) { + int count = processInsertOperations(conn, tableName, insertList); + operationCounts.put("insert", operationCounts.get("insert") + count); + operationCounts.put("total", operationCounts.get("total") + count); + } + + // 执行 UPDATE + if (updateList != null && !updateList.isEmpty()) { + int count = processUpdateOperations(conn, tableName, keyField, updateList); + operationCounts.put("update", operationCounts.get("update") + count); + operationCounts.put("total", operationCounts.get("total") + count); + } + + // 执行 DELETE + if (deleteList != null && !deleteList.isEmpty()) { + int count = processDeleteOperations(conn, tableName, keyField, deleteList); + operationCounts.put("delete", operationCounts.get("delete") + count); + operationCounts.put("total", operationCounts.get("total") + count); + } + } + + conn.commit(); + System.out.println("批量操作成功!操作统计: " + operationCounts); + return Result.ok("批量操作成功", operationCounts); + } catch (SQLException e) { + rollbackTransaction(conn); + System.err.println("批量操作失败,事务已回滚: " + e.getMessage()); + return Result.fail("批量操作失败: " + e.getMessage()); + } catch (Exception e) { + rollbackTransaction(conn); + System.err.println("批量操作失败: " + e.getMessage()); + return Result.fail("批量操作失败: " + e.getMessage()); + } finally { + closeConnection(conn); + } + } + + // 处理插入操作 + private static int processInsertOperations(Connection conn, String tableName, + List> insertList) throws SQLException { + int totalInserted = 0; + for (Map record : insertList) { + totalInserted += insertRecord(conn, tableName, record); + } + return totalInserted; + } + + // 处理更新操作 + private static int processUpdateOperations(Connection conn, String tableName, String keyField, + List> updateList) throws SQLException { + int totalUpdated = 0; + for (Map record : updateList) { + totalUpdated += updateRecord(conn, tableName, keyField, record); + } + return totalUpdated; + } + + // 处理删除操作 + private static int processDeleteOperations(Connection conn, String tableName, String keyField, + List> deleteList) throws SQLException { + int totalDeleted = 0; + for (Map record : deleteList) { + totalDeleted += deleteRecord(conn, tableName, keyField, record); + } + return totalDeleted; + } + + // 插入单条记录 + private static int insertRecord(Connection conn, String tableName, Map data) throws SQLException { + List insertFields = new ArrayList<>(); + String sql = BuildSql.buildInsertSql(conn, tableName, insertFields); + + System.out.println("[SQL-INSERT] " + sql); + System.out.println("[PARAMS] " + insertFields.stream().map(data::get).collect(Collectors.toList())); + + + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + for (int i = 0; i < insertFields.size(); i++) { + ps.setObject(i + 1, data.get(insertFields.get(i))); + } + + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("INSERT 失败:未插入任何数据,数据内容:" + data); + } + return affected; + } + } + + // 更新单条记录 + private static int updateRecord(Connection conn, String tableName, String pkField, + Map data) throws SQLException { + Object pkValue = data.get(pkField); + if (pkValue == null) { + throw new IllegalArgumentException("缺少主键字段: " + pkField + " in " + data); + } + + // 排除主键字段,只获取需要更新的字段 + Set changedFields = data.keySet().stream() + .filter(field -> !field.equalsIgnoreCase(pkField)) + .collect(Collectors.toSet()); + + if (changedFields.isEmpty()) { + throw new IllegalArgumentException("没有可更新的字段: " + data); + } + + // 构建动态SQL + String sql = buildDynamicUpdateSql(tableName, pkField, changedFields); + + + // 准备参数值 + List values = new ArrayList<>(); + for (String field : changedFields) { + values.add(data.get(field)); + } + // 最后添加主键值作为WHERE条件 + values.add(pkValue); + + System.out.println("[SQL-UPDATE] " + sql); + System.out.println("[PARAMS] " + values); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + for (int i = 0; i < values.size(); i++) { + ps.setObject(i + 1, values.get(i)); + } + + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("UPDATE 失败:未匹配记录,数据内容:" + data); + } + return affected; + } + } + + // 删除单条记录 + private static int deleteRecord(Connection conn, String tableName, String pkField, + Map row) throws SQLException { + Object pkValue = row.get(pkField); + if (pkValue == null) { + throw new IllegalArgumentException("缺少主键字段: " + pkField + " in " + row); + } + + String sql = BuildSql.buildDeleteSql(tableName, pkField); + System.out.println("[SQL-DELETE] " + sql); + System.out.println("[PARAMS] " + pkValue); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + ps.setObject(1, pkValue); + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("DELETE 失败(未匹配记录): " + row); + } + return affected; + } + } + + // 事务回滚 + private static void rollbackTransaction(Connection conn) { + if (conn != null) { + try { + conn.rollback(); + } catch (SQLException ex) { + System.err.println("回滚事务时出错: " + ex.getMessage()); + } + } + } + + // 关闭连接 + private static void closeConnection(Connection conn) { + if (conn != null) { + try { + conn.setAutoCommit(true); + conn.close(); + } catch (SQLException e) { + System.err.println("关闭数据库连接时出错: " + e.getMessage()); + } + } + } + + /** + * 构建动态UPDATE SQL语句 + * + * @param table 表名 + * @param pkField 主键字段名 + * @param fieldsToUpdate 需要更新的字段集合 + * @return 生成的SQL语句 + */ + private static String buildDynamicUpdateSql(String table, String pkField, Set fieldsToUpdate) { + StringBuilder sql = new StringBuilder("UPDATE ") + .append(table) + .append(" SET "); + + // 添加要更新的字段 + String setClause = fieldsToUpdate.stream() + .map(field -> field + " = ?") + .collect(Collectors.joining(", ")); + sql.append(setClause); + + // 添加WHERE条件 + sql.append(" WHERE ").append(pkField).append(" = ?"); + + return sql.toString(); + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/util/BatchTableV3.java b/code/link/link-backend/src/main/java/com/link/api/util/BatchTableV3.java new file mode 100644 index 00000000..7b0aeb98 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/BatchTableV3.java @@ -0,0 +1,209 @@ +package com.link.api.util; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.*; +import java.util.stream.Collectors; + +public class BatchTableV3 { + + public static Result> executeDatabaseOperations(String orgCode, List> tableMaps) { + // 初始化操作计数器 + Map operationCounts = new HashMap<>(); + operationCounts.put("insert", 0); + operationCounts.put("update", 0); + operationCounts.put("delete", 0); + operationCounts.put("total", 0); + + Connection conn = null; + try { + conn = DruidDataSourceManager.getConnection(orgCode); + conn.setAutoCommit(false); + + // 遍历所有表操作 + for (Map tablemap : tableMaps) { + String tableName = (String) tablemap.get("tableName"); + @SuppressWarnings("unchecked") + List keyFields = (List) tablemap.get("keyFields"); // ✅ 多主键字段 + List> insertList = (List>) tablemap.get("inserts"); + List> updateList = (List>) tablemap.get("updates"); + List> deleteList = (List>) tablemap.get("deletes"); + + // 执行 DELETE + if (deleteList != null && !deleteList.isEmpty()) { + int count = processDeleteOperations(conn, tableName, keyFields, deleteList); + operationCounts.put("delete", operationCounts.get("delete") + count); + operationCounts.put("total", operationCounts.get("total") + count); + } + + // 执行 UPDATE + if (updateList != null && !updateList.isEmpty()) { + int count = processUpdateOperations(conn, tableName, keyFields, updateList); + operationCounts.put("update", operationCounts.get("update") + count); + operationCounts.put("total", operationCounts.get("total") + count); + } + + // 执行 INSERT + if (insertList != null && !insertList.isEmpty()) { + int count = processInsertOperations(conn, tableName, insertList); + operationCounts.put("insert", operationCounts.get("insert") + count); + operationCounts.put("total", operationCounts.get("total") + count); + } + + } + + conn.commit(); + System.out.println("批量操作成功!操作统计: " + operationCounts); + return Result.ok("批量操作成功", operationCounts); + } catch (SQLException e) { + rollbackTransaction(conn); + System.err.println("批量操作失败,事务已回滚: " + e.getMessage()); + return Result.fail("批量操作失败: " + e.getMessage()); + } catch (Exception e) { + rollbackTransaction(conn); + System.err.println("批量操作失败: " + e.getMessage()); + return Result.fail("批量操作失败: " + e.getMessage()); + } finally { + closeConnection(conn); + } + } + + // 处理插入操作 + private static int processInsertOperations(Connection conn, String tableName, + List> insertList) throws SQLException { + int totalInserted = 0; + for (Map record : insertList) { + totalInserted += insertRecord(conn, tableName, record); + } + return totalInserted; + } + + // 处理更新操作(支持多主键) + private static int processUpdateOperations(Connection conn, String tableName, List keyFields, + List> updateList) throws SQLException { + int totalUpdated = 0; + for (Map record : updateList) { + totalUpdated += updateRecord(conn, tableName, keyFields, record); + } + return totalUpdated; + } + + // 处理删除操作(支持多主键) + private static int processDeleteOperations(Connection conn, String tableName, List keyFields, + List> deleteList) throws SQLException { + int totalDeleted = 0; + for (Map record : deleteList) { + totalDeleted += deleteRecord(conn, tableName, keyFields, record); + } + return totalDeleted; + } + + // 插入单条记录 + private static int insertRecord(Connection conn, String tableName, Map data) throws SQLException { + List insertFields = new ArrayList<>(); + String sql = BuildSql.buildInsertSql(conn, tableName, insertFields); + + System.out.println("[SQL-INSERT] " + sql); + System.out.println("[PARAMS] " + insertFields.stream().map(data::get).collect(Collectors.toList())); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + for (int i = 0; i < insertFields.size(); i++) { + ps.setObject(i + 1, data.get(insertFields.get(i))); + } + + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("INSERT 失败:未插入任何数据,数据内容:" + data); + } + return affected; + } + } + + // 更新单条记录(支持多主键) + private static int updateRecord(Connection conn, String tableName, List pkFields, + Map data) throws SQLException { + for (String pkField : pkFields) { + if (!data.containsKey(pkField)) { + throw new IllegalArgumentException("缺少主键字段: " + pkField + " in " + data); + } + } + + List updateFields = new ArrayList<>(); + String sql = BuildSql.buildUpdateSql(conn, tableName, pkFields, data, updateFields); + + // 直接按 outFields 获取参数 + List values = updateFields.stream() + .map(data::get) + .collect(Collectors.toList()); + + System.out.println("[SQL-UPDATE] " + sql); + System.out.println("[PARAMS] " + values); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + for (int i = 0; i < values.size(); i++) { + ps.setObject(i + 1, values.get(i)); + } + + int affected = ps.executeUpdate(); + if (affected == 0) { + throw new SQLException("UPDATE 失败:未匹配记录,数据内容:" + data); + } + return affected; + } + } + + + // 删除单条记录(支持多主键) + private static int deleteRecord(Connection conn, String tableName, List pkFields, + Map row) throws SQLException { + for (String pkField : pkFields) { + if (!row.containsKey(pkField)) { + throw new IllegalArgumentException("缺少主键字段: " + pkField + " in " + row); + } + } + + String sql = BuildSql.buildDeleteSql(tableName, pkFields); // ✅ 支持多主键 + List values = pkFields.stream() + .map(row::get) + .collect(Collectors.toList()); + + System.out.println("[SQL-DELETE] " + sql); + System.out.println("[PARAMS] " + values); + + try (PreparedStatement ps = conn.prepareStatement(sql)) { + for (int i = 0; i < values.size(); i++) { + ps.setObject(i + 1, values.get(i)); + } + + int affected = ps.executeUpdate(); + // 如果没有匹配记录,不再抛异常,而是直接返回 0 + return affected; + } + } + + // 事务回滚 + private static void rollbackTransaction(Connection conn) { + if (conn != null) { + try { + conn.rollback(); + } catch (SQLException ex) { + System.err.println("回滚事务时出错: " + ex.getMessage()); + } + } + } + + // 关闭连接 + private static void closeConnection(Connection conn) { + if (conn != null) { + try { + conn.setAutoCommit(true); + conn.close(); + } catch (SQLException e) { + System.err.println("关闭数据库连接时出错: " + e.getMessage()); + } + } + } + + +} diff --git a/code/link/link-backend/src/main/java/com/link/api/util/BuildSql.java b/code/link/link-backend/src/main/java/com/link/api/util/BuildSql.java new file mode 100644 index 00000000..7be6491e --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/BuildSql.java @@ -0,0 +1,186 @@ +package com.link.api.util; + +import java.sql.*; +import java.util.*; + +public class BuildSql { + + // 保留类型排除列表:部分字段可能用到 text + private static final Set EXCLUDED_TYPES = new HashSet<>(Arrays.asList( +// "image", "text", "timestamp", "uniqueidentifier" + "image", "timestamp", "uniqueidentifier" + )); + + /** + * 生成 INSERT SQL,例如:INSERT INTO table (col1, col2) VALUES (?, ?) + * @param conn 数据库连接 + * @param tableName 表名 + * @param outFields 输出参数,用于接收有效字段名(按顺序) + * @return 生成的 INSERT SQL + * @throws SQLException 发生 SQL 异常 + */ + public static String buildInsertSql(Connection conn, String tableName, List outFields) throws SQLException { + return buildSqlWithFields(conn, tableName, outFields, "insert"); + } + + /** + * 生成 UPDATE SQL,例如:UPDATE table SET col1=?, col2=? WHERE pkName=? + * @param conn 数据库连接 + * @param tableName 表名 + * @param pkName 主键字段名 + * @param outFields 输出参数,用于接收有效字段名(按顺序,最后是主键) + * @return 生成的 UPDATE SQL + * @throws SQLException 发生 SQL 异常 + */ + public static String buildUpdateSql(Connection conn, String tableName, String pkName, List outFields) throws SQLException { + StringBuilder sql = new StringBuilder("UPDATE ").append(tableName).append(" SET "); + List fields = getValidFields(conn, tableName); + + for (String col : fields) { + if (!col.equalsIgnoreCase(pkName)) { + sql.append(col).append(" = ?, "); + outFields.add(col); + } + } + + if (outFields.isEmpty()) throw new SQLException("无可更新字段"); + + sql.setLength(sql.length() - 2); // 去掉最后多余的逗号和空格 + sql.append(" WHERE ").append(pkName).append(" = ?"); + outFields.add(pkName); // 主键放到最后 + + return sql.toString(); + } + + /** + * 支持多主键,生成 UPDATE SQL,例如:UPDATE table SET col1=?, col2=? WHERE pk1=? AND pk2=? + * + * @param conn 数据库连接 + * @param tableName 表名 + * @param pkNames 多主键字段名列表 + * @param outFields 输出参数,用于接收有效字段名(按顺序,最后是主键) + * @return 生成的 UPDATE SQL + * @throws SQLException 发生 SQL 异常 + */ + public static String buildUpdateSql(Connection conn, String tableName, List pkNames, Map data, List outFields) throws SQLException { + if (pkNames == null || pkNames.isEmpty()) { + throw new SQLException("缺少主键字段"); + } + + StringBuilder sql = new StringBuilder("UPDATE ").append(tableName).append(" SET "); + List fields = getValidFields(conn, tableName); + + boolean hasUpdateField = false; + + // 拼接非主键字段,只更新 data 中存在的字段 + for (String col : fields) { + boolean isPk = pkNames.stream().anyMatch(pk -> pk.equalsIgnoreCase(col)); + if (!isPk && data.containsKey(col)) { + sql.append(col).append(" = ?, "); + outFields.add(col); + hasUpdateField = true; + } + } + + if (!hasUpdateField) { + throw new SQLException("没有可更新字段"); + } + + sql.setLength(sql.length() - 2); // 去掉最后逗号和空格 + + // 拼接 WHERE 多主键条件 + sql.append(" WHERE "); + for (int i = 0; i < pkNames.size(); i++) { + String pk = pkNames.get(i); + sql.append(pk).append(" = ?"); + if (i < pkNames.size() - 1) sql.append(" AND "); + outFields.add(pk); + } + + return sql.toString(); + } + + /** + * 生成 DELETE SQL,例如:DELETE FROM table WHERE pkName=? + * @param tableName 表名 + * @param pkName 主键字段名 + * @return 生成的 DELETE SQL + */ + public static String buildDeleteSql(String tableName, String pkName) { + return "DELETE FROM " + tableName + " WHERE " + pkName + " = ?"; + } + + /** + * 生成 DELETE SQL,例如: + * 单主键: DELETE FROM table WHERE id = ? + * 多主键: DELETE FROM table WHERE id = ? AND org_code = ? + * + * @param tableName 表名 + * @param pkNames 主键字段列表 + * @return 生成的 DELETE SQL + */ + public static String buildDeleteSql(String tableName, List pkNames) throws SQLException { + if (pkNames == null || pkNames.isEmpty()) { + throw new SQLException("缺少主键字段"); + } + + StringBuilder sql = new StringBuilder("DELETE FROM ").append(tableName).append(" WHERE "); + + for (int i = 0; i < pkNames.size(); i++) { + sql.append(pkNames.get(i)).append(" = ?"); + if (i < pkNames.size() - 1) { + sql.append(" AND "); + } + } + + return sql.toString(); + } + + /** + * 获取可插入字段(排除不支持的类型或自增字段) + */ + private static List getValidFields(Connection conn, String tableName) throws SQLException { + List fields = new ArrayList<>(); + String dummyQuery = "SELECT * FROM " + tableName + " WHERE 1=0"; + + try (PreparedStatement ps = conn.prepareStatement(dummyQuery); + ResultSet rs = ps.executeQuery()) { + ResultSetMetaData metaData = rs.getMetaData(); + for (int i = 1; i <= metaData.getColumnCount(); i++) { + String col = metaData.getColumnName(i); + String type = metaData.getColumnTypeName(i).toLowerCase(); + + // 自动排除自增字段和某些不支持类型 + if (EXCLUDED_TYPES.contains(type) || metaData.isAutoIncrement(i)) continue; + + fields.add(col); + } + } + + return fields; + } + + /** + * 内部方法:构建 INSERT SQL 语句 + */ + private static String buildSqlWithFields(Connection conn, String tableName, List outFields, String type) throws SQLException { + List fields = getValidFields(conn, tableName); + if (fields.isEmpty()) throw new SQLException("无可插入字段"); + + StringBuilder sql = new StringBuilder(); + StringBuilder placeholders = new StringBuilder(); + + sql.append("INSERT INTO ").append(tableName).append(" ("); + for (String col : fields) { + sql.append(col).append(","); + placeholders.append("?,"); + outFields.add(col); + } + + sql.setLength(sql.length() - 1); // 移除最后逗号 + placeholders.setLength(placeholders.length() - 1); + + sql.append(") VALUES (").append(placeholders).append(")"); + return sql.toString(); + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/util/ConnectionManager.java b/code/link/link-backend/src/main/java/com/link/api/util/ConnectionManager.java new file mode 100644 index 00000000..59c2ca29 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/ConnectionManager.java @@ -0,0 +1,19 @@ +package com.link.api.util; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; + +@Component +public class ConnectionManager { + + @Autowired + private DataSource dataSource; + + public Connection getConnection() throws SQLException { + return dataSource.getConnection(); + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/util/DruidDataSourceManager.java b/code/link/link-backend/src/main/java/com/link/api/util/DruidDataSourceManager.java new file mode 100644 index 00000000..531091c6 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/DruidDataSourceManager.java @@ -0,0 +1,61 @@ +package com.link.api.util; + + +import com.alibaba.druid.pool.DruidDataSource; + +import java.io.InputStream; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; + +public class DruidDataSourceManager { + private static final String CONFIG_PATH = "dbconfigs/"; // 放在 resources/dbconfigs 目录下 + private static final Map dataSourceMap = new ConcurrentHashMap<>(); + + public static synchronized Connection getConnection(String orgCode) throws SQLException { + DruidDataSource ds = dataSourceMap.get(orgCode); + if (ds == null) { + ds = createDataSourceFromFile(orgCode); + dataSourceMap.put(orgCode, ds); + } + return ds.getConnection(); + } + + private static DruidDataSource createDataSourceFromFile(String orgCode) { + String fileName = CONFIG_PATH + orgCode + ".properties"; + try (InputStream in = DruidDataSourceManager.class.getClassLoader().getResourceAsStream(fileName)) { + if (in == null) throw new RuntimeException("未找到机构配置文件: " + fileName); + Properties props = new Properties(); + props.load(in); + + DruidDataSource ds = new DruidDataSource(); + ds.setUrl(props.getProperty("url")); + ds.setUsername(props.getProperty("username")); + ds.setPassword(props.getProperty("password")); + ds.setDriverClassName(props.getProperty("driver")); + + // 可选参数 + ds.setInitialSize(3); + ds.setMinIdle(2); + ds.setMaxActive(10); + ds.setMaxWait(5000); + ds.setValidationQuery("SELECT 1"); + ds.setTestWhileIdle(true); + ds.setTestOnBorrow(false); + ds.setTestOnReturn(false); + + return ds; + } catch (Exception e) { + throw new RuntimeException("加载数据库配置失败: " + fileName, e); + } + } + + public static synchronized void closeAll() { + for (DruidDataSource ds : dataSourceMap.values()) { + ds.close(); + } + dataSourceMap.clear(); + } +} diff --git a/code/link/link-backend/src/main/java/com/link/api/util/JsonIMUtil.java b/code/link/link-backend/src/main/java/com/link/api/util/JsonIMUtil.java new file mode 100644 index 00000000..cd22d9d2 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/JsonIMUtil.java @@ -0,0 +1,26 @@ +package com.link.api.util; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class JsonIMUtil { + private static final ObjectMapper objectMapper = new ObjectMapper(); + + public static String toJson(Object obj) { + try { + return objectMapper.writeValueAsString(obj); + } catch (JsonProcessingException e) { + e.printStackTrace(); + return null; + } + } + + public static T fromJson(String json, Class clazz) { + try { + return objectMapper.readValue(json, clazz); + } catch (JsonProcessingException e) { + e.printStackTrace(); + return null; + } + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/util/JsonUtils.java b/code/link/link-backend/src/main/java/com/link/api/util/JsonUtils.java new file mode 100644 index 00000000..177efea0 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/JsonUtils.java @@ -0,0 +1,231 @@ +package com.link.api.util; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.sql.*; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class JsonUtils { + + private static final ObjectMapper mapper = new ObjectMapper(); + + + public static List> queryColumnConfig(String orgCode, String tableName) throws SQLException { + String sql = "SELECT col_FieldName, col_Caption, col_Width FROM s_columnLib WHERE col_TableName = ? and col_Visible= ? order by col_Position"; + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql) + ) { + stmt.setString(1, tableName); + stmt.setString(2, "1"); + + try (ResultSet rs = stmt.executeQuery()) { + return extractFieldMapList(rs); + } + + } catch (Exception e) { + throw new RuntimeException("字段配置查询失败", e); + } + } + + /** + * 把 ResultSet 映射为 prop/label/width 的字段 Map 列表 + */ + private static List> extractFieldMapList(ResultSet rs) throws SQLException { + List> list = new ArrayList<>(); + while (rs.next()) { + Map map = new LinkedHashMap<>(); + map.put("prop", rs.getObject("col_FieldName")); + map.put("label", rs.getObject("col_Caption")); + map.put("width", rs.getObject("col_Width")); + list.add(map); + } + return list; + } + + public static List> queryColumnEditConfig(String orgCode, String tableName) throws SQLException { + String sql = "SELECT col_FieldName,col_FieldName_v, col_Caption, col_Width, col_edit_type,col_edit_width,col_ReadOnly,col_edit_source_table,col_edit_source_field,col_edit_option FROM s_columnLib WHERE col_TableName = ? and col_edit= ? order by col_edit_position"; + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql) + ) { + stmt.setString(1, tableName); + stmt.setString(2, "1"); + + try (ResultSet rs = stmt.executeQuery()) { + return extractFieldMapList_edit(rs); + } + + } catch (Exception e) { + throw new RuntimeException("字段配置查询失败", e); + } + } + private static List> extractFieldMapList_edit(ResultSet rs) throws SQLException { + List> list = new ArrayList<>(); + while (rs.next()) { + Map map = new LinkedHashMap<>(); + map.put("prop", rs.getObject("col_FieldName")); + map.put("label", rs.getObject("col_Caption")); + map.put("width", rs.getObject("col_Width")); + map.put("edit_type", rs.getObject("col_edit_type")); + map.put("edit_width", rs.getObject("col_edit_width")); + map.put("readOnly", rs.getObject("col_ReadOnly")); + map.put("FieldName_v", rs.getObject("col_FieldName_v")); + map.put("edit_source_table", rs.getObject("col_edit_source_table")); + map.put("edit_source_field", rs.getObject("col_edit_source_field")); + map.put("edit_option", rs.getObject("col_edit_option")); + + + list.add(map); + } + return list; + } + public static List> queryColumnsSelectConfig(String orgCode, String tableName) throws SQLException { + String sql = "select * from s_selectcolumnlib where col_visible='1' and selectid = ? order by col_position"; + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql) + ) { + stmt.setString(1, tableName); + + try (ResultSet rs = stmt.executeQuery()) { + return extractFieldMapList_select(rs); + } + + } catch (Exception e) { + throw new RuntimeException("字段配置查询失败", e); + } + } + private static List> extractFieldMapList_select(ResultSet rs) throws SQLException { + List> list = new ArrayList<>(); + while (rs.next()) { + Map map = new LinkedHashMap<>(); + map.put("prop", rs.getObject("col_FieldName")); + map.put("label", rs.getObject("col_Caption")); + map.put("width", rs.getObject("col_Width")); + + list.add(map); + } + return list; + } + + + public static void ColumnConfigJson(String orgCode, String tableName) throws SQLException { + String sql = "SELECT col_FieldName, col_Caption, col_Width FROM s_columnLib WHERE col_TableName = ?"; + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql) + ) { + stmt.setString(1, tableName); + + try (ResultSet rs = stmt.executeQuery()) { + String json = toFieldJsonArray(rs); + System.out.println(json); // 控制台输出 + } + + } catch (Exception e) { + throw new RuntimeException("字段配置查询失败", e); + } + } + + + public static String toFieldJsonArray(ResultSet rs) throws SQLException { + List> list = new ArrayList<>(); + while (rs.next()) { + Map map = new LinkedHashMap<>(); + map.put("prop", rs.getObject("col_FieldName")); + map.put("label", rs.getObject("col_Caption")); + map.put("width", rs.getObject("col_Width")); + list.add(map); + } + try { + return mapper.writeValueAsString(list); + } catch (Exception e) { + throw new RuntimeException("转换为字段 JSON 失败", e); + } + } + + + public static String toJsonArray(ResultSet rs) throws SQLException { + List> rows = resultSetToList(rs); + try { + return mapper.writeValueAsString(rows); + } catch (Exception e) { + throw new RuntimeException("转换为 JSON 失败", e); + } + } + + /** + * 将 ResultSet 转为 List> + */ + private static List> resultSetToList(ResultSet rs) throws SQLException { + List> rows = new ArrayList<>(); + ResultSetMetaData metaData = rs.getMetaData(); + int colCount = metaData.getColumnCount(); + + while (rs.next()) { + Map row = new LinkedHashMap<>(); + for (int i = 1; i <= colCount; i++) { + String colName = metaData.getColumnLabel(i); // 支持 SQL 别名 + Object value = rs.getObject(i); + row.put(colName, value); + } + rows.add(row); + } + + return rows; + } + + + public static List> querySearchConfig(String orgCode, String tableName) throws SQLException { + String sql = "SELECT col_fieldname,col_fieldname_v, col_caption, col_defaultValue,col_type, col_option,col_hit,col_source_table,col_source_field,col_source_option FROM s_search_sub WHERE father = ? and col_visible= ? order by col_position"; + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql) + ) { + stmt.setString(1, tableName); + stmt.setString(2, "1"); + + try (ResultSet rs = stmt.executeQuery()) { + return toSearchFieldJsonArray(rs); + } + + } catch (Exception e) { + throw new RuntimeException("字段配置查询失败", e); + } + } + + + private static List> toSearchFieldJsonArray(ResultSet rs) throws SQLException { + List> list = new ArrayList<>(); + while (rs.next()) { + Map map = new LinkedHashMap<>(); + + map.put("label", rs.getObject("col_caption")); + map.put("field", rs.getObject("col_fieldname")); + map.put("fieldname_v", rs.getObject("col_fieldname_v")); + map.put("type", rs.getObject("col_type")); + map.put("value", rs.getObject("col_defaultValue")); + map.put("placeholder", rs.getObject("col_hit")); + map.put("options", rs.getObject("col_option")); + map.put("sourceTable", rs.getObject("col_source_table")); + map.put("sourceTableField", rs.getObject("col_source_field")); + map.put("source_option", rs.getObject("col_source_option")); + + + list.add(map); + } + return list; + } + + + +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/util/JwtAuthenticationFilter.java b/code/link/link-backend/src/main/java/com/link/api/util/JwtAuthenticationFilter.java new file mode 100644 index 00000000..60529c4d --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/JwtAuthenticationFilter.java @@ -0,0 +1,78 @@ +package com.link.api.util; + +import org.springframework.web.filter.OncePerRequestFilter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +public class JwtAuthenticationFilter extends OncePerRequestFilter { + + // 可跳过 Token 校验的路径 + private static final List EXCLUDE_PATHS = Arrays.asList( + "/api/login", "/api/register", "/public", "/health" + ); + + @Override + protected void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + + // 0. 放行 OPTIONS 请求(解决 CORS 预检) + if ("OPTIONS".equals(request.getMethod())) { + response.setStatus(HttpServletResponse.SC_OK); + filterChain.doFilter(request, response); + return; + } + + try { + String path = request.getRequestURI(); + + // 1. 跳过不需要验证的路径 + if (shouldSkip(path)) { + filterChain.doFilter(request, response); + return; + } + + // 2. 获取并验证 Token + String token = request.getHeader("Authorization"); + if (token == null || !token.startsWith("Bearer ")) { + sendError(response, HttpServletResponse.SC_UNAUTHORIZED, "Missing or invalid Authorization header"); + return; + } + + token = token.substring(7); // 去掉 "Bearer " + JwtPayload payload = JwtUtil.parseToken(token); // 你的 JWT 解析方法 + if (payload == null) { + sendError(response, HttpServletResponse.SC_UNAUTHORIZED, "Invalid token"); + return; + } + + // 3. 保存用户上下文 + UserContext.set(payload.getUserid(), payload.getOrgid()); + + // 4. 放行请求 + filterChain.doFilter(request, response); + + } catch (Exception e) { + sendError(response, HttpServletResponse.SC_UNAUTHORIZED, "Token authentication failed: " + e.getMessage()); + } finally { + UserContext.clear(); // 清理上下文 + } + } + + // 判断是否跳过路径 + private boolean shouldSkip(String path) { + return EXCLUDE_PATHS.stream().anyMatch(path::startsWith); + } + + // 统一返回错误响应 + private void sendError(HttpServletResponse response, int status, String message) throws IOException { + response.setStatus(status); + response.setContentType("application/json"); + response.getWriter().write("{\"error\": \"" + message + "\"}"); + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/util/JwtPayload.java b/code/link/link-backend/src/main/java/com/link/api/util/JwtPayload.java new file mode 100644 index 00000000..d7a8d80a --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/JwtPayload.java @@ -0,0 +1,29 @@ +package com.link.api.util; + +public class JwtPayload { + private String userid; + private String orgid; + + public JwtPayload() {} + + public JwtPayload(String userid, String orgid) { + this.userid = userid; + this.orgid = orgid; + } + + public String getUserid() { + return userid; + } + + public String getOrgid() { + return orgid; + } + + public void setUserid(String userid) { + this.userid = userid; + } + + public void setOrgid(String orgid) { + this.orgid = orgid; + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/util/JwtUtil.java b/code/link/link-backend/src/main/java/com/link/api/util/JwtUtil.java new file mode 100644 index 00000000..a02ec1e6 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/JwtUtil.java @@ -0,0 +1,38 @@ +package com.link.api.util; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; + +import java.util.Date; + +public class JwtUtil { + private static final String SECRET_KEY = "your_secret_key"; + private static final long EXPIRATION = 86400000L; // 1天 + + public static String generateToken(String username,String orgid) { + return Jwts.builder() + .setSubject(username) + .claim("orgid", orgid) + .setIssuedAt(new Date()) + .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION)) + .signWith(SignatureAlgorithm.HS256, SECRET_KEY) + .compact(); + } + + public static JwtPayload parseToken(String token) { + try { + Claims claims = Jwts.parser() + .setSigningKey(SECRET_KEY) + .parseClaimsJws(token) + .getBody(); + + String username = claims.getSubject(); + String orgid = claims.get("orgid", String.class); + + return new JwtPayload(username, orgid); + } catch (Exception e) { + return null; + } + } +} diff --git a/code/link/link-backend/src/main/java/com/link/api/util/PaginationUtils.java b/code/link/link-backend/src/main/java/com/link/api/util/PaginationUtils.java new file mode 100644 index 00000000..cccabf46 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/PaginationUtils.java @@ -0,0 +1,474 @@ +package com.link.api.util; + + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.sql.*; +import java.sql.Date; +import java.text.SimpleDateFormat; +import java.util.*; + +public class PaginationUtils { + + private static final ObjectMapper mapper = new ObjectMapper(); + + /** + * SQL Server 2008 通用分页查询(支持 where 条件) + * + * @param orgCode 机构编码 + * @param tableName 表名 + * @param pkColumn 主键字段名(用于排序) + * @param page 当前页码(从 1 开始) + * @param size 每页条数 + * @param whereClause 传入的条件语句(如 "status = 1 AND name LIKE '%张%'"; 可为空) + * @return JSON 字符串(包含分页记录和总数) + */ + public static String paginateSqlServer2008( + String orgCode, + String tableName, + String pkColumn, + int page, + int size, + String whereClause + ) throws SQLException, JsonProcessingException { + + int startRow = (page - 1) * size + 1; + int endRow = page * size; + + // 构造 where 语句 + String where = (whereClause != null && !whereClause.trim().isEmpty()) + ? " WHERE " + whereClause + : ""; + + // 总数查询 SQL + String countSql = "SELECT COUNT(*) FROM " + tableName + where; + + // 分页数据 SQL + String dataSql = + "SELECT * FROM (" + + " SELECT ROW_NUMBER() OVER (ORDER BY " + pkColumn + ") AS row_num, * " + + " FROM " + tableName + where + + ") AS t WHERE row_num BETWEEN ? AND ?"; + System.out.println(dataSql); + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement countStmt = conn.prepareStatement(countSql); + PreparedStatement dataStmt = conn.prepareStatement(dataSql) + ) { + // 设置分页参数 + dataStmt.setInt(1, startRow); + dataStmt.setInt(2, endRow); + + // 执行总数查询 + int total = 0; + try (ResultSet rs = countStmt.executeQuery()) { + if (rs.next()) { + total = rs.getInt(1); + } + } + + // 执行数据查询 + List> records; + try (ResultSet rs = dataStmt.executeQuery()) { + records = resultSetToList(rs); + } + + // 封装统一结构 + Map result = new LinkedHashMap<>(); + result.put("total", total); + result.put("records", records); + + return mapper.writeValueAsString(Result.ok("分页查询成功", result)); + + } catch (Exception e) { + return mapper.writeValueAsString(Result.fail("分页查询失败:" + e.getMessage())); + } + } + + + public static String detailSqlServer2008( + String orgCode, + String tableName, + String pkColumn, + String whereClause + ) throws SQLException, JsonProcessingException { + // 构造 where 语句 + String where = (whereClause != null && !whereClause.trim().isEmpty()) + ? " WHERE " + whereClause + : ""; + // 分页数据 SQL + String dataSql = "SELECT * FROM " + tableName + where; + System.out.println("dataSql=--==="+dataSql); + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement dataStmt = conn.prepareStatement(dataSql) + ) { + + List> records; + try (ResultSet rs = dataStmt.executeQuery()) { + records = resultSetToList(rs); + } + // 封装统一结构 + Map result = new LinkedHashMap<>(); + result.put("records", records); + return mapper.writeValueAsString(Result.ok("分页查询成功", result)); + } catch (Exception e) { + return mapper.writeValueAsString(Result.fail("分页查询失败:" + e.getMessage())); + } + } + + + public static String getbasicdataSqlServer2008(String orgCode, String tableName,String query) + throws SQLException, JsonProcessingException { + + // ✅ 安全性校验:只允许字母、数字和下划线 + if (!tableName.matches("^[a-zA-Z0-9_]+$")) { + throw new IllegalArgumentException("非法的表名"); + } + + // ✅ 拼接安全 SQL,表名拼接,其余参数使用占位符 +// String sql = String.format( +// "SELECT TOP 10 b_id, b_name FROM %s WHERE (b_id LIKE ? OR b_name LIKE ? ) and b_type like ? ORDER BY b_name", +// tableName +// ); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT TOP 10 b_id, b_name FROM ") + .append(tableName) + .append(" WHERE (b_id LIKE ? OR b_name LIKE ?) "); + + sql.append("ORDER BY b_name"); + + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql.toString()) + ) { + String likeQuery = "%" + query + "%"; + stmt.setString(1, likeQuery); + stmt.setString(2, likeQuery); + + List> records; + try (ResultSet rs = stmt.executeQuery()) { + records = resultSetToList(rs); + } + + Map result = new LinkedHashMap<>(); + result.put("records", records); + + return mapper.writeValueAsString(Result.ok("客户搜索成功", result)); + } catch (Exception e) { + return mapper.writeValueAsString(Result.fail("数据库查询失败:" + e.getMessage())); + } + } + + + public static String getbasicdatabyfieldnameSqlServer2008(String orgCode, String tableName, String fieldName, String query) + throws SQLException, JsonProcessingException { + + // ✅ 安全性校验:表名和字段名只允许字母、数字和下划线 + if (!tableName.matches("^[a-zA-Z0-9_]+$") || !fieldName.matches("^[a-zA-Z0-9_]+$")) { + throw new IllegalArgumentException("非法的表名或字段名"); + } + + String sql=""; + + if(tableName.equals("b_vessel")){ + sql = String.format( + "SELECT TOP 10 b_id, %s ,b_voy FROM %s WHERE %s LIKE ? ORDER BY %s", + fieldName, + tableName, // 表名(已校验) + fieldName, // 字段名(已校验) + fieldName // 排序字段(已校验) + ); + }else if(tableName.equals("b_money")){ + sql = String.format( + "SELECT TOP 10 b_id, %s,b_hl FROM %s WHERE %s LIKE ? ORDER BY %s", + fieldName, + tableName, // 表名(已校验) + fieldName, // 字段名(已校验) + fieldName // 排序字段(已校验) + ); + }else if(tableName.equals("v_cw_account")){ + sql = String.format( + "SELECT TOP 10 b_id, %s,b_account FROM %s WHERE %s LIKE ? ORDER BY %s", + fieldName, + tableName, // 表名(已校验) + fieldName, // 字段名(已校验) + fieldName // 排序字段(已校验) + ); + }else{ + // ✅ 动态生成安全SQL(使用参数化查询) + sql = String.format( + "SELECT TOP 10 b_id, %s FROM %s WHERE %s LIKE ? ORDER BY %s", + fieldName, + tableName, // 表名(已校验) + fieldName, // 字段名(已校验) + fieldName // 排序字段(已校验) + ); + } + + + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql) + ) { + // ✅ 参数化查询防止SQL注入 + String likeQuery = "%" + query + "%"; + stmt.setString(1, likeQuery); + + + List> records; + try (ResultSet rs = stmt.executeQuery()) { + records = resultSetToList(rs); + } + + Map result = new LinkedHashMap<>(); + result.put("records", records); + + return mapper.writeValueAsString(Result.ok("查询成功", result)); + } catch (Exception e) { + return mapper.writeValueAsString(Result.fail("数据库查询失败:" + e.getMessage())); + } + } + + + public static String getbasicdatabyidSqlServer2008( + String orgCode, String tableName, String fieldName, String query + ) throws SQLException, JsonProcessingException { + + // ✅ 校验表名和字段名合法性 + if (!tableName.matches("^[a-zA-Z0-9_]+$") || !fieldName.matches("^[a-zA-Z0-9_]+$")) { + throw new IllegalArgumentException("非法的表名或字段名"); + } + + StringBuilder sql = new StringBuilder(); + sql.append("SELECT TOP 10 b_id, ") + .append(fieldName ) + .append(" FROM " ) +// .append(" as b_name FROM ") + .append(tableName); + + + sql.append(" WHERE b_id LIKE ? OR ") + .append(fieldName) + .append(" LIKE ?"); + + + sql.append(" ORDER BY ").append(fieldName); + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql.toString()) + ) { + + String likeQuery = "%" + query + "%"; + stmt.setString(1, likeQuery); + stmt.setString(2, likeQuery); + + + List> records; + try (ResultSet rs = stmt.executeQuery()) { + records = resultSetToList(rs); + } + + Map result = new LinkedHashMap<>(); + result.put("records", records); + + return mapper.writeValueAsString(Result.ok("查询成功", result)); + + } catch (Exception e) { + return mapper.writeValueAsString(Result.fail("数据库查询失败:" + e.getMessage())); + } + } + + + + private static List> resultSetToList(ResultSet rs) throws SQLException { + List> list = new ArrayList<>(); + ResultSetMetaData meta = rs.getMetaData(); + int cols = meta.getColumnCount(); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + while (rs.next()) { + Map row = new LinkedHashMap<>(); + for (int i = 1; i <= cols; i++) { + String colName = meta.getColumnLabel(i); + Object val = rs.getObject(i); + if (val instanceof Timestamp || val instanceof Date) { + val = dateFormat.format(val); + } + + row.put(colName, val); + } + list.add(row); + } + + return list; + } + + public static String getbasicdatabyselectquerySqlServer2008(String orgCode, String tableName, String query, String orderBy) + throws SQLException, JsonProcessingException { + + // ✅ 安全性校验:表名和字段名只允许字母、数字和下划线 + if (!tableName.matches("^[a-zA-Z0-9_]+$") ) { + throw new IllegalArgumentException("非法的表名或字段名"); + } + + // ✅ 动态生成安全SQL(使用参数化查询)"SELECT TOP 10 b_id, b_name FROM %s WHERE b_id LIKE ? OR b_name LIKE ? ORDER BY b_name", + String sql = String.format( + "SELECT col_FieldName as b_id, col_Caption as b_name FROM %s WHERE %s ORDER BY %s", + tableName, // 表名(已校验) + query, + orderBy + ); + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql) + ) { + + List> records; + try (ResultSet rs = stmt.executeQuery()) { + records = resultSetToList(rs); + } + + Map result = new LinkedHashMap<>(); + result.put("records", records); + + return mapper.writeValueAsString(Result.ok("查询成功", result)); + } catch (Exception e) { + return mapper.writeValueAsString(Result.fail("数据库查询失败:" + e.getMessage())); + } + } + + public static String getDataBySql(String orgCode, String sql) throws JsonProcessingException { + // 简单安全检查,只允许 SELECT + if (sql == null || !sql.trim().toUpperCase().startsWith("SELECT")) { + return mapper.writeValueAsString(Result.fail("只允许执行 SELECT 查询语句")); + } + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql) + ) { + + List> records; + try (ResultSet rs = stmt.executeQuery()) { + records = resultSetToList(rs); + } + + Map result = new LinkedHashMap<>(); + result.put("records", records); + + return mapper.writeValueAsString(Result.ok("查询成功", result)); + } catch (Exception e) { + return mapper.writeValueAsString(Result.fail("数据库查询失败:" + e.getMessage())); + } + } + + + public static String paginateSelectSqlServer2008( + String orgCode, + String selectid, + String pkColumn, + int page, + int size, + String whereClause, + String[] psparams + ) throws SQLException, JsonProcessingException { + + int startRow = (page - 1) * size + 1; + int endRow = page * size; + + // 构造 where 语句 + String where = (whereClause != null && !whereClause.trim().isEmpty()) + ? " and " + whereClause + : ""; + + String sql = "select * from s_selectsql where selectid = ?"; + + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql); + stmt.setString(1, selectid); + + String selectsql=""; + String orderfield = ""; + + try (ResultSet rs = stmt.executeQuery()) { + if (rs.next()) { + selectsql = rs.getString("selectsql"); + + // 1. 把 :xxx 替换成 ? 占位符 + // 例如: "SELECT * FROM customer WHERE id=:id AND name=:name" + // 替换后: "SELECT * FROM customer WHERE id=? AND name=?" + selectsql = selectsql.replaceAll(":[a-zA-Z0-9_]+", "?"); + + // 用参数数组逐个替换 ? 占位符 + for (int i = 0; i < psparams.length; i++) { + String value = (psparams[i] == null) ? "NULL" : "'" + psparams[i] + "'"; + selectsql = selectsql.replaceFirst("\\?", value); + } + orderfield = rs.getString("orderfield"); + + System.out.println("最终SQL: " + selectsql); + } + } + + if (orderfield != null && !orderfield.trim().isEmpty()) { + // orderfield 不为空,可以使用 + pkColumn = orderfield; + } + + + // 总数查询 SQL + String countSql = "select count(*) from (" + selectsql + where + ") a"; + + // 分页数据 SQL + String dataSql = + "SELECT * FROM (" + + " SELECT ROW_NUMBER() OVER (ORDER BY " + pkColumn + ") AS row_num, t.* " + + " FROM (" + selectsql + " " + where + ") t " + + ") AS temp " + + "WHERE row_num BETWEEN ? AND ?"; + System.out.println(dataSql); + + try ( + PreparedStatement countStmt = conn.prepareStatement(countSql); + PreparedStatement dataStmt = conn.prepareStatement(dataSql) + ) { + // 设置分页参数 + dataStmt.setInt(1, startRow); + dataStmt.setInt(2, endRow); + + // 执行总数查询 + int total = 0; + try (ResultSet rs = countStmt.executeQuery()) { + if (rs.next()) { + total = rs.getInt(1); + } + } + + // 执行数据查询 + List> records; + try (ResultSet rs = dataStmt.executeQuery()) { + records = resultSetToList(rs); + } + + // 封装统一结构 + Map result = new LinkedHashMap<>(); + result.put("total", total); + result.put("records", records); + + return mapper.writeValueAsString(Result.ok("分页查询成功", result)); + + } catch (Exception e) { + return mapper.writeValueAsString(Result.fail("分页查询失败:" + e.getMessage())); + } + } + +} + diff --git a/code/link/link-backend/src/main/java/com/link/api/util/QueryByPkUtils.java b/code/link/link-backend/src/main/java/com/link/api/util/QueryByPkUtils.java new file mode 100644 index 00000000..642a1132 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/QueryByPkUtils.java @@ -0,0 +1,77 @@ +package com.link.api.util; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.sql.*; +import java.text.SimpleDateFormat; +import java.util.HashMap; +import java.util.Map; + +public class QueryByPkUtils { + + private static final ObjectMapper mapper = new ObjectMapper(); + + /** + * 根据主键查询单条记录 + * + * @param orgCode 机构编码 + * @param tableName 表名 + * @param pkColumn 主键字段名 + * @param pkColumnValue 主键值 + * @return JSON字符串(包含查询到的记录) + */ + public static String queryByPrimaryKey( + String orgCode, + String tableName, + String pkColumn, + String pkColumnValue + ) throws SQLException, JsonProcessingException { + + // 构造查询SQL + String sql = "SELECT * FROM " + tableName + " WHERE " + pkColumn + " = ?"; + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql) + ) { + // 设置主键参数 + stmt.setString(1, pkColumnValue); + + // 执行查询 + try (ResultSet rs = stmt.executeQuery()) { + if (rs.next()) { + Map record = resultSetToMap(rs); + return mapper.writeValueAsString(Result.ok("查询成功", record)); + } else { + return mapper.writeValueAsString(Result.fail("未找到对应记录")); + } + } + } catch (Exception e) { + return mapper.writeValueAsString(Result.fail("查询失败:" + e.getMessage())); + } + } + + /** + * 将ResultSet转换为Map + */ + private static Map resultSetToMap(ResultSet rs) throws SQLException { + Map map = new HashMap<>(); + ResultSetMetaData meta = rs.getMetaData(); + int cols = meta.getColumnCount(); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + for (int i = 1; i <= cols; i++) { + String colName = meta.getColumnLabel(i); + Object val = rs.getObject(i); + // 格式化日期类型字段 + if (val instanceof Timestamp || val instanceof Date) { + val = dateFormat.format(val); + } + + map.put(colName, val); + } + + return map; + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/util/QueryTreeUtils.java b/code/link/link-backend/src/main/java/com/link/api/util/QueryTreeUtils.java new file mode 100644 index 00000000..ac4237f7 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/QueryTreeUtils.java @@ -0,0 +1,66 @@ +package com.link.api.util; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.sql.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class QueryTreeUtils { + + private static final ObjectMapper mapper = new ObjectMapper(); + + + public static String getTreeNodes( + String orgCode, + String tableName, + String id, + int js + ) throws SQLException, JsonProcessingException { + + int js_new=js; + StringBuilder sql = new StringBuilder("SELECT * FROM " + tableName + " WHERE "); + + sql.append("js = ?"); + + if (id != null && !id.trim().isEmpty()) { + sql.append(" AND b_id LIKE ?"); // 使用预编译参数 + js_new=js_new+1; + } + + try ( + Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql.toString()) + ) { + // 设置主键参数 + stmt.setInt(1, js_new); // js参数 + + if (id != null && !id.trim().isEmpty()) { + stmt.setString(2, id + "_%"); + } + + // 执行查询 + ResultSet rs = stmt.executeQuery(); + + // 这里将结果构建为JSON(你原来抛出了 JsonProcessingException) + List> resultList = new ArrayList<>(); + ResultSetMetaData meta = rs.getMetaData(); + int columnCount = meta.getColumnCount(); + + while (rs.next()) { + Map row = new HashMap<>(); + for (int i = 1; i <= columnCount; i++) { + row.put(meta.getColumnLabel(i), rs.getObject(i)); + } + resultList.add(row); + } + + return mapper.writeValueAsString(Result.ok("查询成功", resultList)); + } catch (Exception e) { + return mapper.writeValueAsString(Result.fail("查询失败:" + e.getMessage())); + } + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/util/Result.java b/code/link/link-backend/src/main/java/com/link/api/util/Result.java new file mode 100644 index 00000000..fdfc61df --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/Result.java @@ -0,0 +1,40 @@ +package com.link.api.util; + +public class Result { + private boolean success; + private String message; + private T data; + + public Result() {} + + public Result(boolean success, String message) { + this.success = success; + this.message = message; + } + + public Result(boolean success, String message, T data) { + this.success = success; + this.message = message; + this.data = data; + } + + public static Result ok(String msg, T data) { + return new Result<>(true, msg, data); + } + + public static Result ok(String msg) { + return new Result<>(true, msg, null); + } + + public static Result fail(String msg) { + return new Result<>(false, msg, null); + } + + // Getters and setters omitted for brevity + public boolean isSuccess() { return success; } + public void setSuccess(boolean success) { this.success = success; } + public String getMessage() { return message; } + public void setMessage(String message) { this.message = message; } + public T getData() { return data; } + public void setData(T data) { this.data = data; } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/util/TableDataService.java b/code/link/link-backend/src/main/java/com/link/api/util/TableDataService.java new file mode 100644 index 00000000..1877ba55 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/TableDataService.java @@ -0,0 +1,176 @@ +package com.link.api.util; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.sql.*; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.Date; + +public class TableDataService { + private static final ObjectMapper mapper = new ObjectMapper(); + /** + * 获取表中不允许为空的字段(col_AllowNull="0") + * @param orgCode 组织编码 + * @param tableName 表名 + * @return 包含不允许为空字段的JSON + */ + public static String getMustFields(String orgCode, String tableName) throws JsonProcessingException { + try { + // 1. 获取不允许为空的字段配置 + List notNullFields = getNotNullFieldNames(orgCode, tableName); + if (notNullFields.isEmpty()) { + return toJson(Result.ok("该表所有字段都允许为空", Collections.emptyMap())); + } + + // 2. 构建字段名 -> "0" 的映射 + Map result = new LinkedHashMap<>(); + for (String field : notNullFields) { + result.put(field, "0"); + } + + return toJson(Result.ok("不允许为空的字段列表", result)); + } catch (SQLException e) { + return toJson(Result.fail("数据库错误: " + e.getMessage())); + } catch (JsonProcessingException e) { + return toJson(Result.fail("JSON生成失败")); + } + } + + /** + * 查询不允许为空的字段名列表 + */ + private static List getNotNullFieldNames(String orgCode, String tableName) throws SQLException { + List fieldNames = new ArrayList<>(); + String sql = "SELECT col_FieldName FROM s_columnLib WHERE col_TableName = ? AND col_AllowNull = '0'"; + + try (Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setString(1, tableName); + ResultSet rs = stmt.executeQuery(); + + while (rs.next()) { + fieldNames.add(rs.getString("col_FieldName")); + } + } + return fieldNames; + } + + /** + * 根据表名生成带默认值的JSON数据 + * @param orgCode 组织编码(用于获取数据源) + * @param tableName 需要生成数据的表名 + * @return 包含Result结果的JSON字符串 + * + */ + + public static String generateTableDataWithDefaults(String orgCode, String tableName) throws JsonProcessingException { + try { + // 1. 获取字段配置 + List configs = getFieldConfigs(orgCode, tableName); + if (configs.isEmpty()) { + return toJson(Result.fail("未找到表【" + tableName + "】的字段配置")); + } + + // 2. 构建数据 + Map data = buildDefaultData(configs); + + // 3. 返回成功结果 + return toJson(Result.ok("默认数据生成成功", data)); + } catch (SQLException e) { + return toJson(Result.fail("数据库操作失败: " + e.getMessage())); + } catch (JsonProcessingException e) { + return toJson(Result.fail("JSON序列化失败: " + e.getMessage())); + } catch (Exception e) { + return toJson(Result.fail("系统异常: " + e.getMessage())); + } + } + + /** + * 从s_columnLib获取字段配置 + */ + private static List getFieldConfigs(String orgCode, String tableName) throws SQLException { + List configs = new ArrayList<>(); + String sql = "SELECT col_FieldName, col_DefaultValue FROM s_columnLib WHERE col_TableName = ?"; + + try (Connection conn = DruidDataSourceManager.getConnection(orgCode); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setString(1, tableName); + try (ResultSet rs = stmt.executeQuery()) { + while (rs.next()) { + configs.add(new FieldConfig( + rs.getString("col_FieldName"), + rs.getString("col_DefaultValue") + )); + } + } + } + return configs; + } + + /** + * 构建默认数据映射 + */ + private static Map buildDefaultData(List configs) { + Map data = new LinkedHashMap<>(); + for (FieldConfig config : configs) { + data.put(config.fieldName, parseDefaultValue(config.defaultValue)); + } + return data; + } + + /** + * 智能解析默认值 + */ + private static Object parseDefaultValue(String value) { + if (value == null || value.trim().isEmpty()) { + return ""; + } + + // 特殊关键字处理 + switch (value.toLowerCase()) { + case "date": + // 返回当前日期(格式:yyyy-MM-dd) + return new SimpleDateFormat("yyyy-MM-dd").format(new Date()); + case "user": + // 返回 user+1(示例逻辑,可根据需求调整) + return UserContext.getUserId(); + case "now": + // 返回当前日期(格式:yyyy-MM-dd) + return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); + } + + // 布尔值处理 + if ("true".equalsIgnoreCase(value)) return true; + if ("false".equalsIgnoreCase(value)) return false; + + // 数字类型处理 + try { + return value.contains(".") + ? Double.parseDouble(value) + : Long.parseLong(value); + } catch (NumberFormatException ignored) { + return value; + } + } + + /** + * 对象转JSON(统一处理异常) + */ + private static String toJson(Object obj) throws JsonProcessingException { + return mapper.writeValueAsString(obj); + } + + // 字段配置内部类 + private static class FieldConfig { + final String fieldName; + final String defaultValue; + + FieldConfig(String fieldName, String defaultValue) { + this.fieldName = fieldName; + this.defaultValue = defaultValue; + } + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/api/util/Tools.java b/code/link/link-backend/src/main/java/com/link/api/util/Tools.java new file mode 100644 index 00000000..eb898a4b --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/Tools.java @@ -0,0 +1,121 @@ +package com.link.api.util; + +import java.util.HashMap; +import java.util.Map; + + +public final class Tools { + + // ================== Snowflake 单例模式 ================== + private static volatile Snowflake INSTANCE; + + /** + * 获取 Snowflake 单例(默认 workerId=1, datacenterId=1) + */ + public static Snowflake snowflake() { + if (INSTANCE == null) { + synchronized (Tools.class) { + if (INSTANCE == null) { + INSTANCE = new Snowflake(1, 1); + } + } + } + return INSTANCE; + } + + /** + * 快速生成唯一ID(使用默认单例) + */ + public static long nextId() { + return snowflake().nextId(); + } + + // ================== Snowflake 实现 ================== + public static final class Snowflake { + private static final long EPOCH = 1288834974657L; + private static final long WORKER_ID_BITS = 5L; + private static final long DATACENTER_ID_BITS = 5L; + private static final long SEQUENCE_BITS = 12L; + + private final long workerId; + private final long datacenterId; + private long sequence = 0L; + private long lastTimestamp = -1L; + + public Snowflake(long workerId, long datacenterId) { + this.workerId = workerId; + this.datacenterId = datacenterId; + validateIds(); + } + + public synchronized long nextId() { + long timestamp = System.currentTimeMillis(); + if (timestamp < lastTimestamp) { + throw new RuntimeException("时钟回拨!拒绝生成ID"); + } + if (timestamp == lastTimestamp) { + sequence = (sequence + 1) & ((1 << SEQUENCE_BITS) - 1); + if (sequence == 0) { + timestamp = tilNextMillis(lastTimestamp); + } + } else { + sequence = 0L; + } + lastTimestamp = timestamp; + return ((timestamp - EPOCH) << (WORKER_ID_BITS + SEQUENCE_BITS)) + | (datacenterId << (WORKER_ID_BITS + SEQUENCE_BITS)) + | (workerId << SEQUENCE_BITS) + | sequence; + } + + private void validateIds() { + long maxWorkerId = ~(-1L << WORKER_ID_BITS); + if (workerId > maxWorkerId || workerId < 0) { + throw new IllegalArgumentException("Worker ID 必须介于 0 和 " + maxWorkerId + " 之间"); + } + long maxDatacenterId = ~(-1L << DATACENTER_ID_BITS); + if (datacenterId > maxDatacenterId || datacenterId < 0) { + throw new IllegalArgumentException("Datacenter ID 必须介于 0 和 " + maxDatacenterId + " 之间"); + } + } + + private long tilNextMillis(long lastTimestamp) { + long timestamp = System.currentTimeMillis(); + while (timestamp <= lastTimestamp) { + timestamp = System.currentTimeMillis(); + } + return timestamp; + } + } + + // ================== Map 工具方法 ================== + public static Map mapOf(Object... kvs) { + if (kvs.length % 2 != 0) { + throw new IllegalArgumentException("参数必须是 key-value 对"); + } + Map map = new HashMap<>(); + for (int i = 0; i < kvs.length; i += 2) { + map.put(String.valueOf(kvs[i]), kvs[i + 1]); + } + return map; + } + + // 禁止实例化 + private Tools() {} +} + +//public class tools { +// +// public static Map mapOf(Object... kvs) { +// if (kvs.length % 2 != 0) { +// throw new IllegalArgumentException("参数个数必须为偶数,代表 key-value 对"); +// } +// +// Map map = new HashMap<>(); +// for (int i = 0; i < kvs.length; i += 2) { +// map.put(String.valueOf(kvs[i]), kvs[i + 1]); +// } +// return map; +// } +// +//} diff --git a/code/link/link-backend/src/main/java/com/link/api/util/UserContext.java b/code/link/link-backend/src/main/java/com/link/api/util/UserContext.java new file mode 100644 index 00000000..4dc65d67 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/api/util/UserContext.java @@ -0,0 +1,47 @@ +package com.link.api.util; + + +/** + * 用户上下文,用于存储每个请求线程的用户信息,如 userId 和 orgId(租户ID)。 + */ +public class UserContext { + + // 使用 ThreadLocal 保存当前线程的用户上下文 + private static final ThreadLocal userContextThreadLocal = new ThreadLocal<>(); + + private final String userId; + private final String orgId; // orgId 表示租户标识 + + private UserContext(String userId, String orgId) { + this.userId = userId; + this.orgId = orgId; + } + + /** 设置当前线程用户上下文 */ + public static void set(String userId, String orgId) { + userContextThreadLocal.set(new UserContext(userId, orgId)); + } + + /** 获取完整的 UserContext 对象 */ + public static UserContext get() { + return userContextThreadLocal.get(); + } + + /** 获取当前用户 ID(静态便捷方法) */ + public static String getUserId() { + UserContext context = get(); + return context != null ? context.userId : null; + } + + /** 获取当前 orgId(租户 ID) */ + public static String getOrgId() { + UserContext context = get(); + return context != null ? context.orgId : null; + } + + /** 清除上下文(防止内存泄露) */ + public static void clear() { + userContextThreadLocal.remove(); + } +} + diff --git a/code/link/link-backend/src/main/java/com/link/api/utils/ExceptionUtils.java b/code/link/link-backend/src/main/java/com/link/api/utils/ExceptionUtils.java deleted file mode 100644 index 5c82a493..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/utils/ExceptionUtils.java +++ /dev/null @@ -1,98 +0,0 @@ -package com.link.api.utils; - -import com.link.api.model.ExceptionEntity; -import com.link.api.exception.BaseException; - -import javax.servlet.http.HttpServletRequest; -import java.util.Date; - -import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Map; - -public class ExceptionUtils { - - private static final ObjectMapper objectMapper = new ObjectMapper(); - - /** - * 根据异常和请求信息,生成统一的异常信息对象 ExceptionDetailInfo - * @param ex 异常对象 - * @param request HttpServletRequest,用来获取请求路径和方法等 - * @param projectBasePackage 项目基础包名,如 "com.error",用来定位业务代码异常栈帧 - * @return ExceptionDetailInfo 封装的异常信息 - */ - public static ExceptionEntity extractExceptionInfo(Exception ex, HttpServletRequest request, String projectBasePackage) { - StackTraceElement[] stackTrace = ex.getStackTrace(); - - StackTraceElement source = null; - if (stackTrace != null) { - for (StackTraceElement element : stackTrace) { - if (element.getClassName().startsWith(projectBasePackage)) { - source = element; - break; - } - } - if (source == null && stackTrace.length > 0) { - source = stackTrace[0]; - } - } - - String briefMessage; - - if (ex instanceof BaseException) { - BaseException be = (BaseException) ex; - briefMessage = be.getMessage(); - } else { - if (ex.getMessage() != null) { - String[] lines = ex.getMessage().split("\n"); - // 遍历每一行,找到第一行“非空、非符号”的有效信息 - String usefulLine = null; - for (String line : lines) { - String trimmed = line.trim(); - if (!trimmed.isEmpty()) { - usefulLine = trimmed; - break; - } - } - // 如果找到有效行,就用它;否则仅用异常类名 - if (usefulLine != null) { - briefMessage = ex.getClass().getSimpleName() + ": " + usefulLine; - } else { - briefMessage = ex.getClass().getSimpleName(); - } - } else { - briefMessage = ex.getClass().getSimpleName(); - } - } - - String paramJson = null; - if (request != null) { - Map paramMap = new HashMap<>(); - Enumeration paramNames = request.getParameterNames(); - while (paramNames.hasMoreElements()) { - String paramName = paramNames.nextElement(); - paramMap.put(paramName, request.getParameter(paramName)); - } - try { - paramJson = JsonUtils.toJson(paramMap); - } catch (Exception e) { - paramJson = "参数序列化失败: " + e.getMessage(); - } - } - - return ExceptionEntity.builder() - .id(SnowflakeIdUtils.nextId()) - .uri(request != null ? request.getRequestURI() : null) - .httpMethod(request != null ? request.getMethod() : null) - .className(source != null ? source.getClassName() : null) - .methodName(source != null ? source.getMethodName() : null) - .fileName(source != null ? source.getFileName() : null) - .lineNumber(source != null ? source.getLineNumber() : null) - .summary(briefMessage) - .detailMessage(org.apache.commons.lang3.exception.ExceptionUtils.getStackTrace(ex)) - .time(new Date()) - .params(paramJson) - .build(); - } -} diff --git a/code/link/link-backend/src/main/java/com/link/api/utils/JsonUtils.java b/code/link/link-backend/src/main/java/com/link/api/utils/JsonUtils.java deleted file mode 100644 index c04bc999..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/utils/JsonUtils.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.link.api.utils; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; - -public class JsonUtils { - private static final ObjectMapper objectMapper = new ObjectMapper(); - // 判断字符串是否为有效的 JSON 格式 - public static boolean isTypeJSON(String msgText) { - try { - // 尝试将字符串解析为 JsonNode,如果能解析,说明是有效的 JSON - JsonNode node = objectMapper.readTree(msgText); - return node != null; - } catch (Exception e) { - return false; // 如果抛出异常,说明不是有效的 JSON - } - } - - // 将 JSON 字符串转换为指定类型的 Java 对象 - public static T toBean(String json, Class clazz) { - try { - // 使用 ObjectMapper 将 JSON 字符串转换为指定类型的对象 - return objectMapper.readValue(json, clazz); - } catch (JsonProcessingException e) { - e.printStackTrace(); // 打印异常信息 - return null; // 如果转换失败,返回 null - } - } - - // 将对象转换为 JSON 字符串 - public static String toJson(Object obj) { - try { - return objectMapper.writeValueAsString(obj); // 将对象转换为 JSON 字符串 - } catch (JsonProcessingException e) { - System.out.println(e.getMessage()); - return null; // 如果出现异常,返回 null - } - } -} diff --git a/code/link/link-backend/src/main/java/com/link/api/utils/SnowflakeIdUtils.java b/code/link/link-backend/src/main/java/com/link/api/utils/SnowflakeIdUtils.java deleted file mode 100644 index 81f6c950..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/utils/SnowflakeIdUtils.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.link.api.utils; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class SnowflakeIdUtils { - - // 起始时间戳(建议设置为项目上线时间) - private static final long START_TIMESTAMP = 1288834974657L; - - // 各部分位数 - private static final long SEQUENCE_BITS = 12L; - private static final long MACHINE_BITS = 5L; - private static final long DATACENTER_BITS = 5L; - - // 最大值 - private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS); - private static final long MAX_MACHINE_ID = ~(-1L << MACHINE_BITS); - private static final long MAX_DATACENTER_ID = ~(-1L << DATACENTER_BITS); - - // 左移位数 - private static final long MACHINE_LEFT = SEQUENCE_BITS; - private static final long DATACENTER_LEFT = SEQUENCE_BITS + MACHINE_BITS; - private static final long TIMESTAMP_LEFT = SEQUENCE_BITS + MACHINE_BITS + DATACENTER_BITS; - - // 参数及状态(静态) - private static long datacenterId; - private static long machineId; - private static long sequence = 0L; - private static long lastTimestamp = -1L; - private static boolean initialized = false; - - // 初始化方法(只能调用一次) - public static synchronized void init(long dcId, long mId) { - if (initialized) { - throw new IllegalStateException("SnowflakeIdGenerator has already been initialized."); - } - if (dcId > MAX_DATACENTER_ID || dcId < 0) { - throw new IllegalArgumentException("datacenterId must be between 0 and " + MAX_DATACENTER_ID); - } - if (mId > MAX_MACHINE_ID || mId < 0) { - throw new IllegalArgumentException("machineId must be between 0 and " + MAX_MACHINE_ID); - } - datacenterId = dcId; - machineId = mId; - initialized = true; - } - - // 生成 ID(线程安全) - public static synchronized long nextId() { - if (!initialized) { - throw new IllegalStateException("SnowflakeIdGenerator not initialized. Call init() first."); - } - - long currentTimestamp = timeGen(); - - if (currentTimestamp < lastTimestamp) { - throw new RuntimeException("Clock moved backwards. Refusing to generate id."); - } - - if (currentTimestamp == lastTimestamp) { - sequence = (sequence + 1) & MAX_SEQUENCE; - if (sequence == 0) { - currentTimestamp = waitNextMillis(currentTimestamp); - } - } else { - sequence = 0L; - } - - lastTimestamp = currentTimestamp; - - return ((currentTimestamp - START_TIMESTAMP) << TIMESTAMP_LEFT) - | (datacenterId << DATACENTER_LEFT) - | (machineId << MACHINE_LEFT) - | sequence; - } - - private static long waitNextMillis(long currentTimestamp) { - while (currentTimestamp <= lastTimestamp) { - currentTimestamp = timeGen(); - } - return currentTimestamp; - } - - private static long timeGen() { - return System.currentTimeMillis(); - } -} - diff --git a/code/link/link-backend/src/main/java/com/link/api/utils/SpringUtils.java b/code/link/link-backend/src/main/java/com/link/api/utils/SpringUtils.java deleted file mode 100644 index 0faca58b..00000000 --- a/code/link/link-backend/src/main/java/com/link/api/utils/SpringUtils.java +++ /dev/null @@ -1,157 +0,0 @@ -package com.link.api.utils; - -import org.springframework.aop.framework.AopContext; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.stereotype.Component; - -/** - * spring工具类 方便在非spring管理环境中获取bean - * - * @author webserver - */ -@Component -public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware -{ - /** Spring应用上下文环境 */ - private static ConfigurableListableBeanFactory beanFactory; - - private static ApplicationContext applicationContext; - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException - { - SpringUtils.beanFactory = beanFactory; - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException - { - SpringUtils.applicationContext = applicationContext; - } - - /** - * 获取对象 - * - * @param name - * @return Object 一个以所给名字注册的bean的实例 - * @throws org.springframework.beans.BeansException - * - */ - @SuppressWarnings("unchecked") - public static T getBean(String name) throws BeansException - { - return (T) beanFactory.getBean(name); - } - - /** - * 获取类型为requiredType的对象 - * - * @param clz - * @return - * @throws org.springframework.beans.BeansException - * - */ - public static T getBean(Class clz) throws BeansException - { - T result = (T) beanFactory.getBean(clz); - return result; - } - - /** - * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true - * - * @param name - * @return boolean - */ - public static boolean containsBean(String name) - { - return beanFactory.containsBean(name); - } - - /** - * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) - * - * @param name - * @return boolean - * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException - * - */ - public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException - { - return beanFactory.isSingleton(name); - } - - /** - * @param name - * @return Class 注册对象的类型 - * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException - * - */ - public static Class getType(String name) throws NoSuchBeanDefinitionException - { - return beanFactory.getType(name); - } - - /** - * 如果给定的bean名字在bean定义中有别名,则返回这些别名 - * - * @param name - * @return - * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException - * - */ - public static String[] getAliases(String name) throws NoSuchBeanDefinitionException - { - return beanFactory.getAliases(name); - } - - /** - * 获取aop代理对象 - * - * @param invoker - * @return - */ - @SuppressWarnings("unchecked") - public static T getAopProxy(T invoker) - { - return (T) AopContext.currentProxy(); - } - - /** - * 获取当前的环境配置,无配置返回null - * - * @return 当前的环境配置 - */ - public static String[] getActiveProfiles() - { - return applicationContext.getEnvironment().getActiveProfiles(); - } - - /** - * 获取当前的环境配置,当有多个环境配置时,只获取第一个 - * - * @return 当前的环境配置 - */ - public static String getActiveProfile() - { - final String[] activeProfiles = getActiveProfiles(); - return activeProfiles.length > 0 ? activeProfiles[0] : null; - } - - /** - * 获取配置文件中的值 - * - * @param key 配置文件的key - * @return 当前的配置文件的值 - * - */ - public static String getRequiredProperty(String key) - { - return applicationContext.getEnvironment().getRequiredProperty(key); - } -} \ No newline at end of file diff --git a/code/link/link-backend/src/main/java/com/link/im/ImServer.java b/code/link/link-backend/src/main/java/com/link/im/ImServer.java index 2b95852d..bd9aede3 100644 --- a/code/link/link-backend/src/main/java/com/link/im/ImServer.java +++ b/code/link/link-backend/src/main/java/com/link/im/ImServer.java @@ -1,5 +1,6 @@ package com.link.im; +import com.link.im.handler.ImWebSocketHandler; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; diff --git a/code/link/link-backend/src/main/java/com/link/im/ImWebSocketHandler.java b/code/link/link-backend/src/main/java/com/link/im/ImWebSocketHandler.java deleted file mode 100644 index 6324f833..00000000 --- a/code/link/link-backend/src/main/java/com/link/im/ImWebSocketHandler.java +++ /dev/null @@ -1,120 +0,0 @@ -package com.link.im; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.netty.channel.*; -import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; -import io.netty.handler.timeout.IdleStateEvent; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -@ChannelHandler.Sharable -public class ImWebSocketHandler extends SimpleChannelInboundHandler { - - private static final ConcurrentHashMap CLIENTS = new ConcurrentHashMap<>(); - private static final ConcurrentHashMap USER_CHANNEL_MAP = new ConcurrentHashMap<>(); - private static final ConcurrentHashMap> GROUPS = new ConcurrentHashMap<>(); - private static final ObjectMapper MAPPER = new ObjectMapper(); - - @Override - public void handlerAdded(ChannelHandlerContext ctx) { - String channelId = ctx.channel().id().asShortText(); - CLIENTS.put(channelId, ctx.channel()); - - System.out.println("✅ 客户端连接: " + channelId); - } - - @Override - public void handlerRemoved(ChannelHandlerContext ctx) { - String channelId = ctx.channel().id().asShortText(); - CLIENTS.remove(channelId); - - USER_CHANNEL_MAP.entrySet().removeIf(entry -> entry.getValue().equals(channelId)); - GROUPS.values().forEach(set -> set.remove(channelId)); - - System.out.println("❌ 客户端断开: " + channelId); - } - - @Override - protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { - String text = msg.text(); - System.out.println("📩 收到消息: " + text); - - try { - JsonNode json = MAPPER.readTree(text); - String type = json.path("type").asText(); - - switch (type) { - case "bind": { // 绑定用户ID到连接 - String userId = json.path("userId").asText(); - USER_CHANNEL_MAP.put(userId, ctx.channel().id().asShortText()); - ctx.writeAndFlush(new TextWebSocketFrame("{\"type\":\"system\",\"msg\":\"绑定成功\"}")); - break; - } - case "ping": { // 心跳 - ctx.writeAndFlush(new TextWebSocketFrame("{\"type\":\"pong\"}")); - break; - } - case "single": { // 单聊 - String to = json.path("to").asText(); - String content = json.path("content").asText(); - String from = json.path("from").asText(); - - sendToUser(to, String.format("{\"type\":\"single\",\"from\":\"%s\",\"content\":\"%s\"}", from, content)); - break; - } - case "group": { // 群聊 - String groupId = json.path("groupId").asText(); - String from = json.path("from").asText(); - String content = json.path("content").asText(); - - broadcastGroup(groupId, String.format("{\"type\":\"group\",\"from\":\"%s\",\"content\":\"%s\"}", from, content)); - break; - } - default: - ctx.writeAndFlush(new TextWebSocketFrame("{\"type\":\"error\",\"msg\":\"未知消息类型\"}")); - } - } catch (Exception e) { - ctx.writeAndFlush(new TextWebSocketFrame("{\"type\":\"error\",\"msg\":\"JSON格式错误\"}")); - } - } - - private void sendToUser(String userId, String message) { - String channelId = USER_CHANNEL_MAP.get(userId); - if (channelId != null) { - Channel ch = CLIENTS.get(channelId); - if (ch != null && ch.isActive()) { - ch.writeAndFlush(new TextWebSocketFrame(message)); - } - } - } - - private void broadcastGroup(String groupId, String message) { - Set members = GROUPS.get(groupId); - if (members == null) return; - for (String channelId : members) { - Channel ch = CLIENTS.get(channelId); - if (ch != null && ch.isActive()) { - ch.writeAndFlush(new TextWebSocketFrame(message)); - } - } - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - // 如果仍保留 IdleStateHandler,可以检测心跳超时 - if (evt instanceof IdleStateEvent) { - System.out.println("💤 连接超时,关闭: " + ctx.channel().id().asShortText()); - ctx.close(); - } else { - super.userEventTriggered(ctx, evt); - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - cause.printStackTrace(); - ctx.close(); - } -} diff --git a/code/link/link-backend/src/main/java/com/link/im/handler/ImWebSocketHandler.java b/code/link/link-backend/src/main/java/com/link/im/handler/ImWebSocketHandler.java new file mode 100644 index 00000000..3f806f8f --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/im/handler/ImWebSocketHandler.java @@ -0,0 +1,70 @@ +package com.link.im.handler; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.link.im.manager.GroupManager; +import com.link.im.manager.UserChannelManager; +import com.link.im.manager.WriteManager; +import com.link.im.model.ChatMessage; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; +import io.netty.handler.timeout.IdleStateEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +@ChannelHandler.Sharable +public class ImWebSocketHandler extends SimpleChannelInboundHandler { + private static final Logger logger = LoggerFactory.getLogger(ImWebSocketHandler.class); + private static final ObjectMapper MAPPER = new ObjectMapper(); + + @Override + public void handlerAdded(ChannelHandlerContext ctx) { + System.out.println("✅ 客户端连接: " + ctx.channel().id().asShortText()); + } + + @Override + public void handlerRemoved(ChannelHandlerContext ctx) { + // 下线时通过 UserChannelManager 移除 + UserChannelManager.removeUserByChannel(ctx.channel()); + + // 同时从所有群中移除 + GroupManager.removeUserFromAllGroups(ctx.channel()); + + System.out.println("❌ 客户端断开: " + ctx.channel().id().asShortText()); + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { + String text = msg.text(); + try { + ChatMessage chatMsg = MAPPER.readValue(text, ChatMessage.class); + + System.out.println(chatMsg); + } catch (Exception e) { + WriteManager.writeToChannel(ctx.channel(),"JSON格式错误"); + } + } + + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + if (evt instanceof IdleStateEvent) { + System.out.println("💤 连接超时,关闭: " + ctx.channel().id().asShortText()); + ctx.close(); + } else { + super.userEventTriggered(ctx, evt); + } + } + + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + logger.error("Channel 异常, 关闭连接: {}", ctx.channel().id().asShortText(), cause); + ctx.close(); + } +} diff --git a/code/link/link-backend/src/main/java/com/link/im/manager/GroupManager.java b/code/link/link-backend/src/main/java/com/link/im/manager/GroupManager.java new file mode 100644 index 00000000..1bcea325 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/im/manager/GroupManager.java @@ -0,0 +1,71 @@ +package com.link.im.manager; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.netty.channel.Channel; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public class GroupManager { + private static final Logger logger = LoggerFactory.getLogger(GroupManager.class); + + // groupId -> Set + private static final ConcurrentHashMap> GROUP_MAP = new ConcurrentHashMap<>(); + + /** + * 创建或获取群成员集合 + */ + private static Set getGroup(String groupId) { + return GROUP_MAP.computeIfAbsent(groupId, k -> Collections.newSetFromMap(new ConcurrentHashMap<>())); + } + + /** + * 添加成员到群 + */ + public static void addUser(String groupId, String userId) { + if (groupId == null || userId == null) return; + getGroup(groupId).add(userId); + } + + /** + * 从群中移除成员 + */ + public static void removeUser(String groupId, String userId) { + Set members = GROUP_MAP.get(groupId); + if (members != null) { + members.remove(userId); + } + } + + /** + * 根据 Channel 移除该用户在所有群的成员记录 + */ + public static void removeUserFromAllGroups(Channel channel) { + if (channel == null) return; + + // 遍历所有群 + GROUP_MAP.forEach((groupId, members) -> { + members.removeIf(userId -> { + Channel ch = UserChannelManager.getChannel(userId); + boolean match = ch != null && ch.equals(channel); + if (match) { + logger.info("用户从群移除: groupId={}, userId={}", groupId, userId); + } + return match; + }); + }); + } + + /** + * 获取群成员列表(不可修改) + */ + public static Set getMembers(String groupId) { + Set members = GROUP_MAP.get(groupId); + if (members == null) return Collections.emptySet(); + return Collections.unmodifiableSet(members); + } +} diff --git a/code/link/link-backend/src/main/java/com/link/im/manager/UserChannelManager.java b/code/link/link-backend/src/main/java/com/link/im/manager/UserChannelManager.java new file mode 100644 index 00000000..6c4b4dca --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/im/manager/UserChannelManager.java @@ -0,0 +1,64 @@ +package com.link.im.manager; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.netty.channel.Channel; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.ConcurrentHashMap; + +public class UserChannelManager { + private static final Logger logger = LoggerFactory.getLogger(UserChannelManager.class); + + private static final ConcurrentHashMap userChannelMap = new ConcurrentHashMap<>(); + + /** + * 添加用户与Channel绑定 + */ + public static void addUser(String userId, Channel channel) { + if (userId != null && channel != null) { + userChannelMap.put(userId, channel); + } + } + + /** + * 移除用户绑定 + */ + public static void removeUserById(String userId) { + if (userId != null) { + userChannelMap.remove(userId); + } + } + + /** + * 根据 Channel 移除对应用户绑定 + */ + public static void removeUserByChannel(Channel channel) { + if (channel == null) return; + + userChannelMap.entrySet().removeIf(entry -> { + boolean match = entry.getValue().equals(channel); + if (match) { + logger.info("用户下线: userId={}, channelId={}", entry.getKey(), channel.id().asShortText()); + } + return match; + }); + } + + /** + * 根据 userId 获取 Channel + */ + public static Channel getChannel(String userId) { + return userChannelMap.get(userId); + } + + /** + * 判断用户是否在线 + */ + public static boolean isOnline(String userId) { + Channel ch = userChannelMap.get(userId); + return ch != null && ch.isActive(); + } + +} diff --git a/code/link/link-backend/src/main/java/com/link/im/manager/WriteManager.java b/code/link/link-backend/src/main/java/com/link/im/manager/WriteManager.java new file mode 100644 index 00000000..742126da --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/im/manager/WriteManager.java @@ -0,0 +1,57 @@ +package com.link.im.manager; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.link.im.handler.ImWebSocketHandler; +import io.netty.channel.Channel; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Set; + +public class WriteManager { + private static final ObjectMapper MAPPER = new ObjectMapper(); + private static final Logger logger = LoggerFactory.getLogger(WriteManager.class); + + /** + * 发送消息 - 根据 userId + */ + public static void writeToUser(String userId, Object message) { + Channel ch = UserChannelManager.getChannel(userId); + writeToChannel(ch, message); + } + + /** + * 发送消息 - 根据 Channel + */ + public static void writeToChannel(Channel channel, Object message) { + if (channel != null && channel.isActive() && message != null) { + try { + String json = message instanceof String ? (String) message : MAPPER.writeValueAsString(message); + channel.writeAndFlush(new TextWebSocketFrame(json)); + } catch (Exception e) { + logger.error("发送消息失败", e); + } + } + } + + /** + * 向群内所有在线成员广播消息 + * 只在异常时记录日志 + */ + public static void writeToGroup(String groupId, Object message) { + Set members = GroupManager.getMembers(groupId); + for (String userId : members) { + try { + Channel ch = UserChannelManager.getChannel(userId); + if (ch != null && ch.isActive() && message != null) { + String json = message instanceof String ? (String) message : MAPPER.writeValueAsString(message); + + ch.writeAndFlush(new TextWebSocketFrame(json)); + } + } catch (Exception e) { + logger.error("❌ 群消息发送失败 groupId={} userId={}", groupId, userId, e); + } + } + } +} diff --git a/code/link/link-backend/src/main/java/com/link/im/model/ChatMessage.java b/code/link/link-backend/src/main/java/com/link/im/model/ChatMessage.java new file mode 100644 index 00000000..32a2e9a4 --- /dev/null +++ b/code/link/link-backend/src/main/java/com/link/im/model/ChatMessage.java @@ -0,0 +1,40 @@ +package com.link.im.model; + +import java.util.List; + +public class ChatMessage { + private String type; + private String msg; + + public ChatMessage() { + } + + public ChatMessage(String type, String msg) { + this.type = type; + this.msg = msg; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + @Override + public String toString() { + return "ChatMessage{" + + "type='" + type + '\'' + + ", msg='" + msg + '\'' + + '}'; + } +} \ No newline at end of file diff --git a/code/link/link-backend/src/main/resources/application.properties b/code/link/link-backend/src/main/resources/application.properties new file mode 100644 index 00000000..8fa00146 --- /dev/null +++ b/code/link/link-backend/src/main/resources/application.properties @@ -0,0 +1,17 @@ +#spring.datasource.url=jdbc:sqlserver://localhost:1433;DatabaseName=G3HD +server.port=8089 +# Netty WebSocket????? +netty.port=8090 +spring.datasource.url=jdbc:p6spy:sqlserver://127.0.0.1:1433;DatabaseName=G3HY2025 +spring.datasource.username=sa +spring.datasource.password=g3soft +#spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver +spring.datasource.driver-class-name=com.p6spy.engine.spy.P6SpyDriver +spring.datasource.hikari.maximum-pool-size=10 +logging.level.com.zaxxer.hikari=DEBUG +logging.level.java.sql=DEBUG +logging.level.org.springframework.jdbc.core=DEBUG +logging.level.org.springframework.jdbc.core.JdbcTemplate=DEBUG +logging.level.org.springframework.jdbc.core.StatementCreatorUtils=TRACE +logging.level.p6spy=DEBUG +logging.level.com.p6spy=DEBUG diff --git a/code/link/link-backend/src/main/resources/application.yml b/code/link/link-backend/src/main/resources/application.yml deleted file mode 100644 index 2d55dabd..00000000 --- a/code/link/link-backend/src/main/resources/application.yml +++ /dev/null @@ -1,25 +0,0 @@ -server: - servlet: - context-path: / # 应用的访问路径 - tomcat: - uri-encoding: UTF-8 # URI编码 - accept-count: 1000 # 连接数满后的排队数 - threads: - max: 800 # 最大线程数 - min-spare: 100 # 初始化的线程数 - port: 8089 - -netty: - port: 8090 - -spring: - config: - name: application - location: classpath:/,file:./config/ - jackson: - date-format: yyyy-MM-dd HH:mm:ss - time-zone: Asia/Shanghai - servlet: - multipart: - max-file-size: -1 - max-request-size: -1 diff --git a/code/link/link-backend/src/main/resources/backup/application.yml b/code/link/link-backend/src/main/resources/backup/application.yml new file mode 100644 index 00000000..e18116be --- /dev/null +++ b/code/link/link-backend/src/main/resources/backup/application.yml @@ -0,0 +1,8 @@ +spring: + datasource: + url: jdbc:sqlserver://192.168.1.189:1433;databaseName=G3HD + username: sa + password: g3soft + driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver +server: + port: 8080 diff --git a/code/link/link-backend/src/main/resources/datasource.yml b/code/link/link-backend/src/main/resources/datasource.yml deleted file mode 100644 index 2b9b0951..00000000 --- a/code/link/link-backend/src/main/resources/datasource.yml +++ /dev/null @@ -1,22 +0,0 @@ -#datasource: -# db1: -# url: jdbc:jtds:sqlserver://127.0.0.1:1433;databaseName=test1 -# username: sa -# password: sa -# driverClassName: net.sourceforge.jtds.jdbc.Driver -# db2: -# url: jdbc:jtds:sqlserver://127.0.0.1:1433;databaseName=test2 -# username: sa -# password: sa -# driverClassName: net.sourceforge.jtds.jdbc.Driver -datasource: - db1: - url: jdbc:jtds:sqlserver://192.168.1.176:1433;databaseName=G3-Email - username: sa - password: sa - driverClassName: net.sourceforge.jtds.jdbc.Driver - db2: - url: jdbc:jtds:sqlserver://192.168.1.176:1433;databaseName=G3-Website - username: sa - password: sa - driverClassName: net.sourceforge.jtds.jdbc.Driver \ No newline at end of file diff --git a/code/link/link-backend/src/main/resources/dbconfigs/G3HD.properties b/code/link/link-backend/src/main/resources/dbconfigs/G3HD.properties new file mode 100644 index 00000000..7506e981 --- /dev/null +++ b/code/link/link-backend/src/main/resources/dbconfigs/G3HD.properties @@ -0,0 +1,4 @@ +url=jdbc:sqlserver://127.0.0.1:1433;databaseName=G3HY2025 +username=sa +password=g3soft +driver=com.microsoft.sqlserver.jdbc.SQLServerDriver \ No newline at end of file diff --git a/code/link/link-backend/src/main/resources/dbconfigs/G3HD2.properties b/code/link/link-backend/src/main/resources/dbconfigs/G3HD2.properties new file mode 100644 index 00000000..bb2277ab --- /dev/null +++ b/code/link/link-backend/src/main/resources/dbconfigs/G3HD2.properties @@ -0,0 +1,4 @@ +url=jdbc:sqlserver://127.0.0.1:1433;databaseName=G3HD +username=sa +password=g3soft +driver=com.microsoft.sqlserver.jdbc.SQLServerDriver \ No newline at end of file diff --git a/code/link/link-backend/src/main/resources/spy.properties b/code/link/link-backend/src/main/resources/spy.properties new file mode 100644 index 00000000..fc9a6edb --- /dev/null +++ b/code/link/link-backend/src/main/resources/spy.properties @@ -0,0 +1,12 @@ +# ?? slf4j ?????? +appender=com.p6spy.engine.spy.appender.Slf4JLogger + +# ???? +logMessageFormat=com.p6spy.engine.spy.appender.SingleLineFormat + +# ???? +logLevel=INFO + +# ?????????????SQL?? +excludecategories=info,debug,result,batch + diff --git a/code/link/link-backend/src/main/resources/test.html b/code/link/link-backend/src/main/resources/test.html index 3d7b857a..3b245cb8 100644 --- a/code/link/link-backend/src/main/resources/test.html +++ b/code/link/link-backend/src/main/resources/test.html @@ -12,7 +12,7 @@ .card{background:var(--card);padding:14px;border-radius:10px;box-shadow:0 1px 4px rgba(2,6,23,0.06);margin-bottom:12px} .row{display:flex;gap:8px;align-items:center} input[type="text"], input[type="number"], textarea, select { - padding:8px;border:1px solid #e6e9ef;border-radius:6px;font-size:13px; + padding:8px;border:1px solid #e6e9ef;border-radius:6px;font-size:13px; } button{background:var(--accent);color:#fff;border:0;padding:8px 12px;border-radius:8px;cursor:pointer} button.secondary{background:#e6eefc;color:#08306b} @@ -104,183 +104,183 @@ diff --git a/code/link/link-backend/target/classes/application.properties b/code/link/link-backend/target/classes/application.properties new file mode 100644 index 00000000..8fa00146 --- /dev/null +++ b/code/link/link-backend/target/classes/application.properties @@ -0,0 +1,17 @@ +#spring.datasource.url=jdbc:sqlserver://localhost:1433;DatabaseName=G3HD +server.port=8089 +# Netty WebSocket????? +netty.port=8090 +spring.datasource.url=jdbc:p6spy:sqlserver://127.0.0.1:1433;DatabaseName=G3HY2025 +spring.datasource.username=sa +spring.datasource.password=g3soft +#spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver +spring.datasource.driver-class-name=com.p6spy.engine.spy.P6SpyDriver +spring.datasource.hikari.maximum-pool-size=10 +logging.level.com.zaxxer.hikari=DEBUG +logging.level.java.sql=DEBUG +logging.level.org.springframework.jdbc.core=DEBUG +logging.level.org.springframework.jdbc.core.JdbcTemplate=DEBUG +logging.level.org.springframework.jdbc.core.StatementCreatorUtils=TRACE +logging.level.p6spy=DEBUG +logging.level.com.p6spy=DEBUG diff --git a/code/link/link-backend/target/classes/backup/application.yml b/code/link/link-backend/target/classes/backup/application.yml new file mode 100644 index 00000000..e18116be --- /dev/null +++ b/code/link/link-backend/target/classes/backup/application.yml @@ -0,0 +1,8 @@ +spring: + datasource: + url: jdbc:sqlserver://192.168.1.189:1433;databaseName=G3HD + username: sa + password: g3soft + driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver +server: + port: 8080 diff --git a/code/link/link-backend/target/classes/com/link/LinkApplication.class b/code/link/link-backend/target/classes/com/link/LinkApplication.class new file mode 100644 index 0000000000000000000000000000000000000000..7a4e4fbbf1de252825fb1b4e9190f21b4ee06e20 GIT binary patch literal 742 zcmZ`%O;6k~5PjaSKz3VTp%f@B>TTgrU$|6TA;C&Lr61~6+XE*jm|!-sSB?Yxt)4)Y z;0N?aRg43OEm~q_#-16!H#45U|Nh(n*u_?eCRPKy4zLzt9UB4OFtk1?t=t|%y|+DL zXbjAeWLSulmS4*}miDKJGqQ9elZb32tn%l3W5b=Of?*>{OwKc<$DHns_a~D~CBiAA z`-GSar5RRxhta7x6Fd_-{@u>PaePUUi>3PsuOYi(SW58dC3j7@nl@d+Gz*mGmYJT}I0Q96?teXP>b zNKy zY*oepA?-VN0cg_>n8ypeBoKf3V+97ZyS4f?>Q`v|dV~nc>BGZnJ;1cFfJL$|(ch%G QBvnJVlFL{j?U`Qzx3*@zm;e9( literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/controller/AuthController.class b/code/link/link-backend/target/classes/com/link/api/controller/AuthController.class new file mode 100644 index 0000000000000000000000000000000000000000..e69a9c6d578582bb5f52310a8dbcf575d38ea331 GIT binary patch literal 2295 zcmbVONpl-j5dKD%G-Fw@o$(Sx0LuwU?06ZrfU%9iHd&m=ODx+V3EN0|mM0p`h*|7% zapJ}Sic2m~+_}Of3sg~@OclS00=^k7(KwWGP)VbH{rYwH*ZsYg{`l+XUje*9auNhj=wmio+P2p(szPGd3TFyvFn0DE)BxFZ&_w-#|^F71Vmi5}q=%F=D zJ-Z@ks@8W*-70H&&oQhr{lp7)X+Ov_>P3R$2)AZiE>lkWu5gx0)YWv?-LoBv3EL@$ zs&LQRwk5n6&A!S4nA&D=l-mq77)8EcYofmt4LkkGp3pBSDG;%x0mW`=sd!nEf46CH+En98Pc0Ho4)jOWp z)lHvJQX$70G&&Ov)Pny%Bd&|jeBpWvQ%1@}1X`o$tWZTluG zv}CbGMk!l#L`ks!y2*B*Ki+7FWLswv3I7CTi12j8SM;KCfR%;Ecnzk9up5+Y(70Vi z^O{fdsedtVyR}C-h$3@D&2|mXcJ{;BvEEv;Fib3%Cu@xpji?T&;@q_E*kzP{&eayGLGWnl?N_rykh97ig4 zMBazVH-lmH^VchQ#lW$OQ+z(hmBw$0--(M4A^j9?XZTEp${svVIbAovX}rJ{XEt6! zA~-C4jSeW(eg6w$w;!Toc}$HzMk0pakX#v4<)%CxA5)dqA~A7*6Pfq{luY7x^p2~Y zYU(k%I&f?J$4p}45z^DiO!5G;ndF1O@BzBNg*K5%JVMX3Jg)X;=r}Z2PiB%$u~Sv+ z;t%K!T=q3xGQT(iObh7cQ)R||EJi;|GJr`IVwPC0(qf4SZxUmX2rI<6iy?f0EWV=U zH#mdu@N!s>b$X~!H^e&#mPt`+ex9TbF&UTT=_ zOV%nlb4H{$K9g6)MwvoA%K6^VSO44#2-Zo3c!lYT` ztGE}!&~Rvrcr|p@vbj`RWpsQUl99-H!YoyebsUAPO=Yi(oE#qIuZHazY33HG6Xc48 zz%VC-TyXQ#rbpmXcuZY$#ws|bEPD*(%72l8k$wec&rI2oR#D7ZzD43rJB}+&#U&vO zrkNqbFR6^rD`dA^Z_&6VvPRZ&@`hP`Ym5uYcuJ}=nAe-8T_$S1Qgf=%Xzf6)r)sP< zE{C@4vQlD!89HsRmhIWR#R67!O#0f6OVz|SHRXWZaFs2wzKPD1 zEjuqf<{@nTAPpyVyn$;v-c+h@>3AFO#PO~U6Il)2k3M>^^6{rDUw`)S&Ub&j_u=n9 z-Ff)_Co3O(5l2o(9^|4k@Up(|$WguO%9r;Zef!m)KYp%b9&GkmOHt&wb<0%k3}0KW zG0pOq9DBknvd)@V=EC9B8rYyG>s?fmK*fY~!`aDN4I-&E&1t3jOxg~8Z)BQZ0sHF1 zz398nET<~_YZphZw-l{a4MQ7Ps;8j>J33^$>s^I&TSJ4psNvM+hAZ=K)A65VawdfD zn+4WYY~HkN7R3f;H#O232|C*N-4S`E)x(*9r_s(YZ1ocZsYVyq-8>om)cD!h{{Y%O zZW_?dwJ8v`Vh{Iv_yBvck0%XJpa+eC@^9G29WcI(hRJWR?FYoJK0sqz(|yDT_;(+P z`)D3Gyo}_@SX;}U*fR9HldG?Ggs)n|hs)S`H*jKtRVay6&OlgItf` zNep0r0Bw}_G4xWhpHL1Wfu|U0jQ4wS0MAfHa24QccaZN?wBvXd&oR_P+^g_H&0cEu zQNrNuIQnsz`{o8-g3m$6ik$(Z__>2hin^oMp!s+nFHpv(8eZf^ku+E(=@%*~l2VE! z#l^0zbJ51Et#Z*7SP3z<5M4X*br6(FQOHAUg@=|Z4;>L6QdJ&ORUT67cnCEkJY*h= yhhz0T4E-|?ZU2UcLFVchS};T&j+2KI|0f=fujiqG_*C({6ckUAyOTT*1AhZq?A#Rq literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/controller/DataListController.class b/code/link/link-backend/target/classes/com/link/api/controller/DataListController.class new file mode 100644 index 0000000000000000000000000000000000000000..4ba63f6276ccfe42f4ab5176ed8be9cdcc49cccc GIT binary patch literal 6429 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 xDf(l=hFh63^5b+e zl&&v^nmntS&4nHar%L<68I}z)B=3xouB$~+NHVvtP_(f_{iYwJKe7j(g%cC$r6Y`b zPmPT5Q%9xu=Xi&rpkLo6nvX?(Tv$UiILZC#iR67czQ{xAq{-!8HWo9%r(S8Esls2_ zT9^_4G#ty^?{?^AhGCU}I$396_ii136i zX!-kT>@aMV;Y$z1&|V&%VV%}vCY|rw_r%gG=fr8xJ8GY3-DobzxlBgu>m}bJlE|qv zTLV0!l?-V+S(=u^mKuE#t|363v<}IDWZZs!4%YsG;Eb#QF`cz}>ilJxuq)obA5 literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/controller/DataSaveController.class b/code/link/link-backend/target/classes/com/link/api/controller/DataSaveController.class new file mode 100644 index 0000000000000000000000000000000000000000..60ce3f7c9da08efe840c6347fa2474a78793dc66 GIT binary patch literal 5467 zcmc&&`*##)8Ga_&-I;8rgoXyXG(e#Vgj|>c($<8ggcL9|37CXLE|pGpC&?tcGwIAM zB=z3XRA^Udr|cEbiD$8#Fa&dm4D z_r2fyKF@pkhIjw-);j<`fIr94f~h#B@mK{uhL6kmL>v)JDfpzge@a|F9mi+zxQr*_ zh>JJdE3pbEWZW;tPbz3v@IV~RcuK(z1s!objnB$Bso-;QEXU{Lcm~f_VlAGl#PfJT zT)rUViwX|N_>zJ_88621622VASH$J3cv)Otk@2-S*5m79lW&OS-xQZu#qe7)zAfXF z1hvbu45u@#yRP9%h;275(`%Pd-mq~{LZs7984{|xP0Q%bW`+%CKp#$1Qr&GQ_4J_b zm|~n0M!YeTI-0wac1BB^)`X@{np)DfJjYI_4M%%W_w;`Kn9&)E+9bqXO7;zp8%a+> zQ$zQ-eoWW0o|)Eq^vSl3#mannpW$ZH9+yUKXS6FNtnnA6b!$}X_Z-t2<;z$`pPV!t z2`#>g5#99+XDXA{#`WZcYg<~1)(o3gN((mUZk#R*qBw2z>KOsZvcsixS4vqz(T`B3LyADt1jJjY-e6Ep8+`#-mxorQ22Wyq2)M7~tv2 zJjmOMr*1FMopZfF5_s0u&dqM=_vkmB^vPmyNm$xrcw=^IzwXdifG_p%Pn z9+=0)BDY$YvF(k*kVIeB^304eXu2kG?yxM|(*+{jep@#g)!a!z;fS#6xa~}6$Bkjl zCrS_JYWr>16O091M~~_0tO!&;;zU5%xqw<%yj$q&O>Wy4Ft*c9P16;1E_4UCFz^cj z>o#5Q`q5hv#+d{qA5dhafM8z3&-3W83tg;0H1L}*>S-_ANoW49^F@(K|Z0Vw&RyGfKr3HL2J zvshidq5}QDEcNTzTwX3dpF17fb@w^usA;vS&~Uem(<)xW>oU%$_%6Oj=jIpromn%T z5*5Bm^qIM=Z&mSq{6NJI#p)la_%VLM&LW!9BAbAUvpA>X4g6HaD8^*Gsp4mtVerhE z5OTo!e&zYc|N7F|E2mHV?S-eVoIdyWH(&e5+pnlNFZREn;v(KsaS3lTyHxxf@2L0% zW?7MaW%)E^;?F4$Vvj*SH$~*Sj9;qw6)wy8wT$1WNaC^z1D9EP7T~2@Bg(Ud`{aNrQPlpQqJ=vFfsYfl9>aC@QP#2B zuXQCj^s#b|=w@2NrV{2~SHEJ0@whx6WPT{-&-PGLl)(5Jh=dca42KU*=Cb?Ir9FNcD{p*?#@g!}WJ(gDjDCzIplXs5KNH ztr>>bIj4)Sfdhp$=U`aiY@M_nL-TlQ)dqO!Ge!)@u#!gUz>T=(Vf;U>^pm8+&8O&%k}Mb3{OW1uN1FJPA&Kt7>t>EL-X-3njpreq<3ky?@EY@lOR<&D zYA}HxdfPaXa1ZW9R4kTU&a0s8)e9)=c?0E_5IJ}r(dyU?)t zs~2%g8LF4eplTLNS|b!KEfz%^o2qa1Mdwl78slhLIFdQKEgUJ$iAW-vh$ZBNasf4G zd-A)l2zI|cFyI0bXMG2rzzKdyMtt`+p^|2c(F%TsCQy$%a35;%5Z2%z?qthbi!AQq ziaI>Ww*rO{el+qsmh(PHkm_+1t!P6z$KBYD`}yu6zG>%ElscO50H0#iv2atZI!U zBGs#XuxGKRHOkSQ;V8yYK+-JMw#pm@B#Dv2Q9zOyRh+r{dUMkSth*sS8Cdh^X(6N< z3AvkryO~gLq4QfYM8FI}KSqf~IBh~M=sChjY#|v3NJ5nF8ZpSH7}wO2iXjp(M2vRu zs!-={e1u>}_-{N%s9iwH$&i$jq;iPx2~rw4JJ5D0)OIM;wuf&CD(4^Kd_mc2l>Zx* zG7if)@*a*{rCDV%9+vS@NCiY5@46w#6=Xi2 z1J%C40)#!3b}=LN`alXdBLpsp^U4BP>O)xS3t$OiRY>xVvv}-Xw1dO*9ZW9lVBJkP z*hgmu=->f5IQV`zn40gPv9N>nH{syJbnqx0e1s0_?}vjU#SWH<4+aLN=q~?c%*S}| z&h-F>=_RZ`$X>Hy1`R9_qVjB%_(`y)HBx?0v?dyiPhn*uQWI@%sfk`hV;N@9)PEu> zow@p#MC2^>1yy`C(@@l9g*zr-vR03?NKCM@jx(M!J{IlVe*@O>?Ko|rC1DJAb7q>U zs<4!bN?t}CTe)h8w%4J-FD{X=8VB!ez7wfki?XX!8}rk7QpTf9Y8gw|NZ(`UaC{5- lKUn!%JwZV^TwcrI<&wOd4FNdD-*Nsn@fW8t>v`wje*w+&T}J=_ literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/controller/FilterConfig.class b/code/link/link-backend/target/classes/com/link/api/controller/FilterConfig.class new file mode 100644 index 0000000000000000000000000000000000000000..9ff0220d91b30e220381bf9724d08776bb17b757 GIT binary patch literal 1467 zcmbtUZBG+H5Pr5Ty;3WbDj>cfATKQi))z!g5rZTqnhJ($6XK_9yOb^0JF?wU{3ZSk z{ZbN%{s4cJarUkf3&cozA7*yuW@nzAeP)0D`t}3BGM>dTfEf$3afFPXiz9*=W27x) zVwg9}YzzxVFB-9Afm^uG5M7n7)N2gkbf(A<+3+fYVK6UUvDvJbgnDn69P*^{UfFhv zwvxu~I3s#bHW+5}Wv|X1>F#s;K=QKZYUMeOQ2dp2v``zKyDO_XhW=Vh2QG#e=}g{J zRo*yI(yi_)yDnOu+UF(D)4U~0ydl(~BXlRnwy4U6R<@R&`%>60WlO3vK8EK#ri||T zfElvgM`~*6@HZ{J-qd@-)w1m42#!KXM!K8x>EKU;YQ5lDy#$6e)7^rsy0&gAx-O@; z?q0U^*mje;QWr(pkVKDl*Y&jDB4Wqlg*`AN5l5Qar=h%at?`1_RAoWUrE?4gJq{SO zRI?9lhL!(K80=~Wq{$^Ph$KU<2huTQx|seiS|soQ4~e7(Gk0va$_v^sogu{M6L^GW zhEbY_izt=AW2_|b1Wy^JyDFNtT`*f+b+$@1QP#wVhB%$Jv2@-5IXW{{IWzMEcBS$` zIqz&uv+B}~jif*N(SO7CBQ46fNrTET9OOF}5u=DLRS}AwlJx6L5tFz|Z=)V+hAA@+i{t=egeOTUhpFHc6-v`;k;<6z z6G4ei`55MK17iduiMzOoTXde=6lFjL|L)LgoID}=KSA0YWzx5&e0lHGI}Q8>jF6k& literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/controller/SearchConfigController.class b/code/link/link-backend/target/classes/com/link/api/controller/SearchConfigController.class new file mode 100644 index 0000000000000000000000000000000000000000..ca1468d5333b92b0b7458d2599af35cb3ba2fd9c GIT binary patch literal 2229 zcmcIlZBrXn6n<_9B*e4@3M~}tORW}Y!&aqO31VApX=@0S6l|?;OL7TYHoNKW4TT@z zjN^0f@nZ0|@x##6M=bm%+_kVu+ z4Zsl01UfLBz$-Z2idQibLpp&dhT|C3@7MG)mcZ*clfYS=i(@>2H*h|N3vo;&um=}4 zeJO^^F}x|zGHN-N8WU*h>z@;dj=KdZurFgdGFvIlOYf>VZVtIVic zw0wb+nY>#vY|B|P%(7+VT}OGYZA;I{Nz=WIqD&1f2zoIPLn2~EGIcHqVJO2J zGW@c>@db_Sw(Bh!x8=MsZ#f0Sd?ag3NX2-{x-xjL%cfl+>h_@K!a`%^vCssL)Vq5W zaGl@bUHh%pSzv~q6FIly<>duS_eJmaB|fN&P2lun`0&{fH63zl*Q(>5>-$rlRkWOR z68q5^LnetyWMi00Vj5Ri$y;mxT*a~r(qsB`$JSj(;(k!tkT3DFU;hafr(?yLgs`;m4T)E-26WAX_ zQg52`MR$g>9s1tRG>3L?W4M=n*O}!UWPi1DNP4TaWhyYdjitskRAJYS#qLH|;iMIa zxJv?Ob~ap{P&*Fn1TSY&`o39Ydqo#a%Vtq*V|E84coyyaO6p$(%qu#%c9Amp7JN4k ztV7%jjt_8c37Bm-$bB*tKsS0w2^>N%ngiuO(8L`uv4KeTdo=xw==F6pceJb_mg2L9 z_!?SM$2X7|iFQ2E6Wu`aZuX(n-cV{^D7b;PyMd$QIKgjn6dJ}M0s%op>HIL=A0_7m z-KX$m0BwxtQS^~IOejYY$5VKkoD}7rVa!b$Tot(99ig0#b^^~)I)dl9*WrblFHrLs zISk&8(P}^Utr04L;=ll(2sAukFO^b!25&(4OyWuMC@SzGH(KnaV0}3dPLLiVeG2#& DFo=Wv literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/controller/StudentController.class b/code/link/link-backend/target/classes/com/link/api/controller/StudentController.class new file mode 100644 index 0000000000000000000000000000000000000000..8d0b82e7e61dc582663fa3016ebe271c25cf7a8c GIT binary patch literal 5578 zcmcgw`+F4S6@Dk#&1{wd!eRnJ?f5__!ZW;7Rdx$d9M+v=4{H%rnJ2c*Kuk{KSW6#p&mKcwWW}K8*OW056KAm;87c zKlS4%j)~V-e0bG|*Tm${#QSj{PWbS;jFU3nkl>G7iEb@wZK~TQp`y9V=nqEpXkSo` z=|NMA8FAe*%%NaMYL{wk-BjODE1|qS5w-Mwty7Qdp@>!&jT)9}=|(gz;ofG$3QnV2|2m3sl_$!McQH4CpdJXuHFF@MS;1b*iUk|_)8S0CYx@&g+^Vh2)swLFNO0?5 zS`bCcrn6JRifM`@A8NwtZPe6m%@ke=OM($K8m8?6PD=G!MxPdyFtfu_yZTzxnC-oc zQxZyAG^^L>Zc_#5mSz%+qOfMwMIsU!zKUDzT09Z4sNB=7S}GMehS@GR>%>BWL{>_( z?7ja$mlhKNA%KXb!CiPO%*t|!9g3yNT{T7J`dmr<43tQk;MxtfGTxN&mL279GeIO2 zcj)1$Y9&nCx%sPOvA${p9b1z((g(FrFr-JjgIPhoMYBj%%%QNT%hXhhhU}e^ak*aD z9)GzwbP4lDwZSGvwbVo!Qn@bSj+=oe;~fd1Z`LvgzPqOlUz-un!f$Er<>SVC&Mh>3 z;i-hKH@b(&X;C7^ASXGd6D3aiHAO8gteLep1MchKt3W4NbF=a&tysp-B_#guCjGB* z-j>MX$vdD%5=7D)d{pm#Ov2jf^Q~IF>Dyg!!sOxZPrE2D@zsOO!-ps;QZaS(6m}62DULE`BZJJq5qPZx!}OcS~3} zdGfuSZVp1OMGXn}5A@=O$u@)uRd> ztEMT|y}+{A?HGz%T0eVeBSDT+O_^>4+t@)`gi2HU#huD_M2oW8)Z|r_VZ;G9m2NyJ zh}p^JSX8RpD)OS|vkd1{I$LtJi;hs(5@8#gmn63SK5E%!hJ}*lIcVMRtAtW}*mc?6 zZXj)!8>uwB!03~(F^{)dk>q4X-rnAY+E3G3w0K+%^Hwg>qr`$ZFS=>RR4Mu|yBu!o z;Z>d+b?F+xtuB$1C~B=4hdj!Cg|_T6c=p>m+*bQdf8=gkgx&w zQ4(#G?&D5jD7(inGcYTO+0{vua7bcK5`nC>xz5^c z?poD3lqONuT5}oxz&yG(e>6}o1h%Hd-Ojx=N!;Na5lghY5f4G7w|nrgJ$h^`R$&Hh zTTXITK>W8s2`Q>(eQo4w8xh$-B=)dos`M*FBm^I8h(H_WU?b}2TZl62(ZDDA=;C%T zgje1LP1WHe{v+CmuxXm!JB z8{dV*?K~#17WN2k4_8$a>(-hZVNGyuh&9)8v0jQ0=m{;l<$HENc>pa<* zmwT=UT}3Ye3L$KdP5?TfZzSk-1ihZ1Zzkwl=Doy^#GDA*0-kx?hTjR+r5UidO9E<-4Rz;Kw2KQU2owQ0 zi0^U{e~@##`Nu6114{gp7$84L5wM2@6mf2(s)hut^OSpTM1jESh8&2+3<=d_V3CM= zrpV&8-cqk%Vo9kt&BRg>;p@CPOn9d#!k0Z^UI|1V6-&Y&J%0UJC40!Al`eAF+Ay z*q6vM!ArnSf(LD0yf!a;Wn4$i%tA`V6nY-E>6t}cD*r-zAjN6FplNm?C%XBNAxooM zPPS}HWy|o_oGsr<;;wYUEXyR!a+{`GCQLWMUI2}Ni!nQ45)7>YCd?oc=20fhK_<)- z4EQHW;31xPnkSwiK}Xz#+36-se^$Z_yA%z(6b-uxb0~kp?8r(O5|ev9F&hLcnS=?u cjPy=n#7-Q+h`7mg`*)wc+0XYrzDI!n0uI>%S^xk5 literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/controller/TreeController.class b/code/link/link-backend/target/classes/com/link/api/controller/TreeController.class new file mode 100644 index 0000000000000000000000000000000000000000..4ac67110cb63292353d25b5698c78ff138d5bf9d GIT binary patch literal 1944 zcmbtVTT@$A6#jN@2hvb#D3(-Fti1#%94|;~Kq}CS*Z^&y%s4)6l1+NxvH^=e4K1egRd!FQbT$$#MZ?Gq*>lj_hhGiP7cUYBpJ{jHtnfB*U$fGMn} z@d~EXxPq%`e29XDnKTlZwlHhvk1WhtxRypQrhD+Qndj5!!Gc*Wrjf#R3pXs>6iCfF zo}=dk;`z}HfyAO;Q3Cx%$5Siy+NKKD<)%wXw&<6oyCDO|><@(recuTM^2M@Wvt7sA zvE{B~mwitMzU!*MUJsO7Y;9)*GF7FGaD`!LIiD|X%LmeSrB}7rbl`Z^nQqC_XlH=B zc3iup!n&*JGU*4^rHVn=<%;=ihrY)wYjSs21p*V1r!5(373|eqds~)ws9{&6mYa@O zu~#>@Raq}b>U1uc`e2*m8+&E7s~z9tSuagowIXZOi8(|N+h$5)NuX~{(=VMwb>d|R zoLW};zF)Z`1L`w4HG9{bswZ_lp#Am#TZq}QIYP>n>YjFLYQqT~Vw?9oUrPg*;iif# zlpXGxB5xTK5B*@reyBF>C~?`+wQs4Y0Om4S-UrgH6JgTUyf=Y!ogrEYb;O!JcC3>6 zqOL+6xnZ>QnqLpf>ZW5jH_%C+NprsfS6{++s5{vAGNutM!-HlS5{B9tWHDgjRtBG7 z$-<`@2eZ55dKDvU6GK8cRXV}2p$_X=G2&o7*j<8mBmE#*8n3-Tz08pNc2T~5C6m} ztMmbUD9aweXe_EU9)G&0d;0kLnECwW!zTa}$i{F4mu*~$;VM!V#%!c*T(faKh8wtP z!?7`LPZX>e;bcsXhrCHP%Ur+M+-$TV{Xt^74cY_J|uQay1B`>IK!#6wy$MF ztjm_9&MfXCCC|yecuar9YrVH9Q)XLrN}P-17>?6KoOId(N%wG{fSGiT$Nie)YSS== zV|A^YvyS7L(bR*Mp1pf(>P|oX^!W5lKRww`SeTFF0Tw9XQnpAdP{_N)LWQS9qmQz= zY%$AVKmzEk||E$zX_6Su;4B3(&TkQ+6R>(Md0+>j=tqQ0 zp=87@g_BXY6iHg{4s5%tJ!rHsqdnx7qRF^hvXTRIp9pvF2kAT%-ofFG?O?wE?PWLF#o0J7FQ&Wi}gT@T31i ze?vcLohgHzas1Q|{wBxqJiA*+fQyGF;6-$C`$9nLNlKhcy{}q2bF625?(LS;IpG zz1gt5m0iwqGT8;Vj=>ib30zmvc5Hu1A)YU6C?r2b3EBkCnM80>`@^{^x2c}18 zKM=Ev9Nng`FrM#>RM?@CN_|2mTR@B81{6yEpWxH)vStdyAIDTbl56(E+8UuXC9U82KOttp<@R{h3T{XitALGo@Y6}v2r>TNLo~3;{UEx8~OF^J+_C}mtT;& z!j+dDI(Jd*#+~|se-vm+sk3Kff}Gu?eZd&u5RUxLwU^$3J1=?TW|TSt^z;c65Y_`JoM@&W5QG;2+Z22M2WA1zGs z^FGA?ffz=_2w-qD8xb3v&c~49n9kwzHJ&i|R(vOCj-h@FLMHg^3Kmm@1^*g?1Gt9Q zIZNXWOfiiF|AaV4{PUT4fvYpm5POFBFUUPbV&(+N7=A`F{zPW8C+Lb}^C`4h?#M9l zgmH09@HvDepIH*`!5Dh+Ci?IW`h(mwOM=iyTjoRX9eEK&lpsQ<0tCBb)ajETsP} z(YY|XSEBpQM;D^#n~eSdA4bu_LA1$)+R!iJiB@5*w-l1OOqfL!2_aM_11O3W6w$8& utkw$Vk;1K0+s`wP{05(6VCxuHen;YTq=_fO&`fYF%{wEvW{K-DeDohOKNK7Q literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/model/ChatMessage.class b/code/link/link-backend/target/classes/com/link/api/model/ChatMessage.class new file mode 100644 index 0000000000000000000000000000000000000000..4a9e56fb3a235601c4ddb353023f30a870a28912 GIT binary patch literal 2917 zcmbVN>2ljt6#nEz@*+Eqvy?zc(ln_Z#feJ^OPd8+Lj$SP6r2Xit~er5k>!jnGt*&s z4c>wom>~%RGdutf#c+r5!g+pbNGR5y>EG*BOSg}!4X0z;$LkVOy{^%*^%k8g&9?0rwj&|q=$&K3c|u1L zvTfTkZG%6$5()yO=a^QtZgw4lNH@%5TX%XL!dU1#-Wc?`#aA4&Wpo|Abt*yHksxoF zw&`q1h?kcRh;XfaL|`Lz^82XQIy5@_`l029&~ED1f!;Cs+fyc-6O)+D)tl{Bm8gDK z)lbc8iy*9O?L>EWjc!*zHpuOr@*ti6qG3*jUBfwPA3fGPlq|>1#l!yzH|UZbD3tMi zw@~Bdr9l%i=w2F3L^x)MtXDKq&n($<)x=kZ&T4*v>B#@cvExv|bL=Pdou~7?a1RtO z`GvdS=74fY?eheG1#ZlXCP3p@ChJ#9O^NZ!UFtWF^urJ;2NpCM^%k|7%FzYNM0tlz zQ%+lq1T}x7-Rm@s2PQXMA=E&tJU%KGaZ|z9D!#!z6*sV;;$^&|Vw#s%d09k7#SBZc zn3Hg=f9me{OzVgzs~7_RfoF8Hq(a9bFHK@vnY$d$%@P&2GUp8_f8{H4!H{~2(4cNI z#*vHRkD9A9RJ?~<5@udJEEIV_d++d>(WGY~q-s3xIz~&vXtUSp(3`;--8C(X2T|5f zPYs)#uSB)!KxcdDpz)k8*9IFuIKBcWIIX|Pews=<)ElnTqdG;8v5XiOkfa#b(A}ww z?oLH?cd8>Ky1_Q{^rNbiTp%4a1Fa?c+CrQpK-DfFX}=?OhWM}UjWVryIt6k_kd(y; zme^9*L&GwCF;<1J8Ox$QLHjsiCA8%;B(=&JWNqaPN`N_uB-s?natvvV6Y>N}lPP+F6IT3^F$FOs{obKtU@(jWqHd)B=@DJ*>Evd5gcB_)@egx4ea$o+h-NAg*V z<|Fs>nIFQ3o0Mit@Zo|Rxv$>$^b=MK2^JXzuX$@KHlBKU}*Unj&3Y*M!}NIl#~8LnGA)P>7K zc!@`3i>$@q%k=j!A26Mz1L5o)Qof6OvND_#nDX=HXYdAHm-s)!fgc5Qg7%>^OGa57H)uQd(M|O;QP8xPf{A2?;5DNI|_uYiH$daqP-*`nx~^ zA#vab@S_m38#hUfbD7zloqgw-+5PkP*KYtXaOT3sQwMcCQ|3gOQ)N0fx;AMCRuV zwNCefwBMv7I^XQeM0}Wz1Cf2^L9FG{kjEE1lWJcC&HPf5#(qCc$9^o6FFwDP{+J|U ze~?c{BFU+!9f|z4Ms9VwOH>PJdvcr6?o=cwu&wtZzf4D;ct+jjBBQd7)}3{`b4PjJ zucL)$DXnGcB10)Rw2U50L#OMdtE0t`TphpN4AN;9inmgY#Vgu;uHM4K1~xr3;COJc z=D|~|b*wS$t)39|ws^>o!IcPe9MOB*^c_qXN{&{BQq-PAwI^Nj4w$M+c8hjCc}Cvq z{RI1_)9#V2>tGE{T6?nuY-5LZ3@xgWb6VpQeZv#g@LLR0-QJfJg+9)lcB3lA%pYGtNoCDT40QH}qYYGtN2G3~BkQnQ{j zxti&qa!zKMX|Iy$uzF5rndyL-4p%U#cb_wPn(1-noUAg_aV67H^_;9S(+M%1u3%Ds OaL%-@nU2XCc=8WEkE`$i literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/model/UserInfo.class b/code/link/link-backend/target/classes/com/link/api/model/UserInfo.class new file mode 100644 index 0000000000000000000000000000000000000000..a5c9d44d1da03309364bf829eb244bdaf2e03e95 GIT binary patch literal 1022 zcma)4TWb?R7(JWKy@_pOY^|}@MulY6?u##?2vQJ~sKwU2Pm*mq>F#WpomBl-3I@Rk ze}F$qJhQ1OZG)6$zuSE0e3zNue}4V~@B|Oa$m324Ic!;R*NW`|ng!f*ke?)hem!td z>x{&S@I#@7{=U&t4WBtE+EXQ>faX{v$){K|HUC^HXBo^DN_FguvGgNSg#Jf6fjW%Y@53AGy^M$mz6A?~0dTAJ#bQ&(X-fCZ7rGq7=?9Fe&dKNId zqVj$`(f#0+v}>&XTg#5U*9w+!zk&ja6_l(fqv)XVpKWrPXAk0iZxr;61Gm-gVw>+L z&zI$*W(jH4EN7}oOPvK9MTNRbFGPkGQ1^bo@g7c*@g7a#dXJ~beq%g?Ym{qb1y-23 zO12tf*ZI}4%&0fR^K_LSnbNvWYT3$})Uvg0Pe9dW0!RbS3DSTKbEVWb_%~fTYy8&} z=FQF0nOUZJ6C0Fv0H4|x%X#0C`ATEfv84po(+(@MgwqZk+*%0EUIaH5f*T9L?nUs{ KLh$Xhzs#DL?j)|d!J|ENFACK|KEM2&xp{4P!hX zaL;|;^S=rQnr<@{7f<@&@&uQ&-KnGQ)heiOtLp?WYW20KAoNK9VyJ_lM_ZtPvwv0 zjO>7(-pwVGDZb|gc14EI=;!s`Sv@t~8_Q>tsp;JhqLGO@GF*6_Wt}@W+dIZp^{kPI z<@LNVXQc96$Q4hgQUa|NtAkV+@q99!;^PFvdz)vqexEilWJKF7^_GkPv}A)QU| zn7Eigkjuo+j_4WlAQW6wFwZMg@JRvx;KjI+k!$1>d`h5Y#K_O26JvUovGPWidAnwl zIqtu+!T!6fR0{dzZ0|6wn5RR!tg38c+{p1*V}`lKTz-y`s%EPXz>_sinL~Zlw%QAw z&y+5;bBXO9ORm18{@n_mRq&jG=LLMRmMb!iUC>j8x@Q3T2{ae>SVn zO9-iW1z+NAsdyEy@yb+u8JD@`%BmLk4iu8J2_viGD|lVSSMfDMFB+C_Usv%Be3N1i z#c$yXZ(qf?@rJ2*(B|%BhC;hItC5qmfeh_~F6vLGDKiD&E9f0viUn zNd{T6n;KJr?guY21bk(4M^2w1JX$ca^E}k*pmuMm@+==eJzXi| zBJsf_pJv_Q2@j|C~Nzu^)ixo zA?lM(8*81~qJBF0O((zYr1=&!mkjTg;ax@et?(Yrt$FP5C2e2Sqj{LMXCWGpP8(~T z+M+6*R6D%oqy-kP!;iffz{mJ$r{Pgd+2b=jjV$KzEafT8K+138yLgAPir4X;J^q6F z-{O7zf%1E}4JkX#lYh)SQPn(I6-{WPtf3hXqlM?cf@_5sImpN%dIsqk!auRnys3WP zTN~Hj%4_xTrv8R)_%K|IeHYv5DSz(rtMyUyM(<)L-yB@&A9##!PJD#(KSax-+?ImA zs(-r_L>2JkAPT`pV2F>y{J)4+_^a&bQ)OB<_bhT9PrV%wTN+JmWW~H{W4WrWIJ<}c zUEI}P^z$v~#lAx#ObEMk=dH_#etyMfhIv=^aC#p=)+OR=`3xPf&= ze8_?0R~K(e@J?x7iAzT9uyE#gu)p{Cg3OWHm%O5r}01qpB&zQ(C78l+Ru4sSVW>cW;=cv6z2okwMI z>T$_;&28HCmy|oHbVsRPh&It&rxp;2QSh0D-$!tieGR3pY{pyh%x?M4(Mcr+C$*pFlud!XWc-kbhR$ zwKRvRAvHw+YZjS?uY`S)&?4L8B!_}0Bt`IVJWJyjz%zALfJuBV_z2j_@XBlX{0^6ZQ8( z;i${(QWn8wUyyCeVz|rhPKukDl>JKakiQSfm!j3LYKUw1t0lxWwaXLNoUS7-wROa` z`_&TSn%d=wYfje@ms%26_N&7@l5x^tEot^&$M(?I~ zC%wDzA0kmJ5%)a+UJ%1e3#(MhHtqscxxMj4PSnQ z+0Du?dyIsy_y2DU{sxVK-(Pk|D>Vkc3Eb`2#nbV!E=NoN%kcy^&%cNfQeNt&O!+c@ zm%TzcKsaBs$D6eKIev*>Q@(~Gq-+6qyLo_HDfQB08&+{#&CjJU4?aR@jZ;3s@fydQ z96#ViNWku39bZMN$d4>HqrP9N=(tk`>>V4hPW;IPr&+)8h6LvxRB&`ZSXLlhWwjiH jq)CIiT7kA@!0gKr$`w_DkQ24=^Et}!6PVz%1IPXcaZyGS literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/repository/StudentDAO.class b/code/link/link-backend/target/classes/com/link/api/repository/StudentDAO.class new file mode 100644 index 0000000000000000000000000000000000000000..32af3a42a851871ac879ce295c688aac5d56bf71 GIT binary patch literal 2006 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 A$^ZZW literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/repository/UserRepository.class b/code/link/link-backend/target/classes/com/link/api/repository/UserRepository.class new file mode 100644 index 0000000000000000000000000000000000000000..cfe9618bcf4f7bd1ecd4a391d9258ab72fe55346 GIT binary patch literal 3105 zcmbVOTWl0n82--QW-n9P?zW}FMJlwlg_cDHlx-2A6sfcYx`hf}%5-;V2ev!Q>}+YD zG{(flXCE+;7~=zpi3W@Ts)>CueK6t4g!t-{L=s;0$@-nyOS^Q7iQDe^&-u^)fB$`U ze);Ezp8)K~To@V-2k}}MNAP+CM{z8I0UVdhU<4=dMi3_@9SY*jAWnrajFBKl!#IsI z3eJX6k1=_lR4^V!6z8O7QUZ~4K8zMjD0oZ31%W`nnKhl)1iW3{=LLKNR@xA#A2zeb zXkm8Bu*dbO3^~zZE2U@7>$WM+C8p1rG4ldl!zpVvo-wmm<9g1F+eXgHn~r5K#Lwjo zd#rNo6NpV4j%1$E^ZDzRoj$fOlolB18or{>>G6!7osK6R+ssb)t><)Co0+Aj8P~#4 z_OeB6k4dQLdOd+)fhM#1EaM2~N|q^a)Ay#LeEwP{K4#<#87FBt#Pa2xSw~>o>g^eZ zrQ1e2>FACzYh+!il$Fg2G_OhxkYS`8)5;P=&|c}zjcjl}W#meCie*n1a~+V*$t-y3 zgg_k)q^^$WIXA}&E-HANWmNEvf_GV57EqvJ#BgS;^cme|P>vx32`8X|W7u zRstLPEKL^Mqna(C6lPdMDL+-O-+}2YDo<}QxCFI8gHW{{Z-NsX#RFL|2--Ed0UT^0baG60o$Gut< zzz($W6e844f>7E25&UQ(Vj$`n*?k}0QIYWV_AJ8ZLBcN*0nP8*vk1Qzntu@ifrKIw zK}}hNBJdgRYf3b@h!BNC+=h$WFt?H7Hu4?RXx&|GBGtmAKcfDE7Sh665x=phoCNH#<*i_SgJ?+<5+uyX5 zP&9>QP?i!6HM7u2ZyHNVMQ^l*rG#JeCw!WJDG{xalNf!AxhiGfqD!N_cd=RA#&&#w zkMRj#A#UMY+#!8{U-6LiZ>$IpqGE^W6y2nQ;*=OAzXx+7Mfw;|Tq@5Qc9}erih1$A z_>l4`k#+f-aZ}uo@}gN%@^83j$^TO7kxweV5#Nh@?)_ITKZ2jd16QwKQtCZ$&yxSp z)$5m3{4V|wf4cX-xqL4ki50#8JrLWGh{{M*c7YlFt< zw%{N~?I@>Zn6eX`r%BGyG;in7j+^MfBkmt_qMo2#G^0aY!cJyFW3=sztcl)t(9*jQV{1zU&be@b`_QVg-DFUuH`R{V1F=&guKghNPBf)CL+$v%Ds`nY<~|1TWc BsNetq literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/util/BatchColumn.class b/code/link/link-backend/target/classes/com/link/api/util/BatchColumn.class new file mode 100644 index 0000000000000000000000000000000000000000..8a24f852d29387f9a2a6d7b7ba77e26b9d507554 GIT binary patch literal 4816 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 literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/util/BatchTable$1.class b/code/link/link-backend/target/classes/com/link/api/util/BatchTable$1.class new file mode 100644 index 0000000000000000000000000000000000000000..eb9d3e29bdcbdebe3ea7164cd4ebb3b1ac2dab83 GIT binary patch literal 621 zcma)3-%CO<7(G+dG}E%*5Ya;q8q^AUQ-qR&;#!c=`&>(>*In72k$+ZCK@a@_{ZY|( zuR_v;w6X8|&f)Ca&i3{F@d@AzJ2@n=mce=k8w}~1Hrmx0lI6-HL#h!xDTbn_jk+BT z+R8qNwogpS3#9NLh1C(Bk}22KAw&M!7-bv22t!41-;=?B``Yxm7-~Lp+UFO-$!@&s z;Izt+ZRw5?Ze*$RYb<{jp;PvC;Pal4{V*_G23B!59xDDo;a)u}tBh1NuP0u_q^qYx zH;bEMIEB729o}+Qn@)AIA!hE{JtbYW9#4i`D;Qa+u5{$E_WwUBWB_79qf&Y1TqxUG3l^Kb$%j17E6Rugd^253UMxCC1zJqLXN5& JsS}ie#UJK5q?iBz literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/util/BatchTable$2.class b/code/link/link-backend/target/classes/com/link/api/util/BatchTable$2.class new file mode 100644 index 0000000000000000000000000000000000000000..577d4e4a08b80b587237e8a733d17a12989af40a GIT binary patch literal 603 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 literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/util/BatchTableDelete.class b/code/link/link-backend/target/classes/com/link/api/util/BatchTableDelete.class new file mode 100644 index 0000000000000000000000000000000000000000..51cb8122d5cba2c49c30d983d0a2fb44d3923aa5 GIT binary patch literal 13012 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 R1BHXI5;jQTJRZUMe*^;gx9|V} literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/util/BatchTableV2.class b/code/link/link-backend/target/classes/com/link/api/util/BatchTableV2.class new file mode 100644 index 0000000000000000000000000000000000000000..191155361b120a7b3158e6665ad00826cedcc5e5 GIT binary patch literal 14669 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 b;~6zRyef-N=W;$nwJzj~_zQMljN|_g&{Zq0 literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/util/BatchTableV220250829.class b/code/link/link-backend/target/classes/com/link/api/util/BatchTableV220250829.class new file mode 100644 index 0000000000000000000000000000000000000000..58efdd040353ebf4167bbdf18f251074ee0a3a33 GIT binary patch literal 10688 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 literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/util/BatchTableV3.class b/code/link/link-backend/target/classes/com/link/api/util/BatchTableV3.class new file mode 100644 index 0000000000000000000000000000000000000000..bf2c2dbe2994027c7e76cacdbd5a3a23b636dc75 GIT binary patch literal 10891 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 literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/util/ConnectionManager.class b/code/link/link-backend/target/classes/com/link/api/util/ConnectionManager.class new file mode 100644 index 0000000000000000000000000000000000000000..c1393da57493a6eabb1b3aefcc7b92e6f8bca279 GIT binary patch literal 718 zcma)4O>fjN5Ph@V-Ox0pAGBM_H$q759`L0Hq#~rMz$t*BXz$*v8{8&#a2(qHE0B;7 z9QXnJD8x8j?Q-aW%Z%Rm`OTZLfB*UU3&0*8#OUGXBD&aK!Y$m6a3{iDLOhXP9@)la ziZDn|Q9AuF{|$rw%<3#>r#z$t;dBrFvfc6wKX;2X=+_A>dI-8A3Is9GwV*p zSV>chW0`sDE`*$25C@I7XWFR=%l4mXqy2Nj!p`o9&^@#hcCnIbquw;-Sh)|9yAjq? zo5^A%oep?f?D~(I+dob-TZ%%PQz0uY8m|j+XpK=>@b_98nJdS=mUHFjb;8!pZaZx| z?EHY4@yqi}Rqb>U?lHbx&nq3Q4Cd6S_LHzP!tly9)LuDd{Y9nNR9RW0jNeZXV<|x& zQH1*m*04@^^dD0YRzojQNRx}V;}af{u-TTviygg7XU+*5S3M;j@(1Yghahx9f6mt! z^*Jwc?r>&n91})@GjZ+>zk|NE$IBc88BDs)F}WmQ1*?oGjKU(}@*bnWi-+IP`GEz( e7e*b-71sVyh^GoSnA7EZ8_e8n`Gc9kE#NQuD6Dt@ literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/util/DruidDataSourceManager.class b/code/link/link-backend/target/classes/com/link/api/util/DruidDataSourceManager.class new file mode 100644 index 0000000000000000000000000000000000000000..dadc0b7016f546ec8779452e0134d064eca8d35b GIT binary patch literal 3814 zcma)9`*##q9skT`H?!GHNJzqCd6krkd9u(}r3s=T;gNO|0!cv-t&`nJGBBCh&dx$w zt5$6-)KY6}Yqi)`ZH-pNQ0h{M5FZFy>$Ca~^qAQG!0|`V(H`(~?`*QWX%2^+lRNkO zxZn5pd+)w+_1pyjx8olXv|_Lf5e$`M7^aGY5dwz*q7{=!W@Oc4zA&dvb_lseCNqoN?#zW%! zuvoq#mPf>LG(_w8S94TA$HI72e7`D|$Ha16ERRR>k+izNfl3p@eN_Z)57>? z%JEG+TaItx+hTc69C$(yPN?`!1RI4z&nqbJ*x9qKYx_OD?fp9xRL2kMhxM4H+e5KF z&o%9#HU$+Oj-B>&+uNgC8G~?A_w+s|<0g!5J*A-1Ka}xID@FuQl=YcIw(ezIa^F=< z-SmD9@q{xH(=BsAAJAh-H)AGaDaWy5onqTLtE+7@C6{h8ZPVMVprpQGkAh%_lcbKy zxM>?bnUMj*?bin^Vyfd#Lbvwlt|{OgGUyGPX$6f^p=H{KVtUGqNxSFh>eg+2$Z&=8 zLxxBHZ6o2Cj;)}f{{0R#_^9-~RxHoTv#R3``344()LVa}j#6u%rzZ|En9_3Aq=LxS z(S(r_Mx<4|prEST@P?gaukPw2hG)1`ur%Qsx@Y7=w#{`$wwWRxo8~q91_Q4klyaRE zH7qVjW-n2O>zPKHt_DoTW|2=domiKh%6KdwLmwegp#eFXtgKm2NDB|rz8<=u;zbqT zWihjVSjwa%EGKQWTb7FN5!#es#$@Uve(m{=EuTMg>qH%E*n$oXoAEIf-`DUGURJR7 z#(dV$hkgw|z?guGdFgcKL>_KxV9nI zQNxe%6BR$za1uXLF|OehURAIxuX%UI_RJAuHhv0LPM>&X`q+i3qvO*j&P_l6(7O*G z|JV7G)6YEe&xvg7Y7v0b3TmYUS?BqrX?P7kXCGx;OG6fCG`uc&!GToDO=>tN;L@aP9yVMJ6F9Bm7dWrsf>Mv4+d&P*9V1tAE&aj)*?h@H_mzh*aq6)T1xHbN>AF)8o^}PfneC%6E0@rBm;` z_OkEl8yfzAH&y&m!=LbHmW_tL;IA6~hPO2QT_|0fj|3Bu$hfXyd!lJ`+Z}p(SkAPn zymdPV4zl@K!4f5!yl+>$z^(#Te`L-$P}~QcNqOa6Y*^iMSUnYaq*PqOc|$u3+toLq zEjMLk2jx&qANx$f#(CHa#?qu4ppUlmRy#~k z()7{2y2)w^*54`kg>c^ZCU1LQI=eE4OZ%&c>^IWh-eHbyUsSMuzhJM-QFhu}9GCrE zh}muMEx-#RBu6@nUQZ_bGO3hnq|-)0_+;j0r$L(#+c;0)3o56Wx%9Ajp|EO}&J>9p zdL52s`S&+7SuhOqc-E#l+mefm4$rhIkE+9Zy2lu0i1_;0&p_D{D6Zw%)}*tB?xK#b z$yUDP0$7GNY=VN1N~oMLZzjSYmvB2m-1EZX+DS}|UxnXL;~6NYB+FK=r4m_zZQN@< z1GZxap$1-5ccMfd9_2Sc+}c+WXgrIO07r8$-gFr?jjtmV7ucq=C@sO>rYzKPa+W|y zUrZx_YE+L+>Wi8RN0@m5vZKaCkGo@6?ub%-;oT_+#c4JV@$esK=A6r)SZK7tn+;H1kQ)!Uw}TJ_2H_r46*5IwhF+pWbGgrbNDqDaffDxxORkQky7(<(_Ti9c_*3vAi$HruTtzkDRr z1QJbr03XVD?zTWdFsA9w+}W8qbIzUp_51rz0LyriLJ9>PI_6T)Ft1@Dg%}pq!O-wP z*@A|L8kPjoUaeu7L0Pt1mM^eSGQFm;CEJ1J?=>BxAXtyJWV_azPP1nDRatWw87g_EbT*}Lt8*_H3wG?bz(k1Y*zT?& zTei^&Y{z&_(Dm{=x(X!l)LCF=w$zXxq~S<+yEn;7zAsb>eB0e-g&^eeB~(n$x5EA# zzGqtPHY;B2nN};XJ(s0X3Zmxy3rJv~5>O6G70ypt1+rx;*zxKc(w9w(nvqU&%lDey zd7t*r+Z(R0@tPys?K1?r>T-%$BP85E#tFH&>B#+kfx*983FOY|{Hb-Ub?$s}v07bO zC6ZpTw_O=@d`h-ftUl|@>rJ!zA1u1v^6W_giY6~J+=^7i>-eVi(pEc=Ila_HrEVGn zxSGZlq|pmgc^*$74(g%w1f5;pz&EtL)2`ju(6W{<#w$eLJu=rPHyV zx4XANQZ24r5H3d5mnLcIz9s8n7N+ZBZG<-S6_LUrq6ek9L&U{FSfC+EtZ3M7h#rWaWHX)U z3SYI(v0Nuu4x?dB2G?$T>?p=oN)m^J0tp3@0B!>$w&|Lt3rz@d|9jsu?@1He z2`wM+`LnJ2?z!ild+xdC{`Z_CKmO_iCjeY4>Oo@{^frbNDA zxs>c%L|>sdl^2+z)=Z}|ea(r1RP$gVm2O_aMc45o&o#zu9W-(~7iH6f{h3AC%%;?4 zf!XzKTN2w6&FMsDb8}}Qm&$COJDSp9l9a@@RK7rAWjU@`Ytr*u)6Hv){9wA!X~;2O zzR+I~m|PrPl`{qsIV0IwNED2IBcr%_vYCuPU6G4QWoVRCHbWtTyS2uYa;-ahi~(yb zkj-s2Eu@>X-RcVjY2&ofk?5z0aw;cK$%T9RXl-S&0A2~;m-OQRUJc+}K!6@B5OS8t z2ZRu)X*UYJ+2pE3j*@te+;FfnwKFL!u z`$DhHnw}Qu$)?Ra<=>V_4=OM!Vu$6bytgU-Th5C|IRYAQPGT`Z&jSYzSn!6aQTm}q z@r$y9nL0PPZ5A5DVs|gxmfp3MPHT8rzC;}gfAleqJkh45donQ zh9V*;LNwep5fK)V2!0>$Au>forBrup(adXmX;z7D5m6U9aiaOjlRVi9rRqdqCNje+H+k=cTzHcg=-(dZTORbh(^Rj zF)1J>N5m8{HG+5G#)!B~!~`atJ^q!`x9xuJ{s+vP7hBP}vLj~SxsOTB>Kqo&ptxMT ziEtPZSBN)9#Fb*2yiwByo^@?R**7EcOP-R`v+qTZ8_$x)rUaw3^%A}nySf<40OrXk zVsEOTTK8eQ32F^D!Fv(%3bDKLgEmlZN)&i@9)z@ya3n$&5A3g`Srk%OJ0Mb6uB1T5 z*(kZ3Z^xxQ6rbFs`M@Y7;F~K=2F#qrNm&H0KL7MlW}%c| z-YD2+9aq2H41nF?m(2De#tx%ruwbl~`I*4DdY4X4s6=7@o^&=(Pp(WPlbwSDR4|`6 z=vWindN~ooTq!#49vYqBd6|6~2C6Cry@{IBcD9e$Nrp#p*Iu09uY6#T0_M0EwZ=ws zTbooeivqYc`bh}^4b;iyzMVUnTK7}gtX$Db6jNFHW-22sZY26?F*9=Vl6j{GBvV=@ zpYDY$^GLJl+jFUcp>Xp0ynTKiD_9!4H(|{ume6!X|D9U$IPGyNS*5(x%*q+9sRLjD zT_m@eaZ_FW@{-Gqpj(>Za#l7ru(rdSD<30%Rq>ngFMqy^y=Jz8tv`Mg;vhfDu#rD2 z6ful<^E+ZPpbIy%7eoTx@X5*LPqQ6E**EEB?eY6j-XU6iGn$6rD?^K4v{Xd>zQ!T= z%Mtba^$P222n_970-{BW2JVN}5{w2;;>xIY647X22tk3p7}pYtYSGXUg!dw3QKNpz zRw3E^LyQeAwhD*Mq|P{rE26%Wm@s1qm6EBBvV0a(&?H8K2e@pI8U%-1!X?*_aQ%pP z{qS&0AR6GV0>dqpCAF!FRz$0;Nv1Z@%HbA&)ZgNZ`iEPpOXyZZ-D=bnTes@rj$^34 zQLmYC6k~=l7E95A)vUE;Fo+#&KY`ESvutN$J`P#!F`mq$_!gdI{}7I|xCV?z5@%HZ zjA~{71=3$(UR9y!FRFeR9~F~jUrbi5>|e=Ky;{r`*D3mR*+)6{iw?TaAR7HYRjkBi z%Hx)?6f+Zt(Zd-5)Cvs-Ho*@~EWu{RS*!Ck+_DfcSsay9E4ySuIN-hSIeLNa}*G?r*Q7bS!&8MvYz9fBj;n} zE^^iaQfQzguSZ-}X%@N75yJc{gD^bLeAb zuYig9;4gRuy(aMsO0&(ei&}lK1S-bfC@Nll2^D`AP;n+ba5jBv7Cq^$M8#Re#OsKP zE%e!W3cu%L0WoeNQBh*~Jfh+PqT(!~;zFY0ETZBsPp7vPpk)T))yom=@QkdkFP$y}?6>$92YKE--G2>x_(Cgsb9u zvx`s!H^fcgmssJ}i6s3n&P)N-r@P(N}Zp#HD})Q|IoWca#DzxqNz?G0M5 zxdhJ_g6H{IOpgc=-1_JPA$)`$a0lBa26lH@txPwL<6#D_A=2)J?2l(|a!U12saEz+ zll~%J!Yhh?R`tVpztCl$6=~VZ{v@8|G|?oQ6@7~A6M*-K#SB{S5{p%92CXajt7#b! zlrxRdm&$Gs%fD!D*t8%%m6I#K3b8zwaXLAp57ta~cL$d?%$FqLpE(Op#>3 z+E5Hw@v0NB&a(p6R<2|!RPHI1=n0Yg^FOm6&IVtSTyhFn>-QMU;{yUhB zGZ(kS`>m3|oMMh1DVd{Ja#E!w-kcPT3^Or*ho@+;f>}FrcEz{At|ZJVrt(!KQu%7m zs8%!VRK99>FRIa2-QR%FswF147r6V$*t@g7@&WE{{iQ2>|=jC)BT67 z_M4SghWg<3ig>4uMz8rgeC&6XUYnu zcje6GG_DF6m2RQbU1AMBM5(*P zdVEkM@L}c0YbbFh|NSxjJxE;Ks@`)Z@_63C#T6DVu6N;Lw}Xq_4lZ`Na4~|3=V-bM zrsUz}Ot-_PrCyHkabHCB^iE3HGEqoA??SfEmm{m80}rFV!{ zv~!MyJZz0>7_Q@HZMmJblz9RR?7GZSD+6iq`DXj;{8PSwhNe1#;e6I@E8_BN0lxyo zGXktxu({PQ_MUsHG3v9cGDlpAz-%fCe2Ghcng93iZq`)pVI^-LYc%_D4G!?24-)od z=)a!_eVB)QAFXiI0gJhu@o|Dh1-T-0hEMQfHWGq9Nr&)L${aey`;>hR=kPaL)~jX` z@n@_xl~Kk@Le!^d0=uqb&invpYIMZg9fyd*_bSHGtO5Lm_$6us_)GCJ8eiryU7*VI zStofY*1q3%p-1zzYTpYlr4biOBYqod-(RK24b#I;(0v}{tv6 z)^;965|7dQPx2N#!Ya?Btn!>-mFF>5c}}p(^BAi$%zRKUFV}FmJ{(ZjQPZ3z3W{ma>0s04o*dOxQ`w^Y| z6y5(>R(XERD$h@xhcL@6A2;0Bmyh0WKS{{rr)alilo literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/util/JwtAuthenticationFilter.class b/code/link/link-backend/target/classes/com/link/api/util/JwtAuthenticationFilter.class new file mode 100644 index 0000000000000000000000000000000000000000..bc0ce3b9ee98b55985bd1971c195dd1d094e1b81 GIT binary patch literal 4331 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 E0UC^r0RR91 literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/util/JwtUtil.class b/code/link/link-backend/target/classes/com/link/api/util/JwtUtil.class new file mode 100644 index 0000000000000000000000000000000000000000..400af62b8d5fa936f685e9585eaac0fafa13a245 GIT binary patch literal 2349 zcma)8YgZFj6x}xu29gmmAo5UDY86C@r6MX;1&kF4kD7?m+R`Dp4Fi*zbY>#Zmw%x@ zh4u@kuGQ6k?T_l}zLOY`#42lXALpLOKKq<|C;$BW$DaVM<7Es3n2g~LK8xcn?zLi6 z&G$7-#n6IDwV94#1`jl(qBKGD;&Z5@hwA9_Hax;?E9TT@UJYsGYaxzBEU95RjxX?7 zLne+>$ZB|^<`oT3W9Y{-4XYZy6o_R~Gmlf*wfWSlKq9?qyfBiMVHc7a-!<*Rq`E9Iy4-KyfL}r{k)sCl4TP~K1>(b2{ z>lP)6w39Qe6~i^vz9x+L8>T1FpUyeOq-EM$Nuy*Y%f4wP=eGSPoE3zEw54nKGV5$f zTj1tUBhW_4aHB||wd_gPHj2a(b=-oP7wA}!{)UrZHe8B*=@L$?WVqg&Xk$&H4L8TT zZ0uT&ktgJqoMo6ry6Q=rPIA+8>}|R3EBMKo;DkDYAWj|vncB(8l5aXT&!T}YQ_uKD zZfn6P1$?n;s$@>t&B=$RB2}-kG^#46;{ra`@Rg3|_*%m^I@a*54g>1~?Llb+Vjqx6 z9XaGRaQ+Sjf!=r0uY7LkFqQVEjxAWM?Jg0lc{1lpe{DL|k2;h?1Bd$<}r9X`rB zUSM0pj*eaYAkf{M2pvD7#3~$ZJsm&cXMxjAk=2M+;C>P4Jg}{E_?E||^-Y=c1-gzx zAbYHlwq7=^ymSQyhK3vJMV;yDIqTBb#6#_rmGPvXDc53MYuYyNHdjRtnnqx{iRqy} zsb(|z9;9)Cnf1J~%ug}%(VEM81H8}ta6NWCUls*Uf-Xw^9hIy z&8e$@j3HAyC6iZ`T`VwoG~0|>unoWL$|G<6XQ(w}zngwyp_>a~tzhf|^6 z;bCqBCYnSWE=9-Hle$GWpi|8M!GS}Qou&)xt(fFbnZEY3ktqIMnJ58|WxK8`Tg zd;>XNq(+jnP#@0A`w+hcCYLz21j059ajjPsFpSIGwP6HT=vW#53XLn^{wstoR}c;% z5gDl<8pdn1tnNdrpfwb%U!koFabq9HDrgVkcN|Z2RB$4U3K9Z)^bvxJJBU_}?TFww z<9472CzwbAhC^AgQSyOw}mJM1q$pnd_q+P_HR0kaGrdH zP8GZ>1Y$lJB7|;%zi=u|=sf}pBZ)|&w}QSfg}sZf(Z71->OM~IrNv&r!~|~Q3~5w& zFERwsizsQ1^G8DmPIEUvEN2PeJT(V#i}WkZ*QwP@`4~Q>pL5Jjp$}0n7N8GPGeoWN eAj%|e@D!NfKdZ5$(U;0DDtG{@+~OF*?f(GBR9&qA literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/util/PaginationUtils.class b/code/link/link-backend/target/classes/com/link/api/util/PaginationUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..1ec6d0f654e5927b024411ace53b56553dd74922 GIT binary patch literal 20185 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 literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/util/QueryByPkUtils.class b/code/link/link-backend/target/classes/com/link/api/util/QueryByPkUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..be409aa882feb979e477c2cfca0f6eaa7c2c5a37 GIT binary patch literal 4817 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 YVL40AigQ>X3$Lgvyg|-P$~-vme~&37$p8QV literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/util/Result.class b/code/link/link-backend/target/classes/com/link/api/util/Result.class new file mode 100644 index 0000000000000000000000000000000000000000..89c61bdb25c244c36c9910711e45707eb7b77c52 GIT binary patch literal 2338 zcma)7X-^YT6ur}#wse8Al**#)%d{+Y0T(1hqcJflZf!{9+fc?b(1o;96aRrf#t)i6 zNHp;W_@j*H&YKmcrXkE*?>Xn)ckb(-zdwHy(H89{DNfr7iqb>=JmSyeI6X+xtZU6$v23=DW(}K_W6jipYj2NE^{R=rMA@h{ zHM84Buw1ENCbnxd4Ra4eHGhEM7p-HgWlBaFtVkRJW*FRjq!YwGAHIlZts_ zK-5&J+G-T*M)Rzwog2ljY1E4!^iH?Vl(|wM@XNUqJlivB7s~cHR6V?TKvp@tubU^W z<9AwHYv`ulh6XikuP@q80Y^`UZ3QK-MOtSPJyU&wc3yom4pdM5kS;emHHk{Qm7SY` zdm->l-xJTt$7V=i?8W>q#1QM$mo0?v$b)avGC0-Q0Iv znpN|@B)C51ICsx@*wFwK2hdB)&p{N|(NQ&T`8W%F;ajg<1qIEc#^ZPeWQs5t zdJ)F6T0oLSLsmZn0>sk@zN46Vj#fe&EnJcG109*h@QqoM1C&Ld1F(U{DTnq}U}gd% zhMNQ(!=0g((PkK~Fnvjp!ulo29`GnBSWe=bg32^h4O%2~BFrWMfOQHc&RFM&_=q#u z#l%ziPw;UWQ0SNj4D0v`9ZZxB>L_enlH!TU;Z#hZ-~k7)+ zf`HDq1xC(E4vah_q=~f*)>!1A?21fdC~o35DDTjWE%-g8FhJ@TML#NqwJVB!$IyQp zIZKz|Nztrzxd^Z{NWS#&Oa`}Nr)L&09_yUNI&ZUXgOzh^vr0B=#OHx2i&c2QVP)i~ z1PRVr7J#}44~pamP~}1x>P&yA5-rg(6b7AoL6L8u_UC57hxb3HuFxvd%y1>NobZ_C zQqL2@`HQ|>$6OD@Y!7%fz-x{bkrB2cenKc8qFKIvpG$peF3{Qy&1-?85}KELXy#|Y z?~iMmMd_^5hE3wV2p6PW*Syk~W?}G6uomgAZHfao-_Y)>L7{!Mhj#IFdG+OJ=NHOz UIWq4#{2qvefcx|S_YtA3e{XhZoB#j- literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/util/TableDataService$FieldConfig.class b/code/link/link-backend/target/classes/com/link/api/util/TableDataService$FieldConfig.class new file mode 100644 index 0000000000000000000000000000000000000000..141ed55af9558d81912dcbd4c4af016efdeaa84b GIT binary patch literal 620 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 literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/util/Tools$Snowflake.class b/code/link/link-backend/target/classes/com/link/api/util/Tools$Snowflake.class new file mode 100644 index 0000000000000000000000000000000000000000..75a93aaa9b31605d421f1a481d45a53da4fcb985 GIT binary patch literal 1989 zcmah~&vR2%6#icF(w975X`804R3M2kSOPSlh(ZC|Cc&n(X=y@h{e`c|vpo9pQj-^c ztQ;23I8$*PrZdc-$hg9d3ukDn)Uo5%oeTd1T85Q7R~_-YNoWd)W^wL4_nsf$cg}e? zzx{ph7XXKmQxQbmhejkkk7_@mof6_LAqG^6q>4eDRxsqnunH9^1*0AY;IHYCFgQa^ z+8?;){JrOFUt%N$_4O z6laWLGABXH>5iT?Y^SPvOUB1#!_G2UqotRejF~rxAU`Xi;kap=&PgKd8XVj^EejekG=*L}l%4ZtNrEqJ7cPBd=`*y`lD2IWW6V=B zc(rRNTgXQ()1Hayvu3pHm{v4XC|IT48$LY}nuiVNd?7cg7xlbB)O2jHjmwT8Xu2$Mlwf7+LOnYyncCNqaaPx;!o71kw8++Fl<_updfb* ziZp7vP%dVTQ>LJ-R)BV2&@bv5x^Y6oeuOm~5F#SPJ|T|dgo1Z9yoaL_I)!o6((UQ! zSlJes6PL5btYa2z2|LzqEIzz`d-bbt*5)-%H1wiR!b|mRNy{>(b*ry9UCy)do3eJ)jnYohw5R*arj;{_#8WL$C>al}u6+9N z+vWSqbNBCk5jq%Jz5aPf!#R8?gf2k|-T!Is;f=c*rjRATdM%&h%$C;Ej!a!Jvd$An z=_@72$V+I+mWxH=6(jk$6>VKZb>AxF9?{Z{=ahxBqR-A6HfQC)zqvjWtyTce-F9~U zKd!w_p_*SppzB$~9vtG^5acW2MJGZCgDs)vLJRd>oAlk(_dKP)yh-nZUrZEz{bHyp zF*s|LmE*=$X6$Z68T%l$GTaakMKH1iDdJmzD-wWv0iH;30S%Gx0uG&H0=b_9-eyPUPp(#Po@n&?Hav5NKX_zA3$rbKoOk31x;*x zwVXEA12XG|1~{mxJqQlY^wlcceB`7Z_bo zY-I=g!-AhGc6T2A6|Ps>Pa_~rnPJV(a?mF>C2!rBomTNmGP#VVN^RYh+A46ZLzcD5 ZAbI$?GL{Sd6&Yjiz?l4x!U%i0?H@*{wx$38 literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/util/Tools.class b/code/link/link-backend/target/classes/com/link/api/util/Tools.class new file mode 100644 index 0000000000000000000000000000000000000000..9d167b6c90b2d39a9b1f8c474dc11d5e37290b3e GIT binary patch literal 1585 zcma)6U2hXd6g}gQY`k&m#3@jd4?+mwkAh2p7U~e1hEN<#sN{$$s#-B|7JKn}*IKUw zNPVi>hd%YGQv2Ax@Klku5`jiar1DUy|078K3#cue*_hgq5-ATecjnHWbI;s+G=Kf~ z`v!n>xE?_O6B^EHIHx0s^E&tMs?~cME_CCf4h`>X$m!5AspAr+dT<#Z=!jub!?Zx8 zXgl}rSh8veMDw<7xHFb)G>nEocYeOOFg-srD{!n(a%x%2w5wTJH?vL8w6Y71V>QPA zzn&vjqm6YuncCN#(2TRp)UkqT8}rTDlHo4MC5xWkq9;q$8?x^2$yLLL0$sLo-^(ux z1e2*bfpAUMZ{86YPkvgsD_3OJl6E#0C5ku(1P<+?6M>_9oU|1b#oM?N#hW+~#UO?R#@a2-Tb5Cl*0fu0)(qR5 zyE!&~2e`TV=ptx-m3&WhUfQW%U(hWawyECq1TO3n-jQ_|fR+nq_&vnAa`G& zixEKPSBO6`9uF*D|HEI#))8FP^)-ZkgGfJni0(rAS!R=GtGv*IBN*egF&y`80Ef6c zOfaC2b3Y^EoCh$>R3~}*7EfXb{)1r+i7Ofozwnu`?JXupd>(-I1mVZ%`kC=m7w0Zo zU9!>7j`yh^B`^ zwO!Y>o}jPvKDd>aW!9K{mO(q6-VQ1OYV6b!&=1}lJf{P_%tgFik6Lth^fuK=pucj*8C literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/api/util/UserContext.class b/code/link/link-backend/target/classes/com/link/api/util/UserContext.class new file mode 100644 index 0000000000000000000000000000000000000000..9f49c7e8a6999496d1bb6919da46e0d05448b421 GIT binary patch literal 1373 zcma)4+fEZv6kTU}nRXbUR4ytaH-%CP^@57FAtoe7V-Zb}gvVhzDFeNbVM_cXU%dfF z662E(ev>h-J%hFe0>13C&%Uj_);T|ZefbJt0YwAd$R!cOoS1nrc^$V5p0^Ej;f|QQ za$OMbo{sx+=5#zz7;No1-g2|yJ8%58EzhwkrKWAw6oyLM*1namS&eGGBT-ODmfdQ@ z@>`yxuy}S=v1B*v`I_6<$y>W_zU8~M{OdMQVTohng8ZzaknWgK_C2>zWj5CIs*;Qs z-G=KgDNJWhmz|ExtSdy9n-#9sTXGxDi&lNp@z$)(8YAg|+PdYraz6=1{VjJ-Vf2EH z6;dmXztya~v^=Zs_>RY>$e!cVR>h8IW-c6&WTnU;#QIs^R`@S(-45SEp}Xu`_Rfm6 z8!XBP)v>6M;E2^wnwVX4ERXmZVVx-2)H{&MC^uW4?L2XXbgD!5c>y#rfQ*SWE}7^T z(}P}xk#iqRdIn95;Gv1@cqC@jL;)Fvfi{VbZ$jL5e06i%u_;kUDCEoT&Q}fV3c}`J zbxsoe4W3hP?mHOgXYS_jkKn4T56NE)7fnlLG5o`k(1UW?F8)EMm zRRODbkQ?SwW0Wqef5lVw@B578^XzvR*-y~kBC4b<610U|jZuv8U@MaiQR+Ssh%m`I z6WUMo>bUV($4!o(I(K~JtnpJEIsKpGr;$0?elpmd&l|8cM9Gk9xN{D1%prl9js{T_ Y=sHH%#u3bqh_;(TeGHDsvP8q|Z&WxG@fEiKd`P#lLiI6uH?f?Hl>`NmcwOPTYz z6rOnDKj4Y}6)-?33=GfwKfE&R*^1(c9a_yucemR8cK5s8yTAYa?N0!=@kJ6dxTE6| z?vCRE-qrA)hI=|j)H;u44e!Un03Bmk(eQzeaopAL;Rso)XE}-c_(;bD3K~{*LM-Z- z#F~bWb(~kK>pFQTY1q*43B#yty6yBL!*tmRT9)Vf2bSBiO05bPPq@gF#^A@dt5N&A`N0+i7baI!m*lC9$M@4>D7}e6Gy|R20lZD zVev&)+qK8sk@p)7!5g*=gbHeRn76hR+-_NgX2(C^^?l(=9>ta!@FLw74?5B+QKS`V zN4gpw7}&$Ua`QPualo)IsV{l0$NE!ipVxX-)=P3n1Tt^}FATsy6%Q5SBZh1JO?J#w zsTBc4F<*^bJETT6KD3+-T*Gzh-G0>LJx(t%Tp2(K_pPs*7|y9`usqvuSW#&*Obr92 zY84&7mnK8pRPB^DGlK;g`sr(n`Lkj<)?v`>dcAD7CBvHe=uFIzqx?&GO_(JW<7_v@ zj&wb$OW)}T!F_4%QW-p;z9P?+Vqtr0l}d81{J*NyQdiB5jg1#*DWAISIJ`}V>mnVm zbU+Q`lae?gzaj;-B6-M|gVI6CL&^;EbeLbFbDyPBs%dE!r*{@_Aci@btuPSf9Qfyj-KV7 zBhIjSBk>0k)%foisUBgp@*G+W`;l<0I+;A8qv(`)oOF%T!ink>UP&f@MCu5yo&Gsl z&He=TE6%ZmLYzAN|2!d1hr*LQ7wE18?tt!6%IO|uoX0vb!|AI07OVIk#V!Z)y_6yj zp20+hIMDDN9fLVaJ!HcKQme$q4Kh${+@fEKlH>217&D>T!|V1`C|{sSFlDOLag literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/im/ImServer.class b/code/link/link-backend/target/classes/com/link/im/ImServer.class new file mode 100644 index 0000000000000000000000000000000000000000..9a89959b17fe3829ca878973ba0b58209572fee3 GIT binary patch literal 4370 zcmb_f>w6Su75_~LnPf8&vXq-r2sDKdF2l_Wq*g;3Xk?QBiGiR}XE&2%$?nWLGn)`= zEo$qf)?U2TyHymb6)7}qfB?lyFV=d${sTPfhd%x2Hy?jzW|Ez4HpmC}d3N6SJ#)@^ z&*k@?_spyRxqJ=44*b0aTk)Y7w&AFX`{JYxRKtv69tPzxq+&RRYNUnjn9PpKY(zyS zhB_RTM^=R;qg{g>J`#fiSC*bE^D&HKOqR!0jK{DHAC<-ZqU2*Td!QB{$AdLEflsLT zWDKitv>Kn1@gaFUT#Zl5_?Z|!i_Zz~BenQE9*t4{3o0HH8y}a)6DppJVH3Vsi!b5J z)%c2vuc~-Tf!1MJrqh-&T-S6JROW2QQ=oJxSbi{XdFia#mv+<1jM-{gwr6;0+alYJ zgzXIJZq7+tLxYZyHOFjcL{FNA5q-7lvE4|?~!?7fhva@<7ZH?&Ztlp9BF`ZG<5rB$;fBBMFGo>v}vRHPUJ1{2&lz z#FmP$F?OzJ)8gC9IO~}Am~_YTPy5XIn%>i-uj`i)&`&=FGW<@8W3%+h#bYvmKlvJ=`cjN{@f8LL6xL9=@;Qq=p~hhbm5KIE`mC z{0KkR@T^dNB9t>4eu}fib~$J_ZWWWC({K*wHI~V@Yj_?LDlTX!U{b|J4VQ3P!O~(} zgCy?Gr!xblqhR~%PhWlg@`(mNgbjzxWME=LU~j{X$@4d!ID6xfXWqE-=s%u4d*ktw zZ(L#6r*K6cS6R3C;-8aeZ(MuGe{l^jsJO1-XZX3e_X`!j)bJuEHN0Q)u(D`JIk0PZ z2`_7SMOYUVS!Y16;a6x?@v4Sj<242A#P+EtZ)W?$&;RSGGYW1i7rYXlVN?4V@?>;N;O2Tf}K%&{X40ptd7q5boW6J(If8 zec$fZwtb22y~<6hWWY{^^+g~EW`+c{s(*oX5As+rT#-PA92Bdv!~KqwiC~FIEfM(u z2Dk35MrZc5x9@LFbo8~C*vgn>cn^z(N1_&nbXr)O8s;IK4$I;HmzlHgq~&v=l{nLU zBknb>L-I2hBxY{HIYAR}$dxv3rC5)fZb4b(66qP| zm6PSEwevjN;YCVo_Op%gc<4+cD^M|VIa7??6vh4YnT((#AceJ)f-6eF_vY8to*j@_ zfM|FT$D!)!YEx?%fg#L%*?_m)kaeR8^uvx(FzE2UoQm-^u^GTIcO;g#koRQFd-qUHi({(KU( z7obg{u7475No;7IAbkQ_0Sn>_3#h-2y7P$F< z4{?vfSc87X;Yb8M8zNTlOw=#*&x35=2P~|{Axga&-xE-tqxuRzxaIKO@8i3Y8wuA3 jW>kKV>+Sr^Jj$-}}DL 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 literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/im/manager/UserChannelManager.class b/code/link/link-backend/target/classes/com/link/im/manager/UserChannelManager.class new file mode 100644 index 0000000000000000000000000000000000000000..6dd28505c47e1b284436256293c13b4fd0622226 GIT binary patch literal 3262 zcma)8YjYD-7=BKgcAI2d(v;F#OD$Ni7hn-VfffWR*h;u)N)fznlWnqXvRgKrO2xaP zC|+m$!Y|Mdeh``A=8XCYXB>ZqBY%Q%e9rEsUD~wM$;|HAbKduT-plj8Cx88S_cs8$ z@m(D47>S`3&&AP(qw;Y~KAunU{6qpLaY}xkR`6m1F}#$(USy=wxKujRhzYrKR-V0_ zz&V^x;1#^8;I%kj#~TXXl-eT-bb*GVozEMNKq_rJd97TW+C8nMy}coU=1SRcMhd!R z8O0O&tUzCSTA$Okifb0NtZij0j$>G^HnOlB)yoA+Qa6?{^Oo*b97AAYjcUWbTv503 zTE=xuD?gMrZOt-VcV5f-erSO|Lk9`cFl<_;dr+XRuYW=yI%4Mtw=Qj3#>q-)(s0K0 z$s%_cT~;qn=#DABgUzU0Fv~KgtXW!@0(PwuL5PP;Vy|alsGq3yq_MQs zR#SrKCEd*y1O#OphqDa9qg^~>SDdVI%#ld=Tt^w0Y%^tKEQ_p zTi4be3O-WtF+QQf+GYf!Uu=X^lha1lRdAWSTvT7E5gUBA;tK2y6VBHe3bj+P)4zO+ z{pS;j?$xkSQ=`%2M8+U)OJ5-2O+Ic7#qlXVTf*Wh{P{S?@D-LSK=PCpV)6JEb&WFZ z?q7wgVZ^D5m32+q(oQ=@&dlnr!D-aAm?+Q5MnA%_DS=I4GA{r(;HAu!+%j#1sp6^(MQFBuMWc`_bS9G{;x!s2T`^dVQ*4fj=Gc|XLvpPyhfVCbcK$nxaLjV1dw7JSza+=967 ziEQGt!P`t=GuNswfXC6xuNbyqE6w+CeHL|G0jYs|NDkhE`U@fhgEvukR$8i~Ge9%n zzAp@ptViyM3`Jb^xbQ6K#nAj%-ubr3Ds#zic`bMQoBZpV%QbChrCa1${T67MFW z%iuO5${1BTxfnoG02hF$9!S0LKw`|p&Hx}20CWueiRg7A++=d@G9$PHC4yyGTT#z2 z+G$}CtHTQj)qv`&Sbi{1Vpo8*C%_5Jt4D+AOxA71<#-2KV`oh zQ+&&Ebo*(M*3zs>a_|n~0@t8Ke?YYECrbFGiF?w+NZ>%IEZqS}f=t@8P!`HQK$n82 z4~c3ptWGj6+c|*0%%sa&Ys5GZeha>L?mH(UGV8=MWv#o?qTjka0L7LPI0vtli1e~!&Q8ZFWFzC P+?Ne9M%fo|9O?f705d85 literal 0 HcmV?d00001 diff --git a/code/link/link-backend/target/classes/com/link/im/manager/WriteManager.class b/code/link/link-backend/target/classes/com/link/im/manager/WriteManager.class new file mode 100644 index 0000000000000000000000000000000000000000..c998a8d3577b7b6e8515f82ec7765f235e7cd6f6 GIT binary patch literal 2718 zcmb7GTXPd-7=FGq$+pRsLMeqJhX6v`q;yrvsVQhF4WLbFNog&3+H99(+Z^g* zIwLycjN>I2-r;cR01i+Y8NA`bpWzIse}Ng)=i6*bdcYZbk?-)n-+Or8=Y6-o{r&R; z06q9Lgcb~ha2$gnY(gT86F8~hR09uhhR}+)6byw>kEE0gD>yABBlQ>!;|x+EjNxn; zcRl(Pymnw8h` zdO@c@ZNj!3Gg&#%QFuq=S?EY zbk=y0dP)LX$BQl01XUBXf0`8$5R7X>jmF@-Va_^Mu|UU)7mGk+%3%pvFBer(h+Gj+ zy(muD#quoGGjf$UFlAX62HI3HU3RRTmNFe0ZCzTWOS9EV?OLkZR}++n{?WLCv;so` zebo0EET_whP^wtA4Kra$7Obl+YPYP1if!1gz*KPo85P~oXh2047O`DkFcq8el8R}} z2pnE43TrWyBVi0u&(N}tGpo&+=~B^{F`apB%$#>7%yh~5_U}&y~&oQiXU7rq*n=iB=MntyHV%(N>O#M%QY8VJeX;m)P(8$T5+}Da;9YU(lGmlbG+a#Emci-&U#P?bgbg>d=27N%!uvA<)9>VgY06L zG>{sV&xpY8T7v^Qy;O>KuKe0y2fu(V{CfI02RI41+bnyPYXCwH68=Vb(zptF8@&s0 zlN%qO0zvmu#ZK8J*ge!eH?hVQwb0)N$PcVE z6{)DB9UVOJAvzJI`2Zcp&_#25dE4Nl0w|K+IOaTW58-#8C!&vVJh}kCz%|5Uk8m*d zD+1RLiakW&212(GjNO5fjNV86WY=9ZEFk2=#0@khZ$WKt>{`H@I^3k&bvS^-45H4> zc^edZ+``*!^uL|5os>q(;b0cIItRoYpXUg9vy{L{qBrz2^M>^WztQXLw@3#7+{ouq(^MI35{@U?+jc67#YY z5-3V6#jzJhSs~(m8fb+~_1ykwTOGUCvW`91>9w9KguBC*LhOapbG#!S3&m45RQgRe zq^eF&zZrGw+C8!B9bcl~usf%=>&SVWjCgHlsF15R`dzEz^gdWl*XmNCV^!L=SJT6x z-O{`(UzjJD&j>)eroDE*`ObFjuJ*J``FKlvC;l8Oh2ne{3x?MPs|)TC)fgpe{?b8Re11U(?~M8=WYF5H$0AE4+gr|RM=lM=bSZTF#5kysLVTe{WrGx zo=iTn5PzK0-*HsNILLuzt{I-5GAhPs=?dzbUv`J9!8RbtcV}RQJ2NQ4D%N-sfpu1h zz!_thF(m?}y$gg&_Vo^#Y|J~1x%Y{%Kd{$yi&ebWD0!ZTVzUnde0HWr^BC!oLwQBiCrRI lqGt^0{3Q~ZWRM77C(>V-y@WA4!0yxlhd1wFi`pS<{{ + + + + + WebSocket Demo — Netty + + + +

+ + + + diff --git a/code/link/link-fronted/.vscode/extensions.json b/code/link/link-fronted/.vscode/extensions.json deleted file mode 100644 index a7cea0b0..00000000 --- a/code/link/link-fronted/.vscode/extensions.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "recommendations": ["Vue.volar"] -} diff --git a/code/link/link-fronted/README.md b/code/link/link-fronted/README.md deleted file mode 100644 index 1511959c..00000000 --- a/code/link/link-fronted/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Vue 3 + Vite - -This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 ` - - - - diff --git a/code/link/link-fronted/test.html b/code/link/link-fronted/test.html deleted file mode 100644 index 1fd81587..00000000 --- a/code/link/link-fronted/test.html +++ /dev/null @@ -1,329 +0,0 @@ - - - - - - 企业三栏聊天页面 - - - -
- - - - -
-
请选择聊天对象
-
- -
- -
-
企业A
-
欢迎使用企业聊天系统。
-
09:30
-
-
-
- -
-
我
-
收到,确认。
-
09:31
-
-
-
-
- - -
-
- - -
-

聊天详情

-
    -
  • 企业名称:企业A
  • -
  • 联系人:张三
  • -
  • 部门:技术部
  • -
  • 未读消息:2
  • -
  • 加入时间:2025-10-10
  • -
-
-
- - diff --git a/code/link/link-fronted/.gitignore b/code/link/link-frontend/.gitignore similarity index 86% rename from code/link/link-fronted/.gitignore rename to code/link/link-frontend/.gitignore index a547bf36..8b7e5021 100644 --- a/code/link/link-fronted/.gitignore +++ b/code/link/link-frontend/.gitignore @@ -13,8 +13,6 @@ dist-ssr *.local # Editor directories and files -.vscode/* -!.vscode/extensions.json .idea .DS_Store *.suo diff --git a/code/link/link-frontend/.vscode/settings.json b/code/link/link-frontend/.vscode/settings.json new file mode 100644 index 00000000..4472e953 --- /dev/null +++ b/code/link/link-frontend/.vscode/settings.json @@ -0,0 +1,51 @@ +{ + "editor.cursorSmoothCaretAnimation": "on", + // Disable the default formatter, use eslint instead + "prettier.enable": false, + "editor.formatOnSave": false, + + // Auto fix + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit", + "source.organizeImports": "never" + }, + + // Silent the stylistic rules in you IDE, but still auto fix them + "eslint.rules.customizations": [ + { "rule": "style/*", "severity": "off", "fixable": true }, + { "rule": "format/*", "severity": "off", "fixable": true }, + { "rule": "*-indent", "severity": "off", "fixable": true }, + { "rule": "*-spacing", "severity": "off", "fixable": true }, + { "rule": "*-spaces", "severity": "off", "fixable": true }, + { "rule": "*-order", "severity": "off", "fixable": true }, + { "rule": "*-dangle", "severity": "off", "fixable": true }, + { "rule": "*-newline", "severity": "off", "fixable": true }, + { "rule": "*quotes", "severity": "off", "fixable": true }, + { "rule": "*semi", "severity": "off", "fixable": true } + ], + + // Enable eslint for all supported languages + "eslint.validate": [ + "javascript", + "javascriptreact", + "typescript", + "typescriptreact", + "vue", + "html", + "markdown", + "json", + "jsonc", + "yaml", + "toml", + "xml", + "gql", + "graphql", + "astro", + "svelte", + "css", + "less", + "scss", + "pcss", + "postcss" + ] +} diff --git a/code/link/link-frontend/.vscode/vue3.code-snippets b/code/link/link-frontend/.vscode/vue3.code-snippets new file mode 100644 index 00000000..1265d5e2 --- /dev/null +++ b/code/link/link-frontend/.vscode/vue3.code-snippets @@ -0,0 +1,16 @@ +{ + "Print Vue3 SFC": { + "scope": "vue", + "prefix": "v3", + "body": [ + "\n", + "\n", + "\n", + ], + }, +} \ No newline at end of file diff --git a/code/link/link-fronted/eslint.config.js b/code/link/link-frontend/eslint.config.js similarity index 72% rename from code/link/link-fronted/eslint.config.js rename to code/link/link-frontend/eslint.config.js index 6517d221..f0ebbbef 100644 --- a/code/link/link-fronted/eslint.config.js +++ b/code/link/link-frontend/eslint.config.js @@ -16,7 +16,7 @@ export default antfu({ }, rules: { - 'no-console': 'off', // 允许 console.log 不报错 - "prefer-promise-reject-errors": "off" + 'no-console': 'off', + 'prefer-promise-reject-errors': 'off', }, }) diff --git a/code/link/link-fronted/index.html b/code/link/link-frontend/index.html similarity index 67% rename from code/link/link-fronted/index.html rename to code/link/link-frontend/index.html index 701238c1..341de46e 100644 --- a/code/link/link-fronted/index.html +++ b/code/link/link-frontend/index.html @@ -4,9 +4,10 @@ - link-fronted + Vite + Vue + - +
diff --git a/code/link/link-frontend/package-lock.json b/code/link/link-frontend/package-lock.json new file mode 100644 index 00000000..26a0cfe4 --- /dev/null +++ b/code/link/link-frontend/package-lock.json @@ -0,0 +1,4022 @@ +{ + "name": "frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "0.0.0", + "dependencies": { + "@element-plus/icons-vue": "^2.3.2", + "axios": "^1.9.0", + "dayjs": "^1.11.13", + "element-plus": "^2.10.7", + "eslint": "^8.57.1", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.2.1", + "eslint-plugin-vue": "^9.32.0", + "lodash-es": "^4.17.21", + "pinia": "^3.0.3", + "pinia-plugin-persistedstate": "^4.5.0", + "sortablejs": "^1.15.6", + "vue": "^3.5.13", + "vue-router": "^4.5.1", + "vue3-puzzle-vcode": "^1.1.7" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.2.3", + "autoprefixer": "^10.4.21", + "postcss": "^8.5.6", + "postcss-cli": "^11.0.1", + "vite": "^6.3.5" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.5", + "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.27.5.tgz", + "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.27.6", + "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.27.6.tgz", + "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@ctrl/tinycolor": { + "version": "3.6.1", + "resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", + "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/@element-plus/icons-vue": { + "version": "2.3.2", + "resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.3.2.tgz", + "integrity": "sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==", + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", + "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.25.5.tgz", + "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz", + "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.25.5.tgz", + "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz", + "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz", + "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz", + "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz", + "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz", + "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz", + "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz", + "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz", + "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz", + "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz", + "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz", + "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz", + "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", + "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", + "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz", + "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", + "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz", + "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz", + "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz", + "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz", + "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz", + "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmmirror.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.1", + "resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.7.1.tgz", + "integrity": "sha512-azI0DrjMMfIug/ExbBaeDVJXcY0a7EPvPjb2xAJPa4HeimBX+Z18HK8QQR3jb6356SnDDdxx+hinMLcJEDdOjw==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.1", + "resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.7.1.tgz", + "integrity": "sha512-cwsmW/zyw5ltYTUeeYJ60CnQuPqmGwuGVhG9w0PRaRKkAyi38BT5CKrpIbb+jtahSwUl04cWzSx9ZOIxeS6RsQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.1", + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.9", + "resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.9.tgz", + "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", + "license": "MIT" + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "license": "BSD-3-Clause" + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmmirror.com/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@popperjs/core": { + "name": "@sxzz/popperjs-es", + "version": "2.11.7", + "resolved": "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz", + "integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.42.0.tgz", + "integrity": "sha512-gldmAyS9hpj+H6LpRNlcjQWbuKUtb94lodB9uCz71Jm+7BxK1VIOo7y62tZZwxhA7j1ylv/yQz080L5WkS+LoQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.42.0.tgz", + "integrity": "sha512-bpRipfTgmGFdCZDFLRvIkSNO1/3RGS74aWkJJTFJBH7h3MRV4UijkaEUeOMbi9wxtxYmtAbVcnMtHTPBhLEkaw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.42.0.tgz", + "integrity": "sha512-JxHtA081izPBVCHLKnl6GEA0w3920mlJPLh89NojpU2GsBSB6ypu4erFg/Wx1qbpUbepn0jY4dVWMGZM8gplgA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.42.0.tgz", + "integrity": "sha512-rv5UZaWVIJTDMyQ3dCEK+m0SAn6G7H3PRc2AZmExvbDvtaDc+qXkei0knQWcI3+c9tEs7iL/4I4pTQoPbNL2SA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.42.0.tgz", + "integrity": "sha512-fJcN4uSGPWdpVmvLuMtALUFwCHgb2XiQjuECkHT3lWLZhSQ3MBQ9pq+WoWeJq2PrNxr9rPM1Qx+IjyGj8/c6zQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.42.0.tgz", + "integrity": "sha512-CziHfyzpp8hJpCVE/ZdTizw58gr+m7Y2Xq5VOuCSrZR++th2xWAz4Nqk52MoIIrV3JHtVBhbBsJcAxs6NammOQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.42.0.tgz", + "integrity": "sha512-UsQD5fyLWm2Fe5CDM7VPYAo+UC7+2Px4Y+N3AcPh/LdZu23YcuGPegQly++XEVaC8XUTFVPscl5y5Cl1twEI4A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.42.0.tgz", + "integrity": "sha512-/i8NIrlgc/+4n1lnoWl1zgH7Uo0XK5xK3EDqVTf38KvyYgCU/Rm04+o1VvvzJZnVS5/cWSd07owkzcVasgfIkQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.42.0.tgz", + "integrity": "sha512-eoujJFOvoIBjZEi9hJnXAbWg+Vo1Ov8n/0IKZZcPZ7JhBzxh2A+2NFyeMZIRkY9iwBvSjloKgcvnjTbGKHE44Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.42.0.tgz", + "integrity": "sha512-/3NrcOWFSR7RQUQIuZQChLND36aTU9IYE4j+TB40VU78S+RA0IiqHR30oSh6P1S9f9/wVOenHQnacs/Byb824g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.42.0.tgz", + "integrity": "sha512-O8AplvIeavK5ABmZlKBq9/STdZlnQo7Sle0LLhVA7QT+CiGpNVe197/t8Aph9bhJqbDVGCHpY2i7QyfEDDStDg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.42.0.tgz", + "integrity": "sha512-6Qb66tbKVN7VyQrekhEzbHRxXXFFD8QKiFAwX5v9Xt6FiJ3BnCVBuyBxa2fkFGqxOCSGGYNejxd8ht+q5SnmtA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.42.0.tgz", + "integrity": "sha512-KQETDSEBamQFvg/d8jajtRwLNBlGc3aKpaGiP/LvEbnmVUKlFta1vqJqTrvPtsYsfbE/DLg5CC9zyXRX3fnBiA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.42.0.tgz", + "integrity": "sha512-qMvnyjcU37sCo/tuC+JqeDKSuukGAd+pVlRl/oyDbkvPJ3awk6G6ua7tyum02O3lI+fio+eM5wsVd66X0jQtxw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.42.0.tgz", + "integrity": "sha512-I2Y1ZUgTgU2RLddUHXTIgyrdOwljjkmcZ/VilvaEumtS3Fkuhbw4p4hgHc39Ypwvo2o7sBFNl2MquNvGCa55Iw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.42.0.tgz", + "integrity": "sha512-Gfm6cV6mj3hCUY8TqWa63DB8Mx3NADoFwiJrMpoZ1uESbK8FQV3LXkhfry+8bOniq9pqY1OdsjFWNsSbfjPugw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.42.0.tgz", + "integrity": "sha512-g86PF8YZ9GRqkdi0VoGlcDUb4rYtQKyTD1IVtxxN4Hpe7YqLBShA7oHMKU6oKTCi3uxwW4VkIGnOaH/El8de3w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.42.0.tgz", + "integrity": "sha512-+axkdyDGSp6hjyzQ5m1pgcvQScfHnMCcsXkx8pTgy/6qBmWVhtRVlgxjWwDp67wEXXUr0x+vD6tp5W4x6V7u1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.42.0.tgz", + "integrity": "sha512-F+5J9pelstXKwRSDq92J0TEBXn2nfUrQGg+HK1+Tk7VOL09e0gBqUHugZv7SW4MGrYj41oNCUe3IKCDGVlis2g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.42.0.tgz", + "integrity": "sha512-LpHiJRwkaVz/LqjHjK8LCi8osq7elmpwujwbXKNW88bM8eeGxavJIKKjkjpMHAh/2xfnrt1ZSnhTv41WYUHYmA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/lodash": { + "version": "4.17.17", + "resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.17.tgz", + "integrity": "sha512-RRVJ+J3J+WmyOTqnz3PiBLA501eKwXl2noseKOrNo/6+XEHjTAxO4xHvxQB6QuNm+s4WRbn6rSiap8+EA+ykFQ==", + "license": "MIT" + }, + "node_modules/@types/lodash-es": { + "version": "4.17.12", + "resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz", + "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", + "license": "MIT", + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.16", + "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz", + "integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==", + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "license": "ISC" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.4", + "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.16", + "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.16.tgz", + "integrity": "sha512-AOQS2eaQOaaZQoL1u+2rCJIKDruNXVBZSiUD3chnUrsoX5ZTQMaCvXlWNIfxBJuU15r1o7+mpo5223KVtIhAgQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.2", + "@vue/shared": "3.5.16", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.16", + "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.16.tgz", + "integrity": "sha512-SSJIhBr/teipXiXjmWOVWLnxjNGo65Oj/8wTEQz0nqwQeP75jWZ0n4sF24Zxoht1cuJoWopwj0J0exYwCJ0dCQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.16", + "@vue/shared": "3.5.16" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.16", + "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.16.tgz", + "integrity": "sha512-rQR6VSFNpiinDy/DVUE0vHoIDUF++6p910cgcZoaAUm3POxgNOOdS/xgoll3rNdKYTYPnnbARDCZOyZ+QSe6Pw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.2", + "@vue/compiler-core": "3.5.16", + "@vue/compiler-dom": "3.5.16", + "@vue/compiler-ssr": "3.5.16", + "@vue/shared": "3.5.16", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.17", + "postcss": "^8.5.3", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.16", + "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.16.tgz", + "integrity": "sha512-d2V7kfxbdsjrDSGlJE7my1ZzCXViEcqN6w14DOsDrUCHEA6vbnVCpRFfrc4ryCP/lCKzX2eS1YtnLE/BuC9f/A==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.16", + "@vue/shared": "3.5.16" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/@vue/devtools-kit": { + "version": "7.7.7", + "resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-7.7.7.tgz", + "integrity": "sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==", + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^7.7.7", + "birpc": "^2.3.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.7.7", + "resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-7.7.7.tgz", + "integrity": "sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==", + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.16", + "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.16.tgz", + "integrity": "sha512-FG5Q5ee/kxhIm1p2bykPpPwqiUBV3kFySsHEQha5BJvjXdZTUfmya7wP7zC39dFuZAcf/PD5S4Lni55vGLMhvA==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.16" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.16", + "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.16.tgz", + "integrity": "sha512-bw5Ykq6+JFHYxrQa7Tjr+VSzw7Dj4ldR/udyBZbq73fCdJmyy5MPIFR9IX/M5Qs+TtTjuyUTCnmK3lWWwpAcFQ==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.16", + "@vue/shared": "3.5.16" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.16", + "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.16.tgz", + "integrity": "sha512-T1qqYJsG2xMGhImRUV9y/RseB9d0eCYZQ4CWca9ztCuiPj/XWNNN+lkNBuzVbia5z4/cgxdL28NoQCvC0Xcfww==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.16", + "@vue/runtime-core": "3.5.16", + "@vue/shared": "3.5.16", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.16", + "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.16.tgz", + "integrity": "sha512-BrX0qLiv/WugguGsnQUJiYOE0Fe5mZTwi6b7X/ybGB0vfrPH9z0gD/Y6WOR1sGCgX4gc25L1RYS5eYQKDMoNIg==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.16", + "@vue/shared": "3.5.16" + }, + "peerDependencies": { + "vue": "3.5.16" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.16", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.16.tgz", + "integrity": "sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg==", + "license": "MIT" + }, + "node_modules/@vueuse/core": { + "version": "9.13.0", + "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-9.13.0.tgz", + "integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==", + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.16", + "@vueuse/metadata": "9.13.0", + "@vueuse/shared": "9.13.0", + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/core/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@vueuse/metadata": { + "version": "9.13.0", + "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.13.0.tgz", + "integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "9.13.0", + "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.13.0.tgz", + "integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==", + "license": "MIT", + "dependencies": { + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/async-validator": { + "version": "4.2.5", + "resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz", + "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==", + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/autoprefixer": { + "version": "10.4.21", + "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/axios": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/axios/-/axios-1.9.0.tgz", + "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/birpc": { + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/birpc/-/birpc-2.5.0.tgz", + "integrity": "sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.25.3", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.25.3.tgz", + "integrity": "sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001735", + "electron-to-chromium": "^1.5.204", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001737", + "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001737.tgz", + "integrity": "sha512-BiloLiXtQNrY5UyF0+1nSJLXUENuhka2pzy2Fx5pGxqavdrxSCW4U6Pn/PoG3Efspi2frRbHpBV2XsrPE6EDlw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmmirror.com/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/copy-anything": { + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-3.0.5.tgz", + "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==", + "license": "MIT", + "dependencies": { + "is-what": "^4.1.8" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==" + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "license": "MIT" + }, + "node_modules/deep-pick-omit": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/deep-pick-omit/-/deep-pick-omit-1.2.1.tgz", + "integrity": "sha512-2J6Kc/m3irCeqVG42T+SaUMesaK7oGWaedGnQQK/+O0gYc+2SP5bKh/KKTE7d7SJ+GCA9UUE1GRzh6oDe0EnGw==" + }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmmirror.com/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dependency-graph": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/dependency-graph/-/dependency-graph-1.0.0.tgz", + "integrity": "sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==" + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.209", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.209.tgz", + "integrity": "sha512-Xoz0uMrim9ZETCQt8UgM5FxQF9+imA7PBpokoGcZloA1uw2LeHzTlip5cb5KOAsXZLjh/moN2vReN3ZjJmjI9A==", + "dev": true, + "license": "ISC" + }, + "node_modules/element-plus": { + "version": "2.10.7", + "resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.10.7.tgz", + "integrity": "sha512-bL4yhepL8/0NEQA5+N2Q6ZVKLipIDkiQjK2mqtSmGh6CxJk1yaBMdG5HXfYkbk1htNcT3ULk9g23lzT323JGcA==", + "dependencies": { + "@ctrl/tinycolor": "^3.4.1", + "@element-plus/icons-vue": "^2.3.1", + "@floating-ui/dom": "^1.0.1", + "@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7", + "@types/lodash": "^4.14.182", + "@types/lodash-es": "^4.17.6", + "@vueuse/core": "^9.1.0", + "async-validator": "^4.2.5", + "dayjs": "^1.11.13", + "escape-html": "^1.0.3", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "lodash-unified": "^1.0.2", + "memoize-one": "^6.0.0", + "normalize-wheel-es": "^1.2.0" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.5", + "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.5.tgz", + "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.5", + "@esbuild/android-arm": "0.25.5", + "@esbuild/android-arm64": "0.25.5", + "@esbuild/android-x64": "0.25.5", + "@esbuild/darwin-arm64": "0.25.5", + "@esbuild/darwin-x64": "0.25.5", + "@esbuild/freebsd-arm64": "0.25.5", + "@esbuild/freebsd-x64": "0.25.5", + "@esbuild/linux-arm": "0.25.5", + "@esbuild/linux-arm64": "0.25.5", + "@esbuild/linux-ia32": "0.25.5", + "@esbuild/linux-loong64": "0.25.5", + "@esbuild/linux-mips64el": "0.25.5", + "@esbuild/linux-ppc64": "0.25.5", + "@esbuild/linux-riscv64": "0.25.5", + "@esbuild/linux-s390x": "0.25.5", + "@esbuild/linux-x64": "0.25.5", + "@esbuild/netbsd-arm64": "0.25.5", + "@esbuild/netbsd-x64": "0.25.5", + "@esbuild/openbsd-arm64": "0.25.5", + "@esbuild/openbsd-x64": "0.25.5", + "@esbuild/sunos-x64": "0.25.5", + "@esbuild/win32-arm64": "0.25.5", + "@esbuild/win32-ia32": "0.25.5", + "@esbuild/win32-x64": "0.25.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmmirror.com/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.2", + "resolved": "https://registry.npmmirror.com/eslint-config-prettier/-/eslint-config-prettier-9.1.2.tgz", + "integrity": "sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==", + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.5.4", + "resolved": "https://registry.npmmirror.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz", + "integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==", + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.11.7" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-vue": { + "version": "9.33.0", + "resolved": "https://registry.npmmirror.com/eslint-plugin-vue/-/eslint-plugin-vue-9.33.0.tgz", + "integrity": "sha512-174lJKuNsuDIlLpjeXc5E2Tss8P44uIimAfGD0b90k0NoirJqpG7stLuU9Vp/9ioTOrQdWVREc4mRd1BD+CvGw==", + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "globals": "^13.24.0", + "natural-compare": "^1.4.0", + "nth-check": "^2.1.1", + "postcss-selector-parser": "^6.0.15", + "semver": "^7.6.3", + "vue-eslint-parser": "^9.4.3", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmmirror.com/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "license": "Apache-2.0" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.4.5", + "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.4.5.tgz", + "integrity": "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmmirror.com/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.3.tgz", + "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fs-extra": { + "version": "11.3.1", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-11.3.1.tgz", + "integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmmirror.com/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "license": "MIT" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-what": { + "version": "4.1.16", + "resolved": "https://registry.npmmirror.com/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "license": "MIT", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, + "node_modules/lodash-unified": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/lodash-unified/-/lodash-unified-1.0.3.tgz", + "integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==", + "license": "MIT", + "peerDependencies": { + "@types/lodash-es": "*", + "lodash": "*", + "lodash-es": "*" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", + "license": "MIT" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-wheel-es": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz", + "integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==", + "license": "BSD-3-Clause" + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinia": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/pinia/-/pinia-3.0.3.tgz", + "integrity": "sha512-ttXO/InUULUXkMHpTdp9Fj4hLpD/2AoJdmAbAeW2yu1iy1k+pkFekQXw5VpC0/5p51IOR/jDaDRfRWRnMMsGOA==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^7.7.2" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "typescript": ">=4.4.4", + "vue": "^2.7.0 || ^3.5.11" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/pinia-plugin-persistedstate": { + "version": "4.5.0", + "resolved": "https://registry.npmmirror.com/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-4.5.0.tgz", + "integrity": "sha512-QTkP1xJVyCdr2I2p3AKUZM84/e+IS+HktRxKGAIuDzkyaKKV48mQcYkJFVVDuvTxlI5j6X3oZObpqoVB8JnWpw==", + "dependencies": { + "deep-pick-omit": "^1.2.1", + "defu": "^6.1.4", + "destr": "^2.0.5" + }, + "peerDependencies": { + "@nuxt/kit": ">=3.0.0", + "@pinia/nuxt": ">=0.10.0", + "pinia": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@nuxt/kit": { + "optional": true + }, + "@pinia/nuxt": { + "optional": true + }, + "pinia": { + "optional": true + } + } + }, + "node_modules/pinia/node_modules/@vue/devtools-api": { + "version": "7.7.7", + "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-7.7.7.tgz", + "integrity": "sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.7" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-cli": { + "version": "11.0.1", + "resolved": "https://registry.npmmirror.com/postcss-cli/-/postcss-cli-11.0.1.tgz", + "integrity": "sha512-0UnkNPSayHKRe/tc2YGW6XnSqqOA9eqpiRMgRlV1S6HdGi16vwJBx7lviARzbV1HpQHqLLRH3o8vTcB0cLc+5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.3.0", + "dependency-graph": "^1.0.0", + "fs-extra": "^11.0.0", + "picocolors": "^1.0.0", + "postcss-load-config": "^5.0.0", + "postcss-reporter": "^7.0.0", + "pretty-hrtime": "^1.0.3", + "read-cache": "^1.0.0", + "slash": "^5.0.0", + "tinyglobby": "^0.2.12", + "yargs": "^17.0.0" + }, + "bin": { + "postcss": "index.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-load-config": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/postcss-load-config/-/postcss-load-config-5.1.0.tgz", + "integrity": "sha512-G5AJ+IX0aD0dygOE0yFZQ/huFFMSNneyfp0e3/bT05a8OfPC5FUoZRPfGijUdGOJNMewJiwzcHJXFafFzeKFVA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1", + "yaml": "^2.4.2" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + } + } + }, + "node_modules/postcss-reporter": { + "version": "7.1.0", + "resolved": "https://registry.npmmirror.com/postcss-reporter/-/postcss-reporter-7.1.0.tgz", + "integrity": "sha512-/eoEylGWyy6/DOiMP5lmFRdmDKThqgn7D6hP2dXKJI/0rJSO1ADFNngZfDzxL0YAxFvws+Rtpuji1YIHj4mySA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "picocolors": "^1.0.0", + "thenby": "^1.3.4" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmmirror.com/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "license": "MIT", + "peer": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.42.0", + "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.42.0.tgz", + "integrity": "sha512-LW+Vse3BJPyGJGAJt1j8pWDKPd73QM8cRXYK1IxOBgL2AGLu7Xd2YOW0M2sLUBCkF5MshXXtMApyEAEzMVMsnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.7" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.42.0", + "@rollup/rollup-android-arm64": "4.42.0", + "@rollup/rollup-darwin-arm64": "4.42.0", + "@rollup/rollup-darwin-x64": "4.42.0", + "@rollup/rollup-freebsd-arm64": "4.42.0", + "@rollup/rollup-freebsd-x64": "4.42.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.42.0", + "@rollup/rollup-linux-arm-musleabihf": "4.42.0", + "@rollup/rollup-linux-arm64-gnu": "4.42.0", + "@rollup/rollup-linux-arm64-musl": "4.42.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.42.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.42.0", + "@rollup/rollup-linux-riscv64-gnu": "4.42.0", + "@rollup/rollup-linux-riscv64-musl": "4.42.0", + "@rollup/rollup-linux-s390x-gnu": "4.42.0", + "@rollup/rollup-linux-x64-gnu": "4.42.0", + "@rollup/rollup-linux-x64-musl": "4.42.0", + "@rollup/rollup-win32-arm64-msvc": "4.42.0", + "@rollup/rollup-win32-ia32-msvc": "4.42.0", + "@rollup/rollup-win32-x64-msvc": "4.42.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sortablejs": { + "version": "1.15.6", + "resolved": "https://registry.npmmirror.com/sortablejs/-/sortablejs-1.15.6.tgz", + "integrity": "sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A==" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmmirror.com/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/superjson": { + "version": "2.2.2", + "resolved": "https://registry.npmmirror.com/superjson/-/superjson-2.2.2.tgz", + "integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==", + "license": "MIT", + "dependencies": { + "copy-anything": "^3.0.2" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/synckit": { + "version": "0.11.11", + "resolved": "https://registry.npmmirror.com/synckit/-/synckit-0.11.11.tgz", + "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.2.9" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "license": "MIT" + }, + "node_modules/thenby": { + "version": "1.3.4", + "resolved": "https://registry.npmmirror.com/thenby/-/thenby-1.3.4.tgz", + "integrity": "sha512-89Gi5raiWA3QZ4b2ePcEwswC3me9JIg+ToSgtE0JWeCynLnLxNr/f9G+xfo9K+Oj4AFdom8YNJjibIARTJmapQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/vite": { + "version": "6.3.5", + "resolved": "https://registry.npmmirror.com/vite/-/vite-6.3.5.tgz", + "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.5.16", + "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.16.tgz", + "integrity": "sha512-rjOV2ecxMd5SiAmof2xzh2WxntRcigkX/He4YFJ6WdRvVUrbt6DxC1Iujh10XLl8xCDRDtGKMeO3D+pRQ1PP9w==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.16", + "@vue/compiler-sfc": "3.5.16", + "@vue/runtime-dom": "3.5.16", + "@vue/server-renderer": "3.5.16", + "@vue/shared": "3.5.16" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-eslint-parser": { + "version": "9.4.3", + "resolved": "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz", + "integrity": "sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "eslint-scope": "^7.1.1", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", + "lodash": "^4.17.21", + "semver": "^7.3.6" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/vue-router": { + "version": "4.5.1", + "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.5.1.tgz", + "integrity": "sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/vue3-puzzle-vcode": { + "version": "1.1.7", + "resolved": "https://registry.npmmirror.com/vue3-puzzle-vcode/-/vue3-puzzle-vcode-1.1.7.tgz", + "integrity": "sha512-mW780dz7HKjrElnE60CeYSeHGidKBKHoMjTDYfqF21330rTkFOsfDK1FQKZ22MktgMtTEoS/imfpEDlM1cxY/g==" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "license": "Apache-2.0", + "engines": { + "node": ">=12" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yaml": { + "version": "2.8.1", + "resolved": "https://registry.npmmirror.com/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmmirror.com/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/code/link/link-frontend/package.json b/code/link/link-frontend/package.json new file mode 100644 index 00000000..d6c29685 --- /dev/null +++ b/code/link/link-frontend/package.json @@ -0,0 +1,36 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@element-plus/icons-vue": "^2.3.2", + "@iconify/vue": "^5.0.0", + "axios": "^1.9.0", + "dayjs": "^1.11.13", + "element-plus": "^2.10.7", + "lodash-es": "^4.17.21", + "pinia": "^3.0.3", + "pinia-plugin-persistedstate": "^4.5.0", + "sortablejs": "^1.15.6", + "vue": "^3.5.13", + "vue-router": "^4.5.1", + "vue3-puzzle-vcode": "^1.1.7" + }, + "devDependencies": { + "@antfu/eslint-config": "^5.2.1", + "@vitejs/plugin-vue": "^5.2.3", + "autoprefixer": "^10.4.21", + "eslint": "^9.34.0", + "eslint-plugin-format": "^1.0.1", + "postcss": "^8.5.6", + "postcss-cli": "^11.0.1", + "sass": "^1.93.2", + "vite": "^6.3.5" + } +} diff --git a/code/link/link-fronted/pnpm-lock.yaml b/code/link/link-frontend/pnpm-lock.yaml similarity index 77% rename from code/link/link-fronted/pnpm-lock.yaml rename to code/link/link-frontend/pnpm-lock.yaml index 437deba3..ee2e38d1 100644 --- a/code/link/link-fronted/pnpm-lock.yaml +++ b/code/link/link-frontend/pnpm-lock.yaml @@ -8,28 +8,70 @@ importers: .: dependencies: + '@element-plus/icons-vue': + specifier: ^2.3.2 + version: 2.3.2(vue@3.5.22(typescript@5.9.3)) + '@iconify/vue': + specifier: ^5.0.0 + version: 5.0.0(vue@3.5.22(typescript@5.9.3)) + axios: + specifier: ^1.9.0 + version: 1.12.2 + dayjs: + specifier: ^1.11.13 + version: 1.11.18 + element-plus: + specifier: ^2.10.7 + version: 2.11.4(vue@3.5.22(typescript@5.9.3)) + lodash-es: + specifier: ^4.17.21 + version: 4.17.21 + pinia: + specifier: ^3.0.3 + version: 3.0.3(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3)) + pinia-plugin-persistedstate: + specifier: ^4.5.0 + version: 4.5.0(pinia@3.0.3(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3))) + sortablejs: + specifier: ^1.15.6 + version: 1.15.6 vue: - specifier: ^3.5.22 + specifier: ^3.5.13 version: 3.5.22(typescript@5.9.3) vue-router: specifier: ^4.5.1 version: 4.5.1(vue@3.5.22(typescript@5.9.3)) + vue3-puzzle-vcode: + specifier: ^1.1.7 + version: 1.1.7 devDependencies: '@antfu/eslint-config': specifier: ^5.2.1 version: 5.4.1(@vue/compiler-sfc@3.5.22)(eslint-plugin-format@1.0.2(eslint@9.37.0))(eslint@9.37.0)(typescript@5.9.3) '@vitejs/plugin-vue': - specifier: ^6.0.1 - version: 6.0.1(vite@7.1.9(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)) + specifier: ^5.2.3 + version: 5.2.4(vite@6.3.6(sass@1.93.2)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)) + autoprefixer: + specifier: ^10.4.21 + version: 10.4.21(postcss@8.5.6) eslint: specifier: ^9.34.0 version: 9.37.0 eslint-plugin-format: specifier: ^1.0.1 version: 1.0.2(eslint@9.37.0) + postcss: + specifier: ^8.5.6 + version: 8.5.6 + postcss-cli: + specifier: ^11.0.1 + version: 11.0.1(postcss@8.5.6) + sass: + specifier: ^1.93.2 + version: 1.93.2 vite: - specifier: ^7.1.7 - version: 7.1.9(yaml@2.8.1) + specifier: ^6.3.5 + version: 6.3.6(sass@1.93.2)(yaml@2.8.1) packages: @@ -114,6 +156,10 @@ packages: '@clack/prompts@0.11.0': resolution: {integrity: sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw==} + '@ctrl/tinycolor@3.6.1': + resolution: {integrity: sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==} + engines: {node: '>=10'} + '@dprint/formatter@0.3.0': resolution: {integrity: sha512-N9fxCxbaBOrDkteSOzaCqwWjso5iAe+WJPsHC021JfHNj2ThInPNEF13ORDKta3llq5D1TlclODCvOvipH7bWQ==} @@ -123,6 +169,11 @@ packages: '@dprint/toml@0.6.4': resolution: {integrity: sha512-bZXIUjxr0LIuHWshZr/5mtUkOrnh0NKVZEF6ACojW5z7zkJu7s9sV2mMXm8XQDqN4cJzdHYUYzUyEGdfciaLJA==} + '@element-plus/icons-vue@2.3.2': + resolution: {integrity: sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==} + peerDependencies: + vue: ^3.2.0 + '@es-joy/jsdoccomment@0.50.2': resolution: {integrity: sha512-YAdE/IJSpwbOTiaURNCKECdAwqrJuFiZhylmesBcIRawtYKnBR2wxPhoIewMg+Yu+QuYvHfJNReWpoxGBKOChA==} engines: {node: '>=18'} @@ -352,6 +403,15 @@ packages: resolution: {integrity: sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@floating-ui/core@1.7.3': + resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} + + '@floating-ui/dom@1.7.4': + resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} + + '@floating-ui/utils@0.2.10': + resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -368,6 +428,14 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} + '@iconify/types@2.0.0': + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + + '@iconify/vue@5.0.0': + resolution: {integrity: sha512-C+KuEWIF5nSBrobFJhT//JS87OZ++QDORB6f2q2Wm6fl2mueSTpFBeBsveK0KW9hWiZ4mNiPjsh6Zs4jjdROSg==} + peerDependencies: + vue: '>=3' + '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} @@ -383,6 +451,94 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@parcel/watcher-android-arm64@2.5.1': + resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.5.1': + resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.5.1': + resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.5.1': + resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.5.1': + resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-arm-musl@2.5.1': + resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + libc: [musl] + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-arm64-musl@2.5.1': + resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@parcel/watcher-linux-x64-glibc@2.5.1': + resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-x64-musl@2.5.1': + resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@parcel/watcher-win32-arm64@2.5.1': + resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-ia32@2.5.1': + resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] + + '@parcel/watcher-win32-x64@2.5.1': + resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.5.1': + resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} + engines: {node: '>= 10.0.0'} + '@pkgr/core@0.1.2': resolution: {integrity: sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} @@ -391,9 +547,6 @@ packages: resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - '@rolldown/pluginutils@1.0.0-beta.29': - resolution: {integrity: sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==} - '@rollup/rollup-android-arm-eabi@4.52.4': resolution: {integrity: sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==} cpu: [arm] @@ -428,56 +581,67 @@ packages: resolution: {integrity: sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.52.4': resolution: {integrity: sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.52.4': resolution: {integrity: sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.52.4': resolution: {integrity: sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.52.4': resolution: {integrity: sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-gnu@4.52.4': resolution: {integrity: sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.52.4': resolution: {integrity: sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.52.4': resolution: {integrity: sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.52.4': resolution: {integrity: sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.52.4': resolution: {integrity: sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.52.4': resolution: {integrity: sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-openharmony-arm64@4.52.4': resolution: {integrity: sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==} @@ -510,6 +674,9 @@ packages: peerDependencies: eslint: '>=9.0.0' + '@sxzz/popperjs-es@2.11.7': + resolution: {integrity: sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==} + '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} @@ -519,6 +686,12 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/lodash-es@4.17.12': + resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} + + '@types/lodash@4.17.20': + resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==} + '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} @@ -528,6 +701,9 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@types/web-bluetooth@0.0.16': + resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==} + '@typescript-eslint/eslint-plugin@8.46.0': resolution: {integrity: sha512-hA8gxBq4ukonVXPy0OKhiaUh/68D0E88GSmtC1iAEnGaieuDi38LhS7jdCHRLi6ErJBNDGCzvh5EnzdPwUc0DA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -587,11 +763,11 @@ packages: resolution: {integrity: sha512-FrvMpAK+hTbFy7vH5j1+tMYHMSKLE6RzluFJlkFNKD0p9YsUT75JlBSmr5so3QRzvMwU5/bIEdeNrxm8du8l3Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@vitejs/plugin-vue@6.0.1': - resolution: {integrity: sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==} - engines: {node: ^20.19.0 || >=22.12.0} + '@vitejs/plugin-vue@5.2.4': + resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==} + engines: {node: ^18.0.0 || >=20.0.0} peerDependencies: - vite: ^5.0.0 || ^6.0.0 || ^7.0.0 + vite: ^5.0.0 || ^6.0.0 vue: ^3.2.25 '@vitest/eslint-plugin@1.3.16': @@ -621,6 +797,15 @@ packages: '@vue/devtools-api@6.6.4': resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} + '@vue/devtools-api@7.7.7': + resolution: {integrity: sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==} + + '@vue/devtools-kit@7.7.7': + resolution: {integrity: sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==} + + '@vue/devtools-shared@7.7.7': + resolution: {integrity: sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==} + '@vue/reactivity@3.5.22': resolution: {integrity: sha512-f2Wux4v/Z2pqc9+4SmgZC1p73Z53fyD90NFWXiX9AKVnVBEvLFOWCEgJD3GdGnlxPZt01PSlfmLqbLYzY/Fw4A==} @@ -638,6 +823,15 @@ packages: '@vue/shared@3.5.22': resolution: {integrity: sha512-F4yc6palwq3TT0u+FYf0Ns4Tfl9GRFURDN2gWG7L1ecIaS/4fCIuFOjMTnCyjsu/OK6vaDKLCrGAa+KvvH+h4w==} + '@vueuse/core@9.13.0': + resolution: {integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==} + + '@vueuse/metadata@9.13.0': + resolution: {integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==} + + '@vueuse/shared@9.13.0': + resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -651,6 +845,10 @@ packages: ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -659,6 +857,10 @@ packages: resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} engines: {node: '>=14'} + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + are-docs-informative@0.0.2: resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==} engines: {node: '>=14'} @@ -666,6 +868,22 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + async-validator@4.2.5: + resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + autoprefixer@10.4.21: + resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + axios@1.12.2: + resolution: {integrity: sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -673,6 +891,13 @@ packages: resolution: {integrity: sha512-qsJ8/X+UypqxHXN75M7dF88jNK37dLBRW7LeUzCPz+TNs37G8cfWy9nWzS+LS//g600zrt2le9KuXt0rWfDz5Q==} hasBin: true + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + birpc@2.6.1: + resolution: {integrity: sha512-LPnFhlDpdSH6FJhJyn4M0kFO7vtQ5iPw24FnG0y21q09xC7e8+1LeR31S1MAIrDAHp4m7aas4bEkTDTvMAtebQ==} + boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -699,6 +924,10 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -719,6 +948,14 @@ packages: character-entities@2.0.2: resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + ci-info@4.3.1: resolution: {integrity: sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==} engines: {node: '>=8'} @@ -727,6 +964,10 @@ packages: resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==} engines: {node: '>=4'} + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -734,6 +975,10 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + comment-parser@1.4.1: resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==} engines: {node: '>= 12.0.0'} @@ -747,6 +992,10 @@ packages: confbox@0.2.2: resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} + copy-anything@3.0.5: + resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==} + engines: {node: '>=12.13'} + core-js-compat@3.46.0: resolution: {integrity: sha512-p9hObIIEENxSV8xIu+V68JjSeARg6UVMG5mR+JEUguG3sI6MsiS1njz2jHmyJDvA+8jX/sytkBHup6kxhM9law==} @@ -762,6 +1011,9 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + dayjs@1.11.18: + resolution: {integrity: sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==} + debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -777,10 +1029,32 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + deep-pick-omit@1.2.1: + resolution: {integrity: sha512-2J6Kc/m3irCeqVG42T+SaUMesaK7oGWaedGnQQK/+O0gYc+2SP5bKh/KKTE7d7SJ+GCA9UUE1GRzh6oDe0EnGw==} + + defu@6.1.4: + resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + dependency-graph@1.0.0: + resolution: {integrity: sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg==} + engines: {node: '>=4'} + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + destr@2.0.5: + resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} + + detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} @@ -788,9 +1062,21 @@ packages: resolution: {integrity: sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + electron-to-chromium@1.5.234: resolution: {integrity: sha512-RXfEp2x+VRYn8jbKfQlRImzoJU01kyDvVPBmG39eU2iuRVhuS6vQNocB8J0/8GrIMLnPzgz4eW6WiRnJkTuNWg==} + element-plus@2.11.4: + resolution: {integrity: sha512-sLq+Ypd0cIVilv8wGGMEGvzRVBBsRpJjnAS5PsI/1JU1COZXqzH3N1UYMUc/HCdvdjf6dfrBy80Sj7KcACsT7w==} + peerDependencies: + vue: ^3.2.0 + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + empathic@2.0.0: resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==} engines: {node: '>=14'} @@ -803,6 +1089,22 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + esbuild@0.25.10: resolution: {integrity: sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==} engines: {node: '>=18'} @@ -812,6 +1114,9 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} @@ -1091,15 +1396,50 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data@4.0.4: + resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} + engines: {node: '>= 6'} + format@0.2.2: resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} engines: {node: '>=0.4.x'} + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + + fs-extra@11.3.2: + resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==} + engines: {node: '>=14.14'} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + get-tsconfig@4.12.0: resolution: {integrity: sha512-LScr2aNr2FbjAjZh2C6X6BxRx1/x+aTDExct/xyq2XKbYOiG5c0aK7pMsSuyc0brz3ibr/lbQiHD9jzt4lccJw==} @@ -1129,6 +1469,10 @@ packages: globrex@0.1.2: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -1139,6 +1483,21 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hookable@5.5.3: + resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -1147,6 +1506,9 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} + immutable@5.1.3: + resolution: {integrity: sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==} + import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -1159,6 +1521,10 @@ packages: resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} engines: {node: '>=12'} + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + is-builtin-module@5.0.0: resolution: {integrity: sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==} engines: {node: '>=18.20'} @@ -1167,6 +1533,10 @@ packages: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -1175,6 +1545,10 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-what@4.1.16: + resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} + engines: {node: '>=12.13'} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -1217,6 +1591,9 @@ packages: resolution: {integrity: sha512-uuPNLJkKN8NXAlZlQ6kmUF9qO+T6Kyd7oV4+/7yy8Jz6+MZNyhPq8EdLpdfnPVzUC8qSf1b4j1azKaGnFsjmsw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + jsonfile@6.2.0: + resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -1224,6 +1601,10 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + local-pkg@1.1.2: resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==} engines: {node: '>=14'} @@ -1232,6 +1613,16 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash-es@4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + + lodash-unified@1.0.3: + resolution: {integrity: sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==} + peerDependencies: + '@types/lodash-es': '*' + lodash: '*' + lodash-es: '*' + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -1247,6 +1638,10 @@ packages: markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + mdast-util-find-and-replace@3.0.2: resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} @@ -1283,6 +1678,9 @@ packages: mdast-util-to-string@4.0.0: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + memoize-one@6.0.0: + resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -1378,6 +1776,14 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -1385,6 +1791,9 @@ packages: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} + mitt@3.0.1: + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + mlly@1.8.0: resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} @@ -1403,9 +1812,23 @@ packages: resolution: {integrity: sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg==} engines: {node: '>=18'} + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + node-releases@2.0.23: resolution: {integrity: sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==} + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + + normalize-wheel-es@1.2.0: + resolution: {integrity: sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==} + nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} @@ -1452,6 +1875,9 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + perfect-debounce@1.0.0: + resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1463,6 +1889,33 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + pinia-plugin-persistedstate@4.5.0: + resolution: {integrity: sha512-QTkP1xJVyCdr2I2p3AKUZM84/e+IS+HktRxKGAIuDzkyaKKV48mQcYkJFVVDuvTxlI5j6X3oZObpqoVB8JnWpw==} + peerDependencies: + '@nuxt/kit': '>=3.0.0' + '@pinia/nuxt': '>=0.10.0' + pinia: '>=3.0.0' + peerDependenciesMeta: + '@nuxt/kit': + optional: true + '@pinia/nuxt': + optional: true + pinia: + optional: true + + pinia@3.0.3: + resolution: {integrity: sha512-ttXO/InUULUXkMHpTdp9Fj4hLpD/2AoJdmAbAeW2yu1iy1k+pkFekQXw5VpC0/5p51IOR/jDaDRfRWRnMMsGOA==} + peerDependencies: + typescript: '>=4.4.4' + vue: ^2.7.0 || ^3.5.11 + peerDependenciesMeta: + typescript: + optional: true + pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} @@ -1476,10 +1929,41 @@ packages: pnpm-workspace-yaml@1.2.0: resolution: {integrity: sha512-4CnZHmLSaprRnIm2iQ27Zl1cWPRHdX7Ehw7ckRwujoPKCk2QAz4agsA2MbTodg4sgbqYfJ68ULT+Q5A8dU+Mow==} + postcss-cli@11.0.1: + resolution: {integrity: sha512-0UnkNPSayHKRe/tc2YGW6XnSqqOA9eqpiRMgRlV1S6HdGi16vwJBx7lviARzbV1HpQHqLLRH3o8vTcB0cLc+5g==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + postcss: ^8.0.0 + + postcss-load-config@5.1.0: + resolution: {integrity: sha512-G5AJ+IX0aD0dygOE0yFZQ/huFFMSNneyfp0e3/bT05a8OfPC5FUoZRPfGijUdGOJNMewJiwzcHJXFafFzeKFVA==} + engines: {node: '>= 18'} + peerDependencies: + jiti: '>=1.21.0' + postcss: '>=8.0.9' + tsx: ^4.8.1 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + + postcss-reporter@7.1.0: + resolution: {integrity: sha512-/eoEylGWyy6/DOiMP5lmFRdmDKThqgn7D6hP2dXKJI/0rJSO1ADFNngZfDzxL0YAxFvws+Rtpuji1YIHj4mySA==} + engines: {node: '>=10'} + peerDependencies: + postcss: ^8.1.0 + postcss-selector-parser@6.1.2: resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} engines: {node: '>=4'} + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + postcss@8.5.6: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} @@ -1497,6 +1981,13 @@ packages: engines: {node: '>=14'} hasBin: true + pretty-hrtime@1.0.3: + resolution: {integrity: sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==} + engines: {node: '>= 0.8'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -1507,6 +1998,17 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + refa@0.12.1: resolution: {integrity: sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} @@ -1523,6 +2025,10 @@ packages: resolution: {integrity: sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==} hasBin: true + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -1534,6 +2040,9 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + rollup@4.52.4: resolution: {integrity: sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -1542,6 +2051,11 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + sass@1.93.2: + resolution: {integrity: sha512-t+YPtOQHpGW1QWsh1CHQ5cPIr9lbbGZLZnbihP/D/qZj/yuV68m8qarcV17nvkOX81BCrvzAlq2klCQFZghyTg==} + engines: {node: '>=14.0.0'} + hasBin: true + scslre@0.3.0: resolution: {integrity: sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==} engines: {node: ^14.0.0 || >=16.0.0} @@ -1562,6 +2076,13 @@ packages: sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + + sortablejs@1.15.6: + resolution: {integrity: sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A==} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -1575,6 +2096,18 @@ packages: spdx-license-ids@3.0.22: resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} + speakingurl@14.0.1: + resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} + engines: {node: '>=0.10.0'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + strip-indent@4.1.0: resolution: {integrity: sha512-OA95x+JPmL7kc7zCu+e+TeYxEiaIyndRx0OrBcK2QPPH09oAndr2ALvymxWA+Lx1PYYvFUm4O63pRkdJAaW96w==} engines: {node: '>=12'} @@ -1583,6 +2116,10 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + superjson@2.2.2: + resolution: {integrity: sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==} + engines: {node: '>=16'} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -1599,6 +2136,9 @@ packages: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} + thenby@1.3.4: + resolution: {integrity: sha512-89Gi5raiWA3QZ4b2ePcEwswC3me9JIg+ToSgtE0JWeCynLnLxNr/f9G+xfo9K+Oj4AFdom8YNJjibIARTJmapQ==} + tinyexec@1.0.1: resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} @@ -1656,6 +2196,10 @@ packages: unist-util-visit@5.0.0: resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + update-browserslist-db@1.1.3: resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} hasBin: true @@ -1668,19 +2212,19 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - vite@7.1.9: - resolution: {integrity: sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg==} - engines: {node: ^20.19.0 || >=22.12.0} + vite@6.3.6: + resolution: {integrity: sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: - '@types/node': ^20.19.0 || >=22.12.0 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 jiti: '>=1.21.0' - less: ^4.0.0 + less: '*' lightningcss: ^1.21.0 - sass: ^1.70.0 - sass-embedded: ^1.70.0 - stylus: '>=0.54.8' - sugarss: ^5.0.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' terser: ^5.16.0 tsx: ^4.8.1 yaml: ^2.4.2 @@ -1708,6 +2252,17 @@ packages: yaml: optional: true + vue-demi@0.14.10: + resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} + engines: {node: '>=12'} + hasBin: true + peerDependencies: + '@vue/composition-api': ^1.0.0-rc.1 + vue: ^3.0.0-0 || ^2.6.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + vue-eslint-parser@10.2.0: resolution: {integrity: sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1719,6 +2274,9 @@ packages: peerDependencies: vue: ^3.2.0 + vue3-puzzle-vcode@1.1.7: + resolution: {integrity: sha512-mW780dz7HKjrElnE60CeYSeHGidKBKHoMjTDYfqF21330rTkFOsfDK1FQKZ22MktgMtTEoS/imfpEDlM1cxY/g==} + vue@3.5.22: resolution: {integrity: sha512-toaZjQ3a/G/mYaLSbV+QsQhIdMo9x5rrqIpYRObsJ6T/J+RyCSFwN2LHNVH9v8uIcljDNa3QzPVdv3Y6b9hAJQ==} peerDependencies: @@ -1736,10 +2294,18 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + xml-name-validator@4.0.0: resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} engines: {node: '>=12'} + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + yaml-eslint-parser@1.3.0: resolution: {integrity: sha512-E/+VitOorXSLiAqtTd7Yqax0/pAS3xaYMP+AUUJGOK1OZG3rhcj9fcJOM5HJ2VrP1FrStVCWr1muTfQCdj4tAA==} engines: {node: ^14.17.0 || >=16.0.0} @@ -1749,6 +2315,14 @@ packages: engines: {node: '>= 14.6'} hasBin: true + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -1835,12 +2409,18 @@ snapshots: picocolors: 1.1.1 sisteransi: 1.0.5 + '@ctrl/tinycolor@3.6.1': {} + '@dprint/formatter@0.3.0': {} '@dprint/markdown@0.17.8': {} '@dprint/toml@0.6.4': {} + '@element-plus/icons-vue@2.3.2(vue@3.5.22(typescript@5.9.3))': + dependencies: + vue: 3.5.22(typescript@5.9.3) + '@es-joy/jsdoccomment@0.50.2': dependencies: '@types/estree': 1.0.8 @@ -2016,6 +2596,17 @@ snapshots: '@eslint/core': 0.16.0 levn: 0.4.1 + '@floating-ui/core@1.7.3': + dependencies: + '@floating-ui/utils': 0.2.10 + + '@floating-ui/dom@1.7.4': + dependencies: + '@floating-ui/core': 1.7.3 + '@floating-ui/utils': 0.2.10 + + '@floating-ui/utils@0.2.10': {} + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.7': @@ -2027,6 +2618,13 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} + '@iconify/types@2.0.0': {} + + '@iconify/vue@5.0.0(vue@3.5.22(typescript@5.9.3))': + dependencies: + '@iconify/types': 2.0.0 + vue: 3.5.22(typescript@5.9.3) + '@jridgewell/sourcemap-codec@1.5.5': {} '@nodelib/fs.scandir@2.1.5': @@ -2041,12 +2639,71 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 + '@parcel/watcher-android-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-x64@2.5.1': + optional: true + + '@parcel/watcher-freebsd-x64@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-musl@2.5.1': + optional: true + + '@parcel/watcher-win32-arm64@2.5.1': + optional: true + + '@parcel/watcher-win32-ia32@2.5.1': + optional: true + + '@parcel/watcher-win32-x64@2.5.1': + optional: true + + '@parcel/watcher@2.5.1': + dependencies: + detect-libc: 1.0.3 + is-glob: 4.0.3 + micromatch: 4.0.8 + node-addon-api: 7.1.1 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.5.1 + '@parcel/watcher-darwin-arm64': 2.5.1 + '@parcel/watcher-darwin-x64': 2.5.1 + '@parcel/watcher-freebsd-x64': 2.5.1 + '@parcel/watcher-linux-arm-glibc': 2.5.1 + '@parcel/watcher-linux-arm-musl': 2.5.1 + '@parcel/watcher-linux-arm64-glibc': 2.5.1 + '@parcel/watcher-linux-arm64-musl': 2.5.1 + '@parcel/watcher-linux-x64-glibc': 2.5.1 + '@parcel/watcher-linux-x64-musl': 2.5.1 + '@parcel/watcher-win32-arm64': 2.5.1 + '@parcel/watcher-win32-ia32': 2.5.1 + '@parcel/watcher-win32-x64': 2.5.1 + optional: true + '@pkgr/core@0.1.2': {} '@pkgr/core@0.2.9': {} - '@rolldown/pluginutils@1.0.0-beta.29': {} - '@rollup/rollup-android-arm-eabi@4.52.4': optional: true @@ -2123,6 +2780,8 @@ snapshots: estraverse: 5.3.0 picomatch: 4.0.3 + '@sxzz/popperjs-es@2.11.7': {} + '@types/debug@4.1.12': dependencies: '@types/ms': 2.1.0 @@ -2131,6 +2790,12 @@ snapshots: '@types/json-schema@7.0.15': {} + '@types/lodash-es@4.17.12': + dependencies: + '@types/lodash': 4.17.20 + + '@types/lodash@4.17.20': {} + '@types/mdast@4.0.4': dependencies: '@types/unist': 3.0.3 @@ -2139,6 +2804,8 @@ snapshots: '@types/unist@3.0.3': {} + '@types/web-bluetooth@0.0.16': {} + '@typescript-eslint/eslint-plugin@8.46.0(@typescript-eslint/parser@8.46.0(eslint@9.37.0)(typescript@5.9.3))(eslint@9.37.0)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.1 @@ -2232,10 +2899,9 @@ snapshots: '@typescript-eslint/types': 8.46.0 eslint-visitor-keys: 4.2.1 - '@vitejs/plugin-vue@6.0.1(vite@7.1.9(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))': + '@vitejs/plugin-vue@5.2.4(vite@6.3.6(sass@1.93.2)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))': dependencies: - '@rolldown/pluginutils': 1.0.0-beta.29 - vite: 7.1.9(yaml@2.8.1) + vite: 6.3.6(sass@1.93.2)(yaml@2.8.1) vue: 3.5.22(typescript@5.9.3) '@vitest/eslint-plugin@1.3.16(eslint@9.37.0)(typescript@5.9.3)': @@ -2280,6 +2946,24 @@ snapshots: '@vue/devtools-api@6.6.4': {} + '@vue/devtools-api@7.7.7': + dependencies: + '@vue/devtools-kit': 7.7.7 + + '@vue/devtools-kit@7.7.7': + dependencies: + '@vue/devtools-shared': 7.7.7 + birpc: 2.6.1 + hookable: 5.5.3 + mitt: 3.0.1 + perfect-debounce: 1.0.0 + speakingurl: 14.0.1 + superjson: 2.2.2 + + '@vue/devtools-shared@7.7.7': + dependencies: + rfdc: 1.4.1 + '@vue/reactivity@3.5.22': dependencies: '@vue/shared': 3.5.22 @@ -2304,6 +2988,25 @@ snapshots: '@vue/shared@3.5.22': {} + '@vueuse/core@9.13.0(vue@3.5.22(typescript@5.9.3))': + dependencies: + '@types/web-bluetooth': 0.0.16 + '@vueuse/metadata': 9.13.0 + '@vueuse/shared': 9.13.0(vue@3.5.22(typescript@5.9.3)) + vue-demi: 0.14.10(vue@3.5.22(typescript@5.9.3)) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + '@vueuse/metadata@9.13.0': {} + + '@vueuse/shared@9.13.0(vue@3.5.22(typescript@5.9.3))': + dependencies: + vue-demi: 0.14.10(vue@3.5.22(typescript@5.9.3)) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: acorn: 8.15.0 @@ -2317,20 +3020,53 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ansi-regex@5.0.1: {} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 ansis@4.2.0: {} + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + are-docs-informative@0.0.2: {} argparse@2.0.1: {} + async-validator@4.2.5: {} + + asynckit@0.4.0: {} + + autoprefixer@10.4.21(postcss@8.5.6): + dependencies: + browserslist: 4.26.3 + caniuse-lite: 1.0.30001749 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + axios@1.12.2: + dependencies: + follow-redirects: 1.15.11 + form-data: 4.0.4 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + balanced-match@1.0.2: {} baseline-browser-mapping@2.8.15: {} + binary-extensions@2.3.0: {} + + birpc@2.6.1: {} + boolbase@1.0.0: {} brace-expansion@1.1.12: @@ -2358,6 +3094,11 @@ snapshots: cac@6.7.14: {} + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + callsites@3.1.0: {} caniuse-lite@1.0.30001749: {} @@ -2373,18 +3114,44 @@ snapshots: character-entities@2.0.2: {} + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + ci-info@4.3.1: {} clean-regexp@1.0.0: dependencies: escape-string-regexp: 1.0.5 + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + color-convert@2.0.1: dependencies: color-name: 1.1.4 color-name@1.1.4: {} + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + comment-parser@1.4.1: {} concat-map@0.0.1: {} @@ -2393,6 +3160,10 @@ snapshots: confbox@0.2.2: {} + copy-anything@3.0.5: + dependencies: + is-what: 4.1.16 + core-js-compat@3.46.0: dependencies: browserslist: 4.26.3 @@ -2407,6 +3178,8 @@ snapshots: csstype@3.1.3: {} + dayjs@1.11.18: {} + debug@4.4.3: dependencies: ms: 2.1.3 @@ -2417,16 +3190,58 @@ snapshots: deep-is@0.1.4: {} + deep-pick-omit@1.2.1: {} + + defu@6.1.4: {} + + delayed-stream@1.0.0: {} + + dependency-graph@1.0.0: {} + dequal@2.0.3: {} + destr@2.0.5: {} + + detect-libc@1.0.3: + optional: true + devlop@1.1.0: dependencies: dequal: 2.0.3 diff-sequences@27.5.1: {} + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + electron-to-chromium@1.5.234: {} + element-plus@2.11.4(vue@3.5.22(typescript@5.9.3)): + dependencies: + '@ctrl/tinycolor': 3.6.1 + '@element-plus/icons-vue': 2.3.2(vue@3.5.22(typescript@5.9.3)) + '@floating-ui/dom': 1.7.4 + '@popperjs/core': '@sxzz/popperjs-es@2.11.7' + '@types/lodash': 4.17.20 + '@types/lodash-es': 4.17.12 + '@vueuse/core': 9.13.0(vue@3.5.22(typescript@5.9.3)) + async-validator: 4.2.5 + dayjs: 1.11.18 + escape-html: 1.0.3 + lodash: 4.17.21 + lodash-es: 4.17.21 + lodash-unified: 1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21) + memoize-one: 6.0.0 + normalize-wheel-es: 1.2.0 + vue: 3.5.22(typescript@5.9.3) + transitivePeerDependencies: + - '@vue/composition-api' + + emoji-regex@8.0.0: {} + empathic@2.0.0: {} enhanced-resolve@5.18.3: @@ -2436,6 +3251,21 @@ snapshots: entities@4.5.0: {} + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + esbuild@0.25.10: optionalDependencies: '@esbuild/aix-ppc64': 0.25.10 @@ -2467,6 +3297,8 @@ snapshots: escalade@3.2.0: {} + escape-html@1.0.3: {} + escape-string-regexp@1.0.5: {} escape-string-regexp@4.0.0: {} @@ -2568,7 +3400,7 @@ snapshots: eslint: 9.37.0 eslint-compat-utils: 0.6.5(eslint@9.37.0) eslint-json-compat-utils: 0.2.1(eslint@9.37.0)(jsonc-eslint-parser@2.4.1) - espree: 10.4.0 + espree: 9.6.1 graphemer: 1.4.0 jsonc-eslint-parser: 2.4.1 natural-compare: 1.4.0 @@ -2820,11 +3652,51 @@ snapshots: flatted@3.3.3: {} + follow-redirects@1.15.11: {} + + form-data@4.0.4: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + format@0.2.2: {} + fraction.js@4.3.7: {} + + fs-extra@11.3.2: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + fsevents@2.3.3: optional: true + function-bind@1.1.2: {} + + get-caller-file@2.0.5: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + get-tsconfig@4.12.0: dependencies: resolve-pkg-maps: 1.0.0 @@ -2847,16 +3719,32 @@ snapshots: globrex@0.1.2: {} + gopd@1.2.0: {} + graceful-fs@4.2.11: {} graphemer@1.4.0: {} has-flag@4.0.0: {} + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hookable@5.5.3: {} + ignore@5.3.2: {} ignore@7.0.5: {} + immutable@5.1.3: {} + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -2866,18 +3754,26 @@ snapshots: indent-string@5.0.0: {} + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + is-builtin-module@5.0.0: dependencies: builtin-modules: 5.0.0 is-extglob@2.1.1: {} + is-fullwidth-code-point@3.0.0: {} + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 is-number@7.0.0: {} + is-what@4.1.16: {} + isexe@2.0.0: {} js-yaml@4.1.0: @@ -2907,6 +3803,12 @@ snapshots: espree: 9.6.1 semver: 7.7.3 + jsonfile@6.2.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -2916,6 +3818,8 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lilconfig@3.1.3: {} + local-pkg@1.1.2: dependencies: mlly: 1.8.0 @@ -2926,6 +3830,14 @@ snapshots: dependencies: p-locate: 5.0.0 + lodash-es@4.17.21: {} + + lodash-unified@1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21): + dependencies: + '@types/lodash-es': 4.17.12 + lodash: 4.17.21 + lodash-es: 4.17.21 + lodash.merge@4.6.2: {} lodash@4.17.21: {} @@ -2938,6 +3850,8 @@ snapshots: markdown-table@3.0.4: {} + math-intrinsics@1.1.0: {} + mdast-util-find-and-replace@3.0.2: dependencies: '@types/mdast': 4.0.4 @@ -3051,6 +3965,8 @@ snapshots: dependencies: '@types/mdast': 4.0.4 + memoize-one@6.0.0: {} + merge2@1.4.1: {} micromark-core-commonmark@2.0.3: @@ -3256,6 +4172,12 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + minimatch@3.1.2: dependencies: brace-expansion: 1.1.12 @@ -3264,6 +4186,8 @@ snapshots: dependencies: brace-expansion: 2.0.2 + mitt@3.0.1: {} + mlly@1.8.0: dependencies: acorn: 8.15.0 @@ -3279,8 +4203,17 @@ snapshots: natural-orderby@5.0.0: {} + node-addon-api@7.1.1: + optional: true + node-releases@2.0.23: {} + normalize-path@3.0.0: {} + + normalize-range@0.1.2: {} + + normalize-wheel-es@1.2.0: {} + nth-check@2.1.1: dependencies: boolbase: 1.0.0 @@ -3326,12 +4259,31 @@ snapshots: pathe@2.0.3: {} + perfect-debounce@1.0.0: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} picomatch@4.0.3: {} + pify@2.3.0: {} + + pinia-plugin-persistedstate@4.5.0(pinia@3.0.3(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3))): + dependencies: + deep-pick-omit: 1.2.1 + defu: 6.1.4 + destr: 2.0.5 + optionalDependencies: + pinia: 3.0.3(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3)) + + pinia@3.0.3(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3)): + dependencies: + '@vue/devtools-api': 7.7.7 + vue: 3.5.22(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + pkg-types@1.3.1: dependencies: confbox: 0.1.8 @@ -3350,11 +4302,44 @@ snapshots: dependencies: yaml: 2.8.1 + postcss-cli@11.0.1(postcss@8.5.6): + dependencies: + chokidar: 3.6.0 + dependency-graph: 1.0.0 + fs-extra: 11.3.2 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-load-config: 5.1.0(postcss@8.5.6) + postcss-reporter: 7.1.0(postcss@8.5.6) + pretty-hrtime: 1.0.3 + read-cache: 1.0.0 + slash: 5.1.0 + tinyglobby: 0.2.15 + yargs: 17.7.2 + transitivePeerDependencies: + - jiti + - tsx + + postcss-load-config@5.1.0(postcss@8.5.6): + dependencies: + lilconfig: 3.1.3 + yaml: 2.8.1 + optionalDependencies: + postcss: 8.5.6 + + postcss-reporter@7.1.0(postcss@8.5.6): + dependencies: + picocolors: 1.1.1 + postcss: 8.5.6 + thenby: 1.3.4 + postcss-selector-parser@6.1.2: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 + postcss-value-parser@4.2.0: {} + postcss@8.5.6: dependencies: nanoid: 3.3.11 @@ -3369,12 +4354,26 @@ snapshots: prettier@3.6.2: {} + pretty-hrtime@1.0.3: {} + + proxy-from-env@1.1.0: {} + punycode@2.3.1: {} quansync@0.2.11: {} queue-microtask@1.2.3: {} + read-cache@1.0.0: + dependencies: + pify: 2.3.0 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + readdirp@4.1.2: {} + refa@0.12.1: dependencies: '@eslint-community/regexpp': 4.12.1 @@ -3390,12 +4389,16 @@ snapshots: dependencies: jsesc: 3.0.2 + require-directory@2.1.1: {} + resolve-from@4.0.0: {} resolve-pkg-maps@1.0.0: {} reusify@1.1.0: {} + rfdc@1.4.1: {} + rollup@4.52.4: dependencies: '@types/estree': 1.0.8 @@ -3428,6 +4431,14 @@ snapshots: dependencies: queue-microtask: 1.2.3 + sass@1.93.2: + dependencies: + chokidar: 4.0.3 + immutable: 5.1.3 + source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.1 + scslre@0.3.0: dependencies: '@eslint-community/regexpp': 4.12.1 @@ -3444,6 +4455,10 @@ snapshots: sisteransi@1.0.5: {} + slash@5.1.0: {} + + sortablejs@1.15.6: {} + source-map-js@1.2.1: {} spdx-exceptions@2.5.0: {} @@ -3455,10 +4470,26 @@ snapshots: spdx-license-ids@3.0.22: {} + speakingurl@14.0.1: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + strip-indent@4.1.0: {} strip-json-comments@3.1.1: {} + superjson@2.2.2: + dependencies: + copy-anything: 3.0.5 + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -3474,6 +4505,8 @@ snapshots: tapable@2.3.0: {} + thenby@1.3.4: {} + tinyexec@1.0.1: {} tinyglobby@0.2.15: @@ -3529,6 +4562,8 @@ snapshots: unist-util-is: 6.0.0 unist-util-visit-parents: 6.0.1 + universalify@2.0.1: {} + update-browserslist-db@1.1.3(browserslist@4.26.3): dependencies: browserslist: 4.26.3 @@ -3541,7 +4576,7 @@ snapshots: util-deprecate@1.0.2: {} - vite@7.1.9(yaml@2.8.1): + vite@6.3.6(sass@1.93.2)(yaml@2.8.1): dependencies: esbuild: 0.25.10 fdir: 6.5.0(picomatch@4.0.3) @@ -3551,8 +4586,13 @@ snapshots: tinyglobby: 0.2.15 optionalDependencies: fsevents: 2.3.3 + sass: 1.93.2 yaml: 2.8.1 + vue-demi@0.14.10(vue@3.5.22(typescript@5.9.3)): + dependencies: + vue: 3.5.22(typescript@5.9.3) + vue-eslint-parser@10.2.0(eslint@9.37.0): dependencies: debug: 4.4.3 @@ -3570,6 +4610,8 @@ snapshots: '@vue/devtools-api': 6.6.4 vue: 3.5.22(typescript@5.9.3) + vue3-puzzle-vcode@1.1.7: {} + vue@3.5.22(typescript@5.9.3): dependencies: '@vue/compiler-dom': 3.5.22 @@ -3586,8 +4628,16 @@ snapshots: word-wrap@1.2.5: {} + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + xml-name-validator@4.0.0: {} + y18n@5.0.8: {} + yaml-eslint-parser@1.3.0: dependencies: eslint-visitor-keys: 3.4.3 @@ -3595,6 +4645,18 @@ snapshots: yaml@2.8.1: {} + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + yocto-queue@0.1.0: {} zwitch@2.0.4: {} diff --git a/code/link/link-frontend/postcss.config.cjs b/code/link/link-frontend/postcss.config.cjs new file mode 100644 index 00000000..3277144d --- /dev/null +++ b/code/link/link-frontend/postcss.config.cjs @@ -0,0 +1,7 @@ +module.exports = { + plugins: [ + require('autoprefixer')({ + overrideBrowserslist: ['> 0.5%', 'last 2 versions', 'IE 10'], + }), + ], +} diff --git a/code/link/link-frontend/public/Thumbs.db b/code/link/link-frontend/public/Thumbs.db new file mode 100644 index 0000000000000000000000000000000000000000..4a527338e0a9a6a4f742ec103f4934f1df08409c GIT binary patch literal 3584 zcmca`Uhu)fjZzO8(10BSGsD0CoD6J8;*3Bx2!nwD0|OI~0pkDr|NlQkkbwcn90fxt z1pWfu3W`4vW&uVbD>OcbkQYXsN;Sr0E8 zaOhNcx74)F@(v?2Q2qcYu>pY*gDHaILOnVE@&nU$3V=wZfMpgaSMAghp~p(C4cU?RIxp@>oA z#DyHnP8$!323`E1Vw_ae#K|QlE+HwUs-~`?sbyknW^Q3==I-I?6&w;879J59 zm7J2AmY$KBRa{b9R$ftA)!fqB*51+CHEHscsne#9glAUcUPH>GPMb-@gC&`3vMPMh0ex zx8OcQ^OqpdUra2Zcmer~k*OTyaX}VVMME|r$3XVPLSZGNMh+3Di3>L#)Lis4UdH6+CCjb>mixqt>VH zrak1?wqCDI@xx)p`AYUDv-JZuZ11}l_*5aG_m6_$@k`sMJr42=?fZNFgvmKW8a$6P$&q?+(oaJfm6<*}`ValwG`9A)eG7Ww*{s1II^*3#e%%ViO2mo?B_<=X`g2M zoOIyrj^v-u`}S5{pZt|;&-L%|d!Czt+4J3p;kb@JZt>N|Tc+k8LrWuf1eg;wj9mRek1c3sEd-IDDs fJMUP^noL>mee14Xq;uGRhW|H#Wzi_0UkCsIk=T10 literal 0 HcmV?d00001 diff --git a/code/link/link-frontend/public/logo.ico b/code/link/link-frontend/public/logo.ico new file mode 100644 index 0000000000000000000000000000000000000000..cb52fd00d9de151b205d7ecb752196232e34f64a GIT binary patch literal 2324 zcmV+v3G4QWP)2*47PtUcNndwY>&ilUS(sp{Z8%Zo&DuokSpD%z#;+YC;i^$@kS%h64IlR)lL~_FMc`Tfy zMP%iHLnD`gPT>*?IDvQ$3ngeCnly6rpvOX6gxw;v-F`B_>9Ui!><|>kLWvzK_S-x@+ zTvY|bS`&eR0EfIfU=_OvO%B)sau{Tb@_zuF(knox2s=~nip`+uXdc=ka+c~HRn}rS9^`U^M%^0MO!*%>#>Y%!uin5h#+2zMt)W-HFYBghh@3Pffl2_7k~qnh@5u-Rejjgoa=S%L0;IRLKBjF zZ7%+em6c_!!GK`}xFs+wH@mmgS6_+Gk8$Glc%@WEHEUwSLZ(Pey0Ay#7r;^>jXU=b|1<{$Go# znssLlyzLE)2=AX31VeA}Xq}bU1>}^WC4fOe8!qVXg5vPk zSMSsmhX#Yv^byn7n)cO5yRNplE@|sZ;~~B{qWoulvsd_n*jysTH#@v!vB+rX?YpYQ z({srYN$gA;8Q)0d9?dV_x}q$$nu6q(!Fpe9JY1|CqrcX#3EleoyK89T+@I4&c$FV(%*u}At zmwJrmTg_GhIbV|0bF;BAMcpVVSJb4xM|1mlU_=pP(!UjmZTkJYUTPo1_bGhW6TZeX zgUzJqCEfj68J_cmM-;wC;Ug-z5z9P9Gq1#$ZBKBI#3vqX%M*OGSm_$ghby%S6?icf zy$zo(&pivpLi{A5z;N z(DiFU3pH)0(RnjAHzKL;mSIUZ1-VmDT}E}TLha9K>d|`bbR@AyQs)9Qf5>tJWo`OL ztECesqTyW+v4>GRBz`AsFu;00F#f__$HGEF*kD?c8V@W8C|e&v&ER zEXfwFbXnCEmF^L+a%MI>@?iS}HX(=~*SNK2V?pmvp9(h;ej^f`5X1x{b^_kd7zU%5 zN92|B!QdFjb}4K`65AAhW-fWHuH6lnCph+cD0X?%5TqWB4i#&MvTFC1)y7LPW-AaJ zHj!h_X&+q>fGH7LZL>@d?Sv@9jZ~Ofh32a1SFXVY! zQ6Hy;F@YWDN-Is1Q_MwV*XqsXMt4bd*46fX_2mmQGoz^||2LN^Eb2`GOUd|Oqs%@| z?&0JFBVNd|p9S!8t#xHfzo*1rnAREFr-(la_4S6nF;DMd_`R$3kHXfk7OTg_(uo}R zdmc*AEGUD|L+xBJI3}0tt za?+oCJYBe>QavoUSjD6jTa-Ifz?WC);L0%jQudEFKjjhDMQ z^gdee3943sZ3fy!cv%7T*t*N1K|wcis$S6Af^3RXZvnYEwO2yU(mR0JWG1gztYQ_B z!K(e7=5o-iNQMZ^1>G%5hk0akYPTf!STNlfXwVbn4y|_bnw?VzB05xrZV}kU>EhEX zBKKgq>e8xBs|OJUli5778O0P(gI3pg#pP6sQH_IL$l}mtS;fh#T~V<(xW>Z{4ULj& zvA`(TyFBbNN|!~`4;~tf+Rp1n0kwH#a=_u=sc9^h5n+!(Lmt{J?D23gH3GUtxhn!o z09^rE9PD#oD1hk(Mx(Zf`kdTnls=DoMY+epK>-c&N}oe+Ssw6`%cDMvYz}rq@eJnk u1;X+9Ks+~_$c5tbQzbH&M)3^g3;YYh(AEid?=#K-0000 - - diff --git a/code/link/link-fronted/src/assets/vue.svg b/code/link/link-frontend/src/assets/vue.svg similarity index 100% rename from code/link/link-fronted/src/assets/vue.svg rename to code/link/link-frontend/src/assets/vue.svg diff --git a/code/link/link-frontend/src/components/avatar.vue b/code/link/link-frontend/src/components/avatar.vue new file mode 100644 index 00000000..e0d7864e --- /dev/null +++ b/code/link/link-frontend/src/components/avatar.vue @@ -0,0 +1,59 @@ + + + + + diff --git a/code/link/link-frontend/src/components/icon.vue b/code/link/link-frontend/src/components/icon.vue new file mode 100644 index 00000000..9e340aac --- /dev/null +++ b/code/link/link-frontend/src/components/icon.vue @@ -0,0 +1,58 @@ + + + + + diff --git a/code/link/link-frontend/src/main.js b/code/link/link-frontend/src/main.js new file mode 100644 index 00000000..71ca5732 --- /dev/null +++ b/code/link/link-frontend/src/main.js @@ -0,0 +1,18 @@ +import './style.css' + +import { createApp } from 'vue' +import App from './App.vue' +import router from './router' + +import ElementPlus from 'element-plus' +import zhCn from 'element-plus/es/locale/lang/zh-cn' +import 'element-plus/dist/index.css' + +import { createPinia } from 'pinia' +import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' +const pinia = createPinia() +pinia.use(piniaPluginPersistedstate) + +const app = createApp(App) + +app.use(router).use(pinia).use(ElementPlus, { locale: zhCn }).mount('#app') diff --git a/code/link/link-frontend/src/router/index.js b/code/link/link-frontend/src/router/index.js new file mode 100644 index 00000000..f89695ad --- /dev/null +++ b/code/link/link-frontend/src/router/index.js @@ -0,0 +1,32 @@ +import { createRouter, createWebHistory } from 'vue-router' +import Im from '@/views/im/index.vue' + +const routes = [ + { + path: '/', + redirect: '/im', + }, + { + path: '/im', + component: Im, + }, +] + +const router = createRouter({ + history: createWebHistory(), + routes, +}) + +// 全局前置守卫 +router.beforeEach((to, from, next) => { + // 设置页面标题 + document.title = to.meta.title ? `${to.meta.title} - 系统名称` : '系统名称' + + // 通过检查,继续跳转 + next() +}) + +router.afterEach(() => { +}) + +export default router diff --git a/code/link/link-frontend/src/stores/userStore.js b/code/link/link-frontend/src/stores/userStore.js new file mode 100644 index 00000000..419ec6f7 --- /dev/null +++ b/code/link/link-frontend/src/stores/userStore.js @@ -0,0 +1,43 @@ +import { defineStore } from 'pinia' +import { ref } from 'vue' + +export const useUserStore = defineStore( + 'userInfo', + () => { + + const userInfo = ref({ + orgid: '', + userid: '', + token: '', + }) + + // 设置用户信息 + function setUserInfo(orgid, userid, token) { + userInfo.value.orgid = orgid + userInfo.value.userid = userid + userInfo.value.token = token + } + + // 重置用户信息 + function resetUserInfo() { + userInfo.value.token = '' + } + + // 判断是否登录:三个都存在才算登录 + function isLogined() { + return !!userInfo.value.orgid && !!userInfo.value.userid && !!userInfo.value.token + } + + return { + userInfo, + setUserInfo, + resetUserInfo, + isLogined, + } + }, + { + persist: { + paths: ['userInfo'], + }, + }, +) diff --git a/code/link/link-frontend/src/style.css b/code/link/link-frontend/src/style.css new file mode 100644 index 00000000..3650bbf9 --- /dev/null +++ b/code/link/link-frontend/src/style.css @@ -0,0 +1,11 @@ +html, +body, +#app { + height: 100%; +} + +* { + box-sizing: border-box; + padding: 0; + margin: 0; +} diff --git a/code/link/link-frontend/src/utils/axios.js b/code/link/link-frontend/src/utils/axios.js new file mode 100644 index 00000000..0a76188b --- /dev/null +++ b/code/link/link-frontend/src/utils/axios.js @@ -0,0 +1,34 @@ +// src/utils/axios.js +import axios from 'axios' + +const instance = axios.create({ + baseURL: 'http://localhost:8089/api', // 后端接口地址 + timeout: 5000 +}) + +// 请求拦截器:自动添加 token +instance.interceptors.request.use( + config => { + const token = localStorage.getItem('token') + if (token) { + config.headers.Authorization = `Bearer ${token}` + } + return config + }, + error => Promise.reject(error) +) + +// 响应拦截器:统一处理错误或过期 token +instance.interceptors.response.use( + response => response, + error => { + if (error.response && error.response.status === 401) { + alert('登录失效,请重新登录') + window.localStorage.removeItem('token') + window.location.href = '/login' + } + return Promise.reject(error) + } +) + +export default instance diff --git a/code/link/link-frontend/src/utils/dataDiffUtils.js b/code/link/link-frontend/src/utils/dataDiffUtils.js new file mode 100644 index 00000000..771ac9a1 --- /dev/null +++ b/code/link/link-frontend/src/utils/dataDiffUtils.js @@ -0,0 +1,493 @@ +import axios from '@/utils/axios' +import { ref } from 'vue' +import { ElMessage, ElMessageBox } from 'element-plus' + + +const saving = ref(false) +const error = ref(null) + +/* 比对并分类数据变更 + * @param {Array} currentData 变更后的数据数组 + * @param {Array} originalData 原始数据数组 + * @param {String} primaryKey 主键字段名 + * @returns {Object} 包含分类结果的对象 { insertdata: [], updatedata: [], deletedata: [], hasChanges: Boolean } + */ +export const compareAndClassifyData = (currentData, originalData, primaryKey) => { + const result = { + insertdata: [], + updatedata: [], + deletedata: [], + hasChanges: false, // 默认无变动 + } + + // 1. 找出新增的数据 + result.insertdata = currentData.filter( + (row) => + !row[primaryKey] || + !originalData.some((originalRow) => originalRow[primaryKey] === row[primaryKey]), + ) + + // 2. 找出修改的数据 + result.updatedata = currentData.reduce((acc, row) => { + const rowId = row[primaryKey] + if (!rowId) return acc + + const originalRow = originalData.find((r) => r[primaryKey] === rowId) + if (!originalRow) return acc + + const changedFields = {} + Object.keys(row).forEach((key) => { + if (key !== primaryKey && !isEqual(row[key], originalRow[key])) { + changedFields[key] = row[key] + } + }) + + if (Object.keys(changedFields).length > 0) { + acc.push({ + [primaryKey]: rowId, + ...changedFields, + }) + } + + return acc + }, []) + + // 3. 找出删除的数据 + result.deletedata = originalData.filter( + (originalRow) => !currentData.some((row) => row[primaryKey] === originalRow[primaryKey]), + ) + + // 4. 是否有任何变动 + result.hasChanges = + result.insertdata.length > 0 || result.updatedata.length > 0 || result.deletedata.length > 0 + + return result +} + +/** + * 在数组对象的每个元素中覆盖式填入 JSON 对象的值 + * @param {Array} arr - 目标数组(将被修改) + * @param {Object} json - 包含要添加/覆盖的键值对的 JSON 对象 + * @returns {Array} 修改后的数组 + * @throws {Error} 当参数类型不正确时抛出错误 + */ +export function fillDefaultArray(arr, json) { + // 基本数据验证 + if (!Array.isArray(arr)) throw new Error('第一个参数必须是数组') + if (typeof json !== 'object' || json === null || Array.isArray(json)) { + throw new Error('第二个参数必须是普通对象(JSON对象)') + } + + // 覆盖式填充每个元素 + return arr.map((item) => { + // 只处理对象元素 + if (typeof item === 'object' && item !== null) { + return { ...item, ...json } // 覆盖已有属性 + } + return item // 非对象元素保持不变 + }) +} + +/* 比对并分类数据变更 + * @param {Array} currentData 变更后的数据数组 + * @param {Array} originalData 原始数据数组 + * @param {String} primaryKey 主键字段名 + * @returns {Object} 包含分类结果的对象 { insertdata: [], updatedata: [], deletedata: [], hasChanges: Boolean } + */ +export const compareAndClassifyData1 = ( + currentData, + originalData, + primaryKey, + extraFields = {}, +) => { + const result = { + insertdata: [], + updatedata: [], + deletedata: [], + hasChanges: false, // 默认无变动 + } + + // 1. 找出新增的数据 + result.insertdata = currentData + .filter( + (row) => + !row[primaryKey] || + !originalData.some((originalRow) => originalRow[primaryKey] === row[primaryKey]), + ) + .map((row) => ({ + ...row, + ...extraFields, // ✅ 添加新字段 + })) + + // 2. 找出修改的数据 + result.updatedata = currentData.reduce((acc, row) => { + const rowId = row[primaryKey] + if (!rowId) return acc + + const originalRow = originalData.find((r) => r[primaryKey] === rowId) + if (!originalRow) return acc + + const changedFields = {} + Object.keys(row).forEach((key) => { + if (key !== primaryKey && !isEqual(row[key], originalRow[key])) { + changedFields[key] = row[key] + } + }) + + if (Object.keys(changedFields).length > 0) { + acc.push({ + [primaryKey]: rowId, + ...changedFields, + }) + } + + return acc + }, []) + + // 3. 找出删除的数据 + result.deletedata = originalData.filter( + (originalRow) => !currentData.some((row) => row[primaryKey] === originalRow[primaryKey]), + ) + + // 4. 是否有任何变动 + result.hasChanges = + result.insertdata.length > 0 || result.updatedata.length > 0 || result.deletedata.length > 0 + + return result +} + +/** + * 深度比较两个值是否相等 + * @param {*} a 第一个值 + * @param {*} b 第二个值 + * @returns {Boolean} 是否相等 + */ +export const isEqual = (a, b) => { + // 处理基本类型和null/undefined + if (a === b) return true + if (typeof a !== typeof b) return false + if (typeof a !== 'object' || a === null || b === null) { + return a === b + } + + // 处理数组 + if (Array.isArray(a) && Array.isArray(b)) { + if (a.length !== b.length) return false + return a.every((item, i) => isEqual(item, b[i])) + } + + // 处理对象 + const keysA = Object.keys(a) + const keysB = Object.keys(b) + + if (keysA.length !== keysB.length) return false + + return keysA.every((key) => isEqual(a[key], b[key])) +} + +/** + * 检测并提取变更的字段(跳过v_开头的字段) + * @param {Object} currentData 当前数据 + * @param {Object} originalData 原始数据 + * @param {String} primaryKey 主键字段名 + * @param {String} primaryKeyValue 主键值 + * @returns {Object} 包含变更数据的对象,结构为 { hasChanges: boolean, updateData: Object } + */ +export const detectChanges = (currentData, originalData, primaryKey, primaryKeyValue) => { + const updateValue = {} + + // 遍历所有字段检测变更 + Object.keys(currentData).forEach((key) => { + // 跳过以v_开头的字段 + if (key.startsWith('v_')) return + + if (key === 'b_type') { + const currentStr = normalizeOwnerCustomer(currentData[key]) + const originalStr = normalizeOwnerCustomer(originalData[key]) + + if (currentStr !== originalStr) { + updateValue[key] = currentStr // 保存时格式化为逗号分隔字符串 + } + } else { + // 比较前,先将 undefined 转换为空字符串 + const currentValue = currentData[key] === undefined ? '' : currentData[key] + const originalValue = originalData[key] === undefined ? '' : originalData[key] + + // 使用JSON.stringify进行深度比较 + if (JSON.stringify(currentValue) !== JSON.stringify(originalValue)) { + updateValue[key] = currentValue + } + } + }) + + // 如果没有有效变更 + if (Object.keys(updateValue).length === 0) { + return { hasChanges: true, updateData: [] } + } + + // 返回变更数据和主键 + return { + hasChanges: true, + updateData: [ + { + [primaryKey]: primaryKeyValue, + ...updateValue, + }, + ], + } +} + +/** + * 检测并提取变更的字段(跳过v_开头的字段和指定排除字段) + * @param {Object} currentData 当前数据 + * @param {Object} originalData 原始数据 + * @param {Array|string} primaryKeys 主键字段名或数组 + * @param {Array} excludeKeys 可选,排除的字段数组 + * @returns {Object} 包含变更数据的对象,结构为 { hasChanges: boolean, updateData: Array } + */ +export const detectChangesFields = (currentData, originalData, primaryKeys, excludeKeys = []) => { + const updateValue = {} + + Object.keys(currentData).forEach((key) => { + // 跳过以v_开头的字段 + if (key.startsWith('v_')) return + + // 跳过排除字段 + if (excludeKeys.includes(key)) return + + const currentValue = currentData[key] === undefined ? '' : currentData[key] + const originalValue = originalData[key] === undefined ? '' : originalData[key] + + if (JSON.stringify(currentValue) !== JSON.stringify(originalValue)) { + updateValue[key] = currentValue + } + }) + + if (Object.keys(updateValue).length === 0) { + return { hasChanges: false, updateData: [] } + } + + // 构建主键字段对象 + const primaryKeyObj = {} + if (Array.isArray(primaryKeys)) { + primaryKeys.forEach((key) => { + primaryKeyObj[key] = originalData[key] + }) + } else { + primaryKeyObj[primaryKeys] = originalData[primaryKeys] + } + + return { + hasChanges: true, + updateData: [ + { + ...primaryKeyObj, + ...updateValue, + }, + ], + } +} + +const normalizeOwnerCustomer = (val) => { + if (typeof val === 'string') { + return val.split(',').filter(Boolean).join(',') // '001,002,003' + } + + if (Array.isArray(val)) { + return val.map((item) => (Array.isArray(item) ? item[0] : item)).join(',') + } + return '' +} + +export async function buildDeleteRequest(tableName, pkColumn, whereClause) { + const params = { + tableName, + pkColumn, + whereClause, + } + + const result = await axios.get('/pagdetail', { params }) + const records = result.data?.data?.records || [] + + if (records.length > 0) { + let deletes = records.map((item) => ({ + [pkColumn]: item[pkColumn], // 动态取主键 + })) + + return { + tableName, + keyField: pkColumn, + inserts: [], + updates: [], + deletes, + } + } + return null +} + +export const getDataBySql = async (sql) => { + try { + const response = await axios.get('/selectlistbysql', { + params: { sql } // GET 请求参数 + }) + if (response.data?.success) { + return response.data.data + } else { + console.error('查询失败:', response.data?.message) + return [] + } + } catch (err) { + console.error('接口异常:', err) + return [] + } +} + + + +/** + * 计算已收金额(折算成目标币种) + * @param {Array} subtable 子表数据 + * @param {String} targetMoney 目标币种 + * @param {Number} exrate 汇率 + * @returns {Number} 折算后的总金额 + */ +export function calcAlreadySumAmount(subtable, targetMoney, exrate = 1) { + const moneyArray = getMoneyArray(subtable) + let Fxzamount = 0 + + moneyArray.forEach(item => { + if (item.moneyname === targetMoney) { + Fxzamount += Math.round(Number(item.amount_xz) * 1000) / 1000 + } else { + if (targetMoney === 'RMB') { + Fxzamount += Math.round(Number(item.amount_xz) * exrate * 1000) / 1000 + } else { + Fxzamount += Math.round(Number(item.amount_xz) / exrate * 1000) / 1000 + } + } + }) + + return Math.round(Fxzamount * 10000) / 10000 +} + +/** + * 按币种汇总子表金额 + * @param {Array} subtable 子表数据 + * @returns {Array} 形如 [{ moneyname: 'USD', amount_xz: 100 }, ...] + */ +function getMoneyArray(subtable) { + const rows = subtable || [] + const moneyMap = new Map() + + rows.forEach(row => { + let xzamount = 0 + if (row.mx_type_id === '应付') { + xzamount = 0 - parseFloat(row.mx_xzamount || 0) + } + if (row.mx_type_id === '应收') { + xzamount = parseFloat(row.mx_xzamount || 0) + } + + if (row.mx_moneytype_id) { + const prev = moneyMap.get(row.mx_moneytype_id) || 0 + moneyMap.set(row.mx_moneytype_id, prev + xzamount) + } + }) + + return Array.from(moneyMap.entries()).map(([moneyname, amount]) => ({ + moneyname, + amount_xz: amount + })) +} + + +export const handleUpdateWidthEdit = (newWidth, oldWidth, column, columns) => { + const index = columns.findIndex(c => c.prop === column.property) + if (index >= 0) { + const newColumns = [...columns] + newColumns[index] = { + ...newColumns[index], + edit_width: parseInt(newWidth) || newWidth // 确保是数字 + } + return newColumns + } + return columns +} + +export const handleSaveColumnEdit = async (columns, tableName) => { + try { + saving.value = true + error.value = '' + const newColumns = columns.map(item => ({ + ...item, + col_TableName: tableName // 或者你要的默认值 + })) + + const response = await axios.post('/saveColumnEdit', newColumns, { + headers: { 'Content-Type': 'application/json' } + }) + if (response.data?.success) { + ElMessage.success('保存成功') + } else { + throw new Error(response.data?.message || '保存失败') + } + } catch (err) { + error.value = `保存失败: ${err.message}` + ElMessage.error('保存失败') + } finally { + saving.value = false + } +} + +export const handleUpdateWidth = (newWidth, oldWidth, column, columns) => { + const index = columns.findIndex(c => c.prop === column.property) + if (index >= 0) { + const newColumns = [...columns] + newColumns[index] = { + ...newColumns[index], + width: parseInt(newWidth) || newWidth // 确保是数字 + } + return newColumns + } + return columns +} + +export const handleSaveColumn = async (columns, tableName) => { + try { + saving.value = true + error.value = '' + const newColumns = columns.map(item => ({ + ...item, + col_TableName: tableName // 或者你要的默认值 + })) + + const response = await axios.post('/saveColumn', newColumns, { + headers: { 'Content-Type': 'application/json' } + }) + if (response.data?.success) { + ElMessage.success('保存成功') + } else { + throw new Error(response.data?.message || '保存失败') + } + } catch (err) { + error.value = `保存失败: ${err.message}` + ElMessage.error('保存失败') + } finally { + saving.value = false + } +} + +// 默认导出所有方法 +export default { + isEqual, + compareAndClassifyData, + detectChanges, + buildDeleteRequest, + fillDefaultArray, + getDataBySql, + calcAlreadySumAmount, + handleUpdateWidthEdit, + handleSaveColumnEdit, + handleUpdateWidth, + handleSaveColumn +} diff --git a/code/link/link-frontend/src/utils/dodata.js b/code/link/link-frontend/src/utils/dodata.js new file mode 100644 index 00000000..2a8e7328 --- /dev/null +++ b/code/link/link-frontend/src/utils/dodata.js @@ -0,0 +1,203 @@ +import axios from '@/utils/axios' +import { ref } from 'vue' + + + +const loading = ref(false) +const error = ref(null) + +// 获取表格列配置 +export const getTableColumns = async (tableName) => { + try { + loading.value = true; + error.value = ''; + const response = await axios.get('/columns', { + params: { tableName } + }); + if (response.data?.success) { + return { + success: true, + data: response.data.data, + message: '列配置加载成功' + }; + } + throw new Error(response.data?.message || '返回数据格式异常'); + } catch (err) { + error.value = `加载列配置失败: ${err.message}`; + console.error('加载列配置错误:', err); + return { + success: false, + data: [], + error: err.message + }; + } finally { + loading.value = false; + } +}; +export const getMainTable = async (tableName, pkColumnValue) => { + try { + loading.value = true; + error.value = ''; + + const params = { + tableName, + pkColumn: "b_id", + pkColumnValue + }; + + const response = await axios.get('/getobject', { params }); + + if (response.data?.success) { + return { + success: true, + data: { + current: { ...response.data.data }, + original: { ...response.data.data } + }, + message: '主表数据加载成功' + }; + } + throw new Error(response.data?.message || '返回数据格式异常'); + } catch (err) { + error.value = `加载主表数据失败: ${err.message}`; + console.error('加载主表错误:', err); + return { + success: false, + data: { + current: null, + original: null + }, + error: err.message + }; + } finally { + loading.value = false; + } +}; +/** + * 通用子表合并查询方法 + * @param {string} tableName 表名 + * @param {Ref} targetRef 目标数据的 ref + * @param {Ref} originalRef 原始数据的 ref(如果需要) + * @param {string} whereClause 条件 + * @param {function} transformFn 数据转换函数 (可选) + */ +export const getSubTableMerger = async ( + tableName, + targetRef, + originalRef, + whereClause, + orderby, + transformFn +) => { + try { + loading.value = true + error.value = null + + const params = { + tableName: tableName || 'v_bs_business_container', + pkColumn: orderby, + whereClause, + } + const response = await axios.get('/pagdetail', { params }) + + if (response.data?.success) { + // const rawData = JSON.parse(JSON.stringify(response.data.data.records || [])) + + // // 去掉 subid 字段 + // const dataWithoutSubid = rawData.map(({ subid, ...rest }) => rest) + + // // 如果有 transformFn,就对数据做额外处理 + // const processedData = transformFn ? dataWithoutSubid.map(transformFn) : dataWithoutSubid + + + const rawData = JSON.parse(JSON.stringify(response.data.data.records || [])) + + // 如果有 transformFn,就对数据做额外处理 + const processedData = transformFn ? rawData.map(transformFn) : rawData + + targetRef.value = [ + ...targetRef.value, + ...JSON.parse(JSON.stringify(processedData)) + ] + return processedData + } else { + throw new Error(response.data?.message || '返回数据格式异常') + } + } catch (err) { + error.value = `加载数据失败: ${err.message}` + console.error('API Error:', err) + return [] + } finally { + loading.value = false + } +} + + +export const fetchTableColumnsEdit = async (tableName, targetRef) => { + try { + loading.value = true + error.value = null + + const response = await axios.get('/columnsEdit', { + params: { tableName }, + }) + if (response.data?.success) { + // 直接对传入的响应式引用赋值 + targetRef.value = response.data.data + } else { + throw new Error(response.data?.message || '返回数据格式异常') + } + } catch (err) { + error.value = `加载列配置失败: ${err.message}` + // 出错时赋空数组 + targetRef.value = [] + } finally { + loading.value = false + } +} + + +export const getsubtable = async (tableName, targetRef, originalRef, whereClause) => { + try { + loading.value = true + error.value = null + + const params = { + tableName: tableName || 'v_bs_business_container', + pkColumn: 'subid', + whereClause, + } + const response = await axios.get('/pagdetail', { params }) + if (response.data?.success) { + const rawData = JSON.parse(JSON.stringify(response.data.data.records || [])) + rawData.forEach(row => { + if (row.mx_sh !== undefined) { + row.mx_sh = Number(row.mx_sh) // 确保是 0 或 1 + } + if (row.mx_checkkp !== undefined) { + row.mx_checkkp = Number(row.mx_checkkp) // 确保是 0 或 1 + } + }) + + targetRef.value = JSON.parse(JSON.stringify(rawData)) + originalRef.value = JSON.parse(JSON.stringify(rawData)) + } else { + throw new Error(response.data?.message || '返回数据格式异常') + } + } catch (err) { + error.value = `加载数据失败: ${err.message}` + return { data: [], total: 0 } + } finally { + loading.value = false + } +} + +// 获取主表数据 + + +export default { + getTableColumns, getMainTable, getSubTableMerger, fetchTableColumnsEdit, getsubtable +} + + +// 导出单例 diff --git a/code/link/link-frontend/src/utils/feeSummary.js b/code/link/link-frontend/src/utils/feeSummary.js new file mode 100644 index 00000000..bf4c9160 --- /dev/null +++ b/code/link/link-frontend/src/utils/feeSummary.js @@ -0,0 +1,50 @@ +// 工具函数:按币种汇总 +function sumByCurrency(data) { + const result = {} + data.forEach(row => { + result[row.mx_moneytype_id] = (result[row.mx_moneytype_id] || 0) + row.mx_amount_root + }) + return result +} + +// 工具函数:折合人民币 +function sumInRMB(data) { + return data.reduce((sum, row) => sum + row.mx_amount_root * row.mx_exrate, 0) +} + +// 主函数:计算应收、应付、利润 +export function calcFeeSummary(receivable = [], payable = []) { + console.log("进入======") + const receivableSummary = { + byCurrency: sumByCurrency(receivable), + rmb: sumInRMB(receivable) + } + + const payableSummary = { + byCurrency: sumByCurrency(payable), + rmb: sumInRMB(payable) + } + + // 计算利润(逐个币种相减 + RMB 总和) + const profitByCurrency = {} + const allCurrencies = new Set([ + ...Object.keys(receivableSummary.byCurrency), + ...Object.keys(payableSummary.byCurrency) + ]) + allCurrencies.forEach(cur => { + profitByCurrency[cur] = + (receivableSummary.byCurrency[cur] || 0) - + (payableSummary.byCurrency[cur] || 0) + }) + + const profitSummary = { + byCurrency: profitByCurrency, + rmb: receivableSummary.rmb - payableSummary.rmb + } + + return { + receivable: receivableSummary, + payable: payableSummary, + profit: profitSummary, + } +} diff --git a/code/link/link-frontend/src/utils/useTableHeaderSortable.js b/code/link/link-frontend/src/utils/useTableHeaderSortable.js new file mode 100644 index 00000000..32e5eb5d --- /dev/null +++ b/code/link/link-frontend/src/utils/useTableHeaderSortable.js @@ -0,0 +1,24 @@ +import Sortable from "sortablejs" + +export function useTableHeaderSortable(columnsRef, options = {}) { + const initSortable = () => { + const headerRow = document.querySelector(".el-table__header-wrapper tr") + if (!headerRow) return + + Sortable.create(headerRow, { + animation: 150, + handle: "th", + ...options, + onEnd(evt) { + const oldIndex = evt.oldIndex - 1 // 跳过 selection 列 + const newIndex = evt.newIndex - 1 + if (oldIndex >= 0 && newIndex >= 0) { + const moved = columnsRef.value.splice(oldIndex, 1)[0] + columnsRef.value.splice(newIndex, 0, moved) + } + } + }) + } + + return { initSortable } +} diff --git a/code/link/link-frontend/src/views/im/index.vue b/code/link/link-frontend/src/views/im/index.vue new file mode 100644 index 00000000..48798c9d --- /dev/null +++ b/code/link/link-frontend/src/views/im/index.vue @@ -0,0 +1,227 @@ + + + + + diff --git a/code/link/link-frontend/test.html b/code/link/link-frontend/test.html new file mode 100644 index 00000000..361f58bd --- /dev/null +++ b/code/link/link-frontend/test.html @@ -0,0 +1,125 @@ + + + + + 列表示例 + + + + +

布局 1:时间在主信息下面

+
+
+
+
箱号:123456
+
完成时间:2025-10-11 14:30
+
+
+ 已完成 +
+
+
+ +

布局 2:时间在右侧 extra 区

+
+
+
箱号:123457
+
+
2025-10-11 15:00
+ 未完成 +
+
+
+ +

布局 3:时间与状态在同一行,右侧对齐

+
+
+
箱号:123458
+
+ 已完成 +
2025-10-11 16:00
+
+
+
+ +

布局 4:多行布局,箱号 + 时间在左,状态在右

+
+
+
+
箱号:123459
+
完成时间:2025-10-11 17:30
+
+
+ 未完成 +
+
+
+ + + diff --git a/code/link/link-fronted/vite.config.js b/code/link/link-frontend/vite.config.js similarity index 80% rename from code/link/link-fronted/vite.config.js rename to code/link/link-frontend/vite.config.js index f232b650..33920b27 100644 --- a/code/link/link-fronted/vite.config.js +++ b/code/link/link-frontend/vite.config.js @@ -7,7 +7,7 @@ export default defineConfig({ plugins: [vue()], resolve: { alias: { - '@': path.resolve(__dirname, 'src'), - }, + '@': path.resolve(__dirname, './src') + } }, })