From a85f9b0120bbe68633e9e6ef8edd69cdca8f5170 Mon Sep 17 00:00:00 2001 From: ZhangAo Date: Fri, 28 Aug 2026 17:31:45 +0800 Subject: [PATCH] 20260828173144 --- .../fmsapi/database/OrgDataSourceFactory.java | 7 +- .../fmsapi/database/OrgDataSourceManager.java | 153 +- .../database/OrgDatabaseConfigLoader.java | 25 +- .../database/OrgDatabaseConfigWatcher.java | 141 + .../database/OrgDatabaseProperties.java | 18 + .../src/main/resources/application.yaml | 7 +- .../database/OrgDataSourceManagerTests.java | 175 + .../OrgDatabaseConfigWatcherTests.java | 138 + .../fms-api/target/classes/application.yaml | 7 +- .../database/OrgDataSourceFactory.class | Bin 3544 -> 3544 bytes .../OrgDataSourceManager$PoolEntry.class | Bin 0 -> 1425 bytes .../database/OrgDataSourceManager.class | Bin 3877 -> 8785 bytes .../database/OrgDatabaseConfigLoader.class | Bin 5924 -> 6468 bytes .../database/OrgDatabaseConfigWatcher.class | Bin 0 -> 6943 bytes .../database/OrgDatabaseProperties$Pool.class | Bin 3036 -> 3036 bytes .../database/OrgDatabaseProperties.class | Bin 1298 -> 1975 bytes .../compile/default-compile/createdFiles.lst | 2 + .../compile/default-compile/inputFiles.lst | 1 + .../default-testCompile/createdFiles.lst | 14 +- .../default-testCompile/inputFiles.lst | 2 + .../2026-08-28T14-29-00_642.dumpstream | 5 + .../2026-08-28T14-30-18_735.dumpstream | 5 + .../2026-08-28T14-30-47_680.dumpstream | 5 + .../2026-08-28T14-31-11_898.dumpstream | 5 + .../2026-08-28T14-53-51_760.dumpstream | 5 + .../2026-08-28T14-54-41_379.dumpstream | 5 + .../2026-08-28T14-54-59_867.dumpstream | 5 + .../2026-08-28T15-10-34_347.dumpstream | 5 + .../2026-08-28T15-14-07_984.dumpstream | 5 + .../2026-08-28T15-18-38_687.dumpstream | 5 + .../2026-08-28T15-28-11_237.dumpstream | 5 + ...n.g3soft.fmsapi.FmsApiApplicationTests.xml | 68 +- ...api.database.OrgDataSourceFactoryTests.xml | 57 +- ...api.database.OrgDataSourceManagerTests.xml | 279 ++ ....database.OrgDatabaseConfigLoaderTests.xml | 45 +- ...database.OrgDatabaseConfigWatcherTests.xml | 80 + ....exception.GlobalExceptionHandlerTests.xml | 51 +- ...api.service.ActiveSessionRegistryTests.xml | 49 +- ...g3soft.fmsapi.service.AuthServiceTests.xml | 47 +- ...ft.fmsapi.service.DataSaveServiceTests.xml | 55 +- ...T-cn.g3soft.fmsapi.utils.JwtUtilsTests.xml | 47 +- ...tils.snowflake.DefaultIdGeneratorTests.xml | 57 +- ...n.g3soft.fmsapi.FmsApiApplicationTests.txt | 2 +- ...api.database.OrgDataSourceFactoryTests.txt | 9 +- ...api.database.OrgDataSourceManagerTests.txt | 4 + ....database.OrgDatabaseConfigLoaderTests.txt | 2 +- ...database.OrgDatabaseConfigWatcherTests.txt | 4 + ....exception.GlobalExceptionHandlerTests.txt | 2 +- ...api.service.ActiveSessionRegistryTests.txt | 2 +- ...g3soft.fmsapi.service.AuthServiceTests.txt | 2 +- ...ft.fmsapi.service.DataSaveServiceTests.txt | 2 +- .../cn.g3soft.fmsapi.utils.JwtUtilsTests.txt | 2 +- ...tils.snowflake.DefaultIdGeneratorTests.txt | 2 +- .../OrgDataSourceManagerTests$1.class | Bin 0 -> 1973 bytes .../database/OrgDataSourceManagerTests.class | Bin 0 -> 7388 bytes .../OrgDatabaseConfigWatcherTests$1.class | Bin 0 -> 1740 bytes .../OrgDatabaseConfigWatcherTests.class | Bin 0 -> 7213 bytes .../fms/fms-vue/src/layouts/DefaultLayout.vue | 39 +- .../fms/fms-vue/src/views/dashboard/index.vue | 5 +- .../components/ModuleTableEditPanel.vue | 2614 -------------- .../components/edit-panels/EditDataView.vue | 81 + .../components/edit-panels/FieldLibrary.vue | 233 ++ .../components/form-edit/FormEditPanel.vue | 3177 +++++++++++++++++ .../components/form-edit/FormInspector.vue | 381 ++ .../components/form-edit/useFormGrid.js | 102 + .../components/table-edit/TableEditPanel.vue | 929 +++++ .../components/table-edit/TableInspector.vue | 450 +++ .../components/table-edit/TablePreview.vue | 520 +++ .../components/table-edit/useColumnResize.js | 72 + .../table-edit/useTablePreviewDrag.js | 124 + .../views/system/module-management/index.vue | 2 +- .../unit/module-table-edit-panel-dnd.spec.js | 2 +- code/fms/重构进度.md | 82 + 73 files changed, 7491 insertions(+), 2935 deletions(-) create mode 100644 code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/database/OrgDatabaseConfigWatcher.java create mode 100644 code/fms/fms-api/src/test/java/cn/g3soft/fmsapi/database/OrgDataSourceManagerTests.java create mode 100644 code/fms/fms-api/src/test/java/cn/g3soft/fmsapi/database/OrgDatabaseConfigWatcherTests.java create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgDataSourceManager$PoolEntry.class create mode 100644 code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgDatabaseConfigWatcher.class create mode 100644 code/fms/fms-api/target/surefire-reports/2026-08-28T14-29-00_642.dumpstream create mode 100644 code/fms/fms-api/target/surefire-reports/2026-08-28T14-30-18_735.dumpstream create mode 100644 code/fms/fms-api/target/surefire-reports/2026-08-28T14-30-47_680.dumpstream create mode 100644 code/fms/fms-api/target/surefire-reports/2026-08-28T14-31-11_898.dumpstream create mode 100644 code/fms/fms-api/target/surefire-reports/2026-08-28T14-53-51_760.dumpstream create mode 100644 code/fms/fms-api/target/surefire-reports/2026-08-28T14-54-41_379.dumpstream create mode 100644 code/fms/fms-api/target/surefire-reports/2026-08-28T14-54-59_867.dumpstream create mode 100644 code/fms/fms-api/target/surefire-reports/2026-08-28T15-10-34_347.dumpstream create mode 100644 code/fms/fms-api/target/surefire-reports/2026-08-28T15-14-07_984.dumpstream create mode 100644 code/fms/fms-api/target/surefire-reports/2026-08-28T15-18-38_687.dumpstream create mode 100644 code/fms/fms-api/target/surefire-reports/2026-08-28T15-28-11_237.dumpstream create mode 100644 code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.database.OrgDataSourceManagerTests.xml create mode 100644 code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.database.OrgDatabaseConfigWatcherTests.xml create mode 100644 code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.database.OrgDataSourceManagerTests.txt create mode 100644 code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.database.OrgDatabaseConfigWatcherTests.txt create mode 100644 code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/database/OrgDataSourceManagerTests$1.class create mode 100644 code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/database/OrgDataSourceManagerTests.class create mode 100644 code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/database/OrgDatabaseConfigWatcherTests$1.class create mode 100644 code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/database/OrgDatabaseConfigWatcherTests.class delete mode 100644 code/fms/fms-vue/src/views/system/module-management/components/ModuleTableEditPanel.vue create mode 100644 code/fms/fms-vue/src/views/system/module-management/components/edit-panels/EditDataView.vue create mode 100644 code/fms/fms-vue/src/views/system/module-management/components/edit-panels/FieldLibrary.vue create mode 100644 code/fms/fms-vue/src/views/system/module-management/components/form-edit/FormEditPanel.vue create mode 100644 code/fms/fms-vue/src/views/system/module-management/components/form-edit/FormInspector.vue create mode 100644 code/fms/fms-vue/src/views/system/module-management/components/form-edit/useFormGrid.js create mode 100644 code/fms/fms-vue/src/views/system/module-management/components/table-edit/TableEditPanel.vue create mode 100644 code/fms/fms-vue/src/views/system/module-management/components/table-edit/TableInspector.vue create mode 100644 code/fms/fms-vue/src/views/system/module-management/components/table-edit/TablePreview.vue create mode 100644 code/fms/fms-vue/src/views/system/module-management/components/table-edit/useColumnResize.js create mode 100644 code/fms/fms-vue/src/views/system/module-management/components/table-edit/useTablePreviewDrag.js create mode 100644 code/fms/重构进度.md diff --git a/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/database/OrgDataSourceFactory.java b/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/database/OrgDataSourceFactory.java index 87b65396..573af505 100644 --- a/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/database/OrgDataSourceFactory.java +++ b/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/database/OrgDataSourceFactory.java @@ -28,9 +28,10 @@ public class OrgDataSourceFactory { dataSource.setTestWhileIdle(poolProperties.isTestWhileIdle()); dataSource.setTestOnBorrow(poolProperties.isTestOnBorrow()); dataSource.setTestOnReturn(poolProperties.isTestOnReturn()); - dataSource.setConnectionErrorRetryAttempts(3); - dataSource.setBreakAfterAcquireFailure(false); - dataSource.setFailFast(false); + // 连接失败快速失败,不做重试:重试没有意义,等下次请求再触发建连 + dataSource.setConnectionErrorRetryAttempts(0); + dataSource.setBreakAfterAcquireFailure(true); + dataSource.setFailFast(true); // 定期 ping 空闲连接,对抗云 NAT/防火墙回收空闲 TCP 会话(默认 120s) dataSource.setKeepAlive(true); dataSource.setKeepAliveBetweenTimeMillis(120_000); diff --git a/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/database/OrgDataSourceManager.java b/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/database/OrgDataSourceManager.java index f75308e0..ccb04053 100644 --- a/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/database/OrgDataSourceManager.java +++ b/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/database/OrgDataSourceManager.java @@ -1,19 +1,38 @@ package cn.g3soft.fmsapi.database; +import cn.g3soft.fmsapi.exception.AuthenticationException; import com.alibaba.druid.pool.DruidDataSource; import jakarta.annotation.PreDestroy; import jakarta.annotation.Resource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import java.sql.Connection; import java.sql.SQLException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +/** + * 机构连接池注册表:按机构码懒加载并缓存连接池,定期检测配置文件变更。 + * 配置(url/密码等)变更后无需重启,到期自动重建连接池并延迟关闭旧池, + * 保证在途请求有足够时间归还旧连接。 + */ @Component public class OrgDataSourceManager { - private final ConcurrentMap dataSources = new ConcurrentHashMap<>(); + private static final Logger log = LoggerFactory.getLogger(OrgDataSourceManager.class); + + private final ConcurrentMap dataSources = new ConcurrentHashMap<>(); + private final ScheduledExecutorService retiredPoolCloser = Executors.newSingleThreadScheduledExecutor(task -> { + Thread thread = new Thread(task, "org-datasource-retired-close"); + thread.setDaemon(true); + return thread; + }); @Resource private OrgDatabaseConfigLoader configLoader; @@ -21,9 +40,16 @@ public class OrgDataSourceManager { @Resource private OrgDataSourceFactory dataSourceFactory; + @Resource + private OrgDatabaseProperties properties; + public DruidDataSource getDataSource(String orgId) { String normalizedOrgId = configLoader.normalizeOrgId(orgId); - return dataSources.computeIfAbsent(normalizedOrgId, this::createDataSource); + PoolEntry entry = dataSources.computeIfAbsent(normalizedOrgId, this::createEntry); + if (tryClaimRefreshCheck(entry)) { + return refreshIfConfigChanged(normalizedOrgId, entry).dataSource(); + } + return entry.dataSource(); } public Connection getConnection(String orgId) throws SQLException { @@ -36,11 +62,11 @@ public class OrgDataSourceManager { public boolean closeDataSource(String orgId) { String normalizedOrgId = configLoader.normalizeOrgId(orgId); - DruidDataSource dataSource = dataSources.remove(normalizedOrgId); - if (dataSource == null) { + PoolEntry entry = dataSources.remove(normalizedOrgId); + if (entry == null) { return false; } - dataSource.close(); + entry.dataSource().close(); return true; } @@ -50,12 +76,123 @@ public class OrgDataSourceManager { @PreDestroy public void closeAll() { - dataSources.values().forEach(DruidDataSource::close); + retiredPoolCloser.shutdownNow(); + dataSources.values().forEach(entry -> entry.dataSource().close()); dataSources.clear(); } - private DruidDataSource createDataSource(String orgId) { + /** + * 配置文件变更事件回调:立即核对并切换连接池(仅对已缓存的机构生效)。 + * 由配置监听线程调用,配置读取与建池的 I/O 不占用请求线程。 + */ + public void onConfigChanged(String orgId) { + String normalizedOrgId = configLoader.normalizeOrgId(orgId); + PoolEntry entry = dataSources.get(normalizedOrgId); + if (entry == null) { + return; + } + try { + refreshIfConfigChanged(normalizedOrgId, entry); + } catch (AuthenticationException exception) { + // 配置文件已删除,旧池已在 refreshIfConfigChanged 中淘汰 + } + } + + /** + * TTL 到期后由一个线程认领检测任务(CAS 防止并发重复 stat 文件)。 + */ + private boolean tryClaimRefreshCheck(PoolEntry entry) { + long intervalNanos = properties.getRefreshCheckInterval().toNanos(); + if (intervalNanos <= 0) { + return false; + } + long last = entry.checkedAtNanos.get(); + long now = System.nanoTime(); + if (now - last < intervalNanos) { + return false; + } + return entry.checkedAtNanos.compareAndSet(last, now); + } + + private PoolEntry refreshIfConfigChanged(String orgId, PoolEntry entry) { + long lastModified = configLoader.lastModified(orgId); + if (lastModified == entry.configLastModified()) { + return entry; + } + PoolEntry fresh; + try { + fresh = createEntry(orgId); + } catch (AuthenticationException exception) { + // 配置文件已不存在:机构已下线,淘汰旧池,按"机构码错误"处理 + if (dataSources.remove(orgId, entry)) { + scheduleClose(entry.dataSource()); + } + throw exception; + } catch (RuntimeException exception) { + // 配置读取或建池瞬时失败:保留旧池,下个检测周期重试 + log.warn("刷新机构数据库连接池失败,继续使用旧配置: {}", orgId, exception); + return entry; + } + if (fresh.config().equals(entry.config())) { + // 内容未变(仅时间戳被 touch):避免无谓重建 + entry.configLastModified(fresh.configLastModified()); + fresh.dataSource().close(); + return entry; + } + if (dataSources.replace(orgId, entry, fresh)) { + scheduleClose(entry.dataSource()); + log.info("机构数据库配置已变更,已切换到新连接池: {}", orgId); + return fresh; + } + // 并发刷新:其他线程已完成切换或已淘汰旧条目,丢弃本次多余的池 + fresh.dataSource().close(); + PoolEntry current = dataSources.get(orgId); + if (current != null) { + return current; + } + // 旧条目被并发淘汰且无新条目(如配置文件删除后又被重建):退回懒加载语义 + return dataSources.computeIfAbsent(orgId, this::createEntry); + } + + private PoolEntry createEntry(String orgId) { + // 先取时间戳再加载:若加载期间文件又被修改,记录的是旧时间戳,下个周期能检测到 + long lastModified = configLoader.lastModified(orgId); OrgDatabaseConfig config = configLoader.load(orgId); - return dataSourceFactory.create(orgId, config); + return new PoolEntry(dataSourceFactory.create(orgId, config), config, lastModified); + } + + private void scheduleClose(DruidDataSource dataSource) { + long delayMillis = properties.getRetiredPoolCloseDelay().toMillis(); + retiredPoolCloser.schedule(dataSource::close, delayMillis, TimeUnit.MILLISECONDS); + } + + private static final class PoolEntry { + + private final DruidDataSource dataSource; + private final OrgDatabaseConfig config; + private volatile long configLastModified; + private final AtomicLong checkedAtNanos = new AtomicLong(System.nanoTime()); + + private PoolEntry(DruidDataSource dataSource, OrgDatabaseConfig config, long configLastModified) { + this.dataSource = dataSource; + this.config = config; + this.configLastModified = configLastModified; + } + + private DruidDataSource dataSource() { + return dataSource; + } + + private OrgDatabaseConfig config() { + return config; + } + + private long configLastModified() { + return configLastModified; + } + + private void configLastModified(long configLastModified) { + this.configLastModified = configLastModified; + } } } diff --git a/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/database/OrgDatabaseConfigLoader.java b/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/database/OrgDatabaseConfigLoader.java index 82cafdf7..6aa81d2c 100644 --- a/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/database/OrgDatabaseConfigLoader.java +++ b/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/database/OrgDatabaseConfigLoader.java @@ -35,10 +35,7 @@ public class OrgDatabaseConfigLoader { public OrgDatabaseConfig load(String orgId) { String normalizedOrgId = normalizeOrgId(orgId); - Path configFile = configDir.resolve(normalizedOrgId + ".properties").normalize(); - if (!configFile.startsWith(configDir)) { - throw new IllegalArgumentException("机构配置文件路径不合法"); - } + Path configFile = resolveConfigFile(normalizedOrgId); if (!Files.isRegularFile(configFile)) { throw new AuthenticationException("机构码错误"); } @@ -80,6 +77,26 @@ public class OrgDatabaseConfigLoader { return configDir; } + /** + * 配置文件修改时间戳,用于检测配置变更。文件不存在时返回 -1。 + */ + public long lastModified(String orgId) { + Path configFile = resolveConfigFile(normalizeOrgId(orgId)); + try { + return Files.getLastModifiedTime(configFile).toMillis(); + } catch (IOException exception) { + return -1L; + } + } + + private Path resolveConfigFile(String normalizedOrgId) { + Path configFile = configDir.resolve(normalizedOrgId + ".properties").normalize(); + if (!configFile.startsWith(configDir)) { + throw new IllegalArgumentException("机构配置文件路径不合法"); + } + return configFile; + } + private String required(Properties properties, String key, Path configFile) { String value = properties.getProperty(key); if (value == null || value.isBlank()) { diff --git a/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/database/OrgDatabaseConfigWatcher.java b/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/database/OrgDatabaseConfigWatcher.java new file mode 100644 index 00000000..2b60bd31 --- /dev/null +++ b/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/database/OrgDatabaseConfigWatcher.java @@ -0,0 +1,141 @@ +package cn.g3soft.fmsapi.database; + +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import jakarta.annotation.Resource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.nio.file.ClosedWatchServiceException; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.nio.file.StandardWatchEventKinds; +import java.nio.file.WatchEvent; +import java.nio.file.WatchKey; +import java.nio.file.WatchService; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 监听机构配置目录,配置文件变更时立即通知 {@link OrgDataSourceManager} 重建连接池, + * 使配置变更毫秒级生效,配置读取与建池的 I/O 全部发生在监听线程,不占用请求线程。 + * 监听注册失败、事件溢出或目录失效时自动降级为定期轮询兜底,不影响服务。 + */ +@Component +public class OrgDatabaseConfigWatcher { + + private static final Logger log = LoggerFactory.getLogger(OrgDatabaseConfigWatcher.class); + private static final Pattern CONFIG_FILE_PATTERN = + Pattern.compile("([A-Za-z0-9_-]{1,32})\\.properties"); + private static final long DEBOUNCE_MILLIS = 500; + + @Resource + private OrgDatabaseConfigLoader configLoader; + + @Resource + private OrgDataSourceManager dataSourceManager; + + private WatchService watchService; + private volatile Thread watcherThread; + + @PostConstruct + public void start() { + Path configDir = configLoader.getConfigDir(); + try { + watchService = FileSystems.getDefault().newWatchService(); + configDir.register( + watchService, + StandardWatchEventKinds.ENTRY_CREATE, + StandardWatchEventKinds.ENTRY_MODIFY, + StandardWatchEventKinds.ENTRY_DELETE + ); + } catch (IOException exception) { + log.warn("注册机构配置目录监听失败,配置变更检测降级为定期轮询: {}", configDir, exception); + closeWatchService(); + return; + } + Thread thread = new Thread(this::watch, "org-dbconfig-watcher"); + thread.setDaemon(true); + this.watcherThread = thread; + thread.start(); + log.info("已启动机构配置目录监听: {}", configDir); + } + + @PreDestroy + public void stop() { + Thread thread = watcherThread; + if (thread != null) { + thread.interrupt(); + } + closeWatchService(); + } + + private void watch() { + try { + while (true) { + WatchKey key = watchService.take(); + Set orgIds = new HashSet<>(); + collect(key, orgIds); + // 合并安静期内的后续事件(编辑器保存常触发多次写入),避免重复重建连接池 + long deadline = System.currentTimeMillis() + DEBOUNCE_MILLIS; + while (System.currentTimeMillis() < deadline) { + WatchKey more = watchService.poll( + deadline - System.currentTimeMillis(), TimeUnit.MILLISECONDS); + if (more == null) { + break; + } + collect(more, orgIds); + } + orgIds.forEach(this::notifyChanged); + } + } catch (ClosedWatchServiceException | InterruptedException exception) { + if (exception instanceof InterruptedException) { + Thread.currentThread().interrupt(); + } + // 正常退出:服务关闭或线程中断,轮询兜底仍然生效 + log.debug("机构配置目录监听线程退出"); + } + } + + private void collect(WatchKey key, Set orgIds) { + for (WatchEvent event : key.pollEvents()) { + if (event.kind() == StandardWatchEventKinds.OVERFLOW) { + continue; + } + if (event.context() instanceof Path path) { + Matcher matcher = CONFIG_FILE_PATTERN.matcher(path.getFileName().toString()); + if (matcher.matches()) { + orgIds.add(matcher.group(1).toUpperCase(Locale.ROOT)); + } + } + } + if (!key.reset()) { + log.warn("机构配置目录已不可用,文件监听停止,配置变更检测降级为定期轮询"); + closeWatchService(); + } + } + + private void notifyChanged(String orgId) { + try { + dataSourceManager.onConfigChanged(orgId); + } catch (RuntimeException exception) { + log.warn("处理机构配置变更事件失败: {}", orgId, exception); + } + } + + private void closeWatchService() { + if (watchService != null) { + try { + watchService.close(); + } catch (IOException ignored) { + // 关闭失败无需处理 + } + } + } +} diff --git a/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/database/OrgDatabaseProperties.java b/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/database/OrgDatabaseProperties.java index 2be6561e..e1fb43a6 100644 --- a/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/database/OrgDatabaseProperties.java +++ b/code/fms/fms-api/src/main/java/cn/g3soft/fmsapi/database/OrgDatabaseProperties.java @@ -9,6 +9,8 @@ import java.time.Duration; public class OrgDatabaseProperties { private Path configDir = Path.of("./config/dbconfigs"); + private Duration refreshCheckInterval = Duration.ofSeconds(30); + private Duration retiredPoolCloseDelay = Duration.ofSeconds(60); private final Pool pool = new Pool(); public Path getConfigDir() { @@ -19,6 +21,22 @@ public class OrgDatabaseProperties { this.configDir = configDir; } + public Duration getRefreshCheckInterval() { + return refreshCheckInterval; + } + + public void setRefreshCheckInterval(Duration refreshCheckInterval) { + this.refreshCheckInterval = refreshCheckInterval; + } + + public Duration getRetiredPoolCloseDelay() { + return retiredPoolCloseDelay; + } + + public void setRetiredPoolCloseDelay(Duration retiredPoolCloseDelay) { + this.retiredPoolCloseDelay = retiredPoolCloseDelay; + } + public Pool getPool() { return pool; } diff --git a/code/fms/fms-api/src/main/resources/application.yaml b/code/fms/fms-api/src/main/resources/application.yaml index 91ab78f5..2759dfc7 100644 --- a/code/fms/fms-api/src/main/resources/application.yaml +++ b/code/fms/fms-api/src/main/resources/application.yaml @@ -13,8 +13,13 @@ fms: jwt-expiration: 24h database: config-dir: ./config/dbconfigs + # 机构配置文件变更检测周期(文件监听的兜底,正常由监听毫秒级触发;设为 0s 关闭轮询) + refresh-check-interval: 30s + # 配置变更切换后,旧连接池延迟关闭时间,等待在途请求归还连接 + retired-pool-close-delay: 60s pool: - initial-size: 0 + # 建池时先建 1 条物理连接:配置指向不可达的库时快速失败,保留旧池不切换 + initial-size: 1 min-idle: 0 max-active: 10 max-wait: 5000 diff --git a/code/fms/fms-api/src/test/java/cn/g3soft/fmsapi/database/OrgDataSourceManagerTests.java b/code/fms/fms-api/src/test/java/cn/g3soft/fmsapi/database/OrgDataSourceManagerTests.java new file mode 100644 index 00000000..ea609c00 --- /dev/null +++ b/code/fms/fms-api/src/test/java/cn/g3soft/fmsapi/database/OrgDataSourceManagerTests.java @@ -0,0 +1,175 @@ +package cn.g3soft.fmsapi.database; + +import cn.g3soft.fmsapi.exception.AuthenticationException; +import com.alibaba.druid.pool.DruidDataSource; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.springframework.test.util.ReflectionTestUtils; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.FileTime; +import java.sql.SQLException; +import java.time.Duration; +import java.time.Instant; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class OrgDataSourceManagerTests { + + @TempDir + Path configDir; + + private String failPassword; + + @Test + void unreachableNewConfigKeepsOldPool() throws Exception { + OrgDataSourceManager manager = newManager(Duration.ofSeconds(30), Duration.ofMillis(200)); + try { + writeConfig("old-password"); + DruidDataSource first = manager.getDataSource("TEST"); + + failPassword = "unreachable-password"; + writeConfig("unreachable-password"); + bumpMtime(); + manager.onConfigChanged("TEST"); + + // 新配置建池失败(fail-fast):保留旧池继续服务,等下次再试 + assertThat(manager.getDataSource("TEST")).isSameAs(first); + assertThat(first.isClosed()).isFalse(); + } finally { + manager.closeAll(); + } + } + + @Test + void configChangeSwitchesToNewDataSource() throws Exception { + OrgDataSourceManager manager = newManager(Duration.ofMillis(50), Duration.ofMillis(200)); + try { + writeConfig("old-password"); + DruidDataSource first = manager.getDataSource("TEST"); + assertThat(manager.getDataSource("TEST")).isSameAs(first); + + writeConfig("new-password"); + bumpMtime(); + + Thread.sleep(150); + DruidDataSource second = manager.getDataSource("TEST"); + assertThat(second).isNotSameAs(first); + assertThat(second.getUsername()).isEqualTo("test"); + + // 旧池延迟关闭 + Thread.sleep(400); + assertThat(first.isClosed()).isTrue(); + assertThat(second.isClosed()).isFalse(); + } finally { + manager.closeAll(); + } + } + + @Test + void untouchedMtimeReusesPool() throws Exception { + OrgDataSourceManager manager = newManager(Duration.ofMillis(50), Duration.ofMillis(200)); + try { + writeConfig("password"); + DruidDataSource first = manager.getDataSource("TEST"); + + Thread.sleep(150); + assertThat(manager.getDataSource("TEST")).isSameAs(first); + assertThat(first.isClosed()).isFalse(); + } finally { + manager.closeAll(); + } + } + + @Test + void missingConfigFileEvictsPool() throws Exception { + OrgDataSourceManager manager = newManager(Duration.ofMillis(50), Duration.ofMillis(200)); + try { + writeConfig("password"); + DruidDataSource first = manager.getDataSource("TEST"); + + Files.delete(configDir.resolve("TEST.properties")); + Thread.sleep(150); + + assertThatThrownBy(() -> manager.getDataSource("TEST")) + .isInstanceOf(AuthenticationException.class); + assertThat(manager.getDataSourceCount()).isZero(); + } finally { + manager.closeAll(); + } + } + + @Test + void configChangeEventSwitchesPoolImmediately() throws Exception { + OrgDataSourceManager manager = newManager(Duration.ofSeconds(30), Duration.ofMillis(200)); + try { + writeConfig("old-password"); + DruidDataSource first = manager.getDataSource("TEST"); + + writeConfig("new-password"); + bumpMtime(); + manager.onConfigChanged("test"); // 模拟监听线程的事件回调(小写验证归一化) + + DruidDataSource second = manager.getDataSource("TEST"); + assertThat(second).isNotSameAs(first); + assertThat(first.isClosed()).isFalse(); // 延迟关闭,在途请求仍可用 + } finally { + manager.closeAll(); + } + } + + private OrgDataSourceManager newManager(Duration refreshInterval, Duration closeDelay) { + OrgDatabaseProperties properties = new OrgDatabaseProperties(); + properties.setConfigDir(configDir); + properties.setRefreshCheckInterval(refreshInterval); + properties.setRetiredPoolCloseDelay(closeDelay); + + OrgDatabaseConfigLoader loader = new OrgDatabaseConfigLoader(); + ReflectionTestUtils.setField(loader, "properties", properties); + loader.init(); + + OrgDataSourceFactory factory = new OrgDataSourceFactory() { + @Override + public DruidDataSource create(String orgId, OrgDatabaseConfig config) { + if (config.password().equals(failPassword)) { + throw new IllegalStateException("模拟新库不可达"); + } + // 测试不真实连接数据库(initialSize=0 时 init 不建物理连接) + DruidDataSource dataSource = new DruidDataSource(); + dataSource.setName(orgId); + dataSource.setUrl(config.url()); + dataSource.setUsername(config.username()); + dataSource.setDriverClassName(config.driver()); + try { + dataSource.init(); + } catch (SQLException exception) { + throw new IllegalStateException(exception); + } + return dataSource; + } + }; + + OrgDataSourceManager manager = new OrgDataSourceManager(); + ReflectionTestUtils.setField(manager, "configLoader", loader); + ReflectionTestUtils.setField(manager, "dataSourceFactory", factory); + ReflectionTestUtils.setField(manager, "properties", properties); + return manager; + } + + private void writeConfig(String password) throws Exception { + Files.writeString(configDir.resolve("TEST.properties"), """ + url=jdbc:sqlserver://127.0.0.1:1433;databaseName=test + username=test + password=%s + """.formatted(password)); + } + + private void bumpMtime() throws Exception { + Files.setLastModifiedTime( + configDir.resolve("TEST.properties"), + FileTime.from(Instant.now().plusSeconds(60)) + ); + } +} diff --git a/code/fms/fms-api/src/test/java/cn/g3soft/fmsapi/database/OrgDatabaseConfigWatcherTests.java b/code/fms/fms-api/src/test/java/cn/g3soft/fmsapi/database/OrgDatabaseConfigWatcherTests.java new file mode 100644 index 00000000..f304e960 --- /dev/null +++ b/code/fms/fms-api/src/test/java/cn/g3soft/fmsapi/database/OrgDatabaseConfigWatcherTests.java @@ -0,0 +1,138 @@ +package cn.g3soft.fmsapi.database; + +import cn.g3soft.fmsapi.exception.AuthenticationException; +import com.alibaba.druid.pool.DruidDataSource; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.springframework.test.util.ReflectionTestUtils; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.FileTime; +import java.sql.SQLException; +import java.time.Duration; +import java.time.Instant; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class OrgDatabaseConfigWatcherTests { + + private static final long AWAIT_MILLIS = 5000; + + @TempDir + Path configDir; + + private OrgDataSourceManager manager; + private OrgDatabaseConfigWatcher watcher; + + @Test + void fileChangeTriggersPoolSwitchWithoutPolling() throws Exception { + start(Duration.ofMinutes(10)); + try { + writeConfig("TEST", "old-password"); + DruidDataSource first = manager.getDataSource("TEST"); + + writeConfig("TEST", "new-password"); + bumpMtime("TEST"); + + DruidDataSource current = first; + long deadline = System.currentTimeMillis() + AWAIT_MILLIS; + while (current == first && System.currentTimeMillis() < deadline) { + Thread.sleep(100); + current = manager.getDataSource("TEST"); + } + assertThat(current).isNotSameAs(first); + assertThat(current.getUsername()).isEqualTo("test"); + } finally { + stop(); + } + } + + @Test + void renamingOrgCodeEvictsOldOrgAndServesNewOne() throws Exception { + start(Duration.ofMinutes(10)); + try { + writeConfig("G3HD", "old-password"); + manager.getDataSource("G3HD"); + + Files.move(configDir.resolve("G3HD.properties"), configDir.resolve("NEWORG.properties")); + + // 删除事件应淘汰旧机构码的池(而不是像改造前一样悄悄继续用旧库) + long deadline = System.currentTimeMillis() + AWAIT_MILLIS; + while (manager.getDataSourceCount() > 0 && System.currentTimeMillis() < deadline) { + Thread.sleep(100); + } + assertThat(manager.getDataSourceCount()).isZero(); + assertThatThrownBy(() -> manager.getDataSource("G3HD")) + .isInstanceOf(AuthenticationException.class); + + // 新机构码走懒加载,正常建池 + assertThat(manager.getDataSource("NEWORG")).isNotNull(); + } finally { + stop(); + } + } + + private void start(Duration refreshInterval) { + OrgDatabaseProperties properties = new OrgDatabaseProperties(); + properties.setConfigDir(configDir); + properties.setRefreshCheckInterval(refreshInterval); + properties.setRetiredPoolCloseDelay(Duration.ofMillis(200)); + + OrgDatabaseConfigLoader loader = new OrgDatabaseConfigLoader(); + ReflectionTestUtils.setField(loader, "properties", properties); + loader.init(); + + OrgDataSourceFactory factory = new OrgDataSourceFactory() { + @Override + public DruidDataSource create(String orgId, OrgDatabaseConfig config) { + DruidDataSource dataSource = new DruidDataSource(); + dataSource.setName(orgId); + dataSource.setUrl(config.url()); + dataSource.setUsername(config.username()); + dataSource.setDriverClassName(config.driver()); + try { + dataSource.init(); + } catch (SQLException exception) { + throw new IllegalStateException(exception); + } + return dataSource; + } + }; + + manager = new OrgDataSourceManager(); + ReflectionTestUtils.setField(manager, "configLoader", loader); + ReflectionTestUtils.setField(manager, "dataSourceFactory", factory); + ReflectionTestUtils.setField(manager, "properties", properties); + + watcher = new OrgDatabaseConfigWatcher(); + ReflectionTestUtils.setField(watcher, "configLoader", loader); + ReflectionTestUtils.setField(watcher, "dataSourceManager", manager); + watcher.start(); + } + + private void stop() { + if (watcher != null) { + watcher.stop(); + } + if (manager != null) { + manager.closeAll(); + } + } + + private void writeConfig(String orgId, String password) throws Exception { + Files.writeString(configDir.resolve(orgId + ".properties"), """ + url=jdbc:sqlserver://127.0.0.1:1433;databaseName=test + username=test + password=%s + """.formatted(password)); + } + + private void bumpMtime(String orgId) throws Exception { + Files.setLastModifiedTime( + configDir.resolve(orgId + ".properties"), + FileTime.from(Instant.now().plusSeconds(60)) + ); + } +} diff --git a/code/fms/fms-api/target/classes/application.yaml b/code/fms/fms-api/target/classes/application.yaml index 91ab78f5..2759dfc7 100644 --- a/code/fms/fms-api/target/classes/application.yaml +++ b/code/fms/fms-api/target/classes/application.yaml @@ -13,8 +13,13 @@ fms: jwt-expiration: 24h database: config-dir: ./config/dbconfigs + # 机构配置文件变更检测周期(文件监听的兜底,正常由监听毫秒级触发;设为 0s 关闭轮询) + refresh-check-interval: 30s + # 配置变更切换后,旧连接池延迟关闭时间,等待在途请求归还连接 + retired-pool-close-delay: 60s pool: - initial-size: 0 + # 建池时先建 1 条物理连接:配置指向不可达的库时快速失败,保留旧池不切换 + initial-size: 1 min-idle: 0 max-active: 10 max-wait: 5000 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgDataSourceFactory.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgDataSourceFactory.class index f9db64e88c3c465e11182bc2fca37dc909b0f33d..b8506ba5a2a24ffa68de98d5fe9a58e8ce4bd288 100644 GIT binary patch delta 91 zcmca1eM5SKGY>EGHileD7M5)c1we9g0MBe&1%^%rMTUL`C5EXCDh#t3R2ddCs4*;K sP-j>Jq}MZOGHhbdV%PzscQfcQ9AMCAILu(caFoH2;S__>WDVX80P9Q@ZU6uP delta 91 zcmca1eM5SKGY>D@HileD7UpdX1we9g0MBe&d4^5~1%`eGMTV&i$_%p^R2UXBs4^^L sP-9pFq}MZOFl=JbWY__ucQfcR9AMC6ILx5WaFoG-;S_`6WDVX80O^4hUjP6A diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgDataSourceManager$PoolEntry.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgDataSourceManager$PoolEntry.class new file mode 100644 index 0000000000000000000000000000000000000000..a75146988bbdcf3734a5ace6189c4781b32447a1 GIT binary patch literal 1425 zcmb7ETTc@~6#k}HsLQP=0-``gY?sTR_+Z44AZV17O9CO`ak@J#i|sDiotp6CKhl8G zU?kB8eey>c&rAslffUnZ&Y78W&i9>f&i2>uuipSH;7JBC#M4MvNFv41y(``cUKM_Y zua$PCqZm@pJ>OIF4DoznD~&dWaT8QmUX?q6@6^Lk`ict`)I5hT7*h=VN(LF+NW-$w zfzETKH})eXYYc6^@PkdSCQb3>4ASVFh}7t#lXEDnUjJ`Qr7O znA02vEDYifLq_8@F?0ndsBuyCN<@LXVcm229wG82?fzwBD%`bj55puT30C$h3{%CH z&FID!O}HsBY9WXF4B2LhqKMQ=;Cf|Gx(sZYVf)&Q{tKJlfu3VAaOsu0ik>f5>$Q>$ zH$|yRC)r})i0YOIJ-t6YOsE}?G=A0^=ZXiLqj6c0YA0~lMJQ@g$&l*V`NDNIXW%-x z)x7*8F}-e+%qvg#HE`KPP3xJVv0assdL2Y6jYkaAEm6)j$zZ+leHkuRMHETus{L$C zF+;N!qwg|-tR6V>vU+UDzR+)P_Vgu?O;TJEW3)b?-FxzrWOMd0n0p)FM?yKIs-G}~<rK9@33XH&hXM6v%2%Gh)Wm zoHWmzVHKxXpd(HJAiDZkgA8JV!ZkfkRwpLyBlI7bOU)o<4v!6*)eJaAi&2?LI=PrU zbTRql(&QXE&yr)!!h}5X;R5paE0TLoRh>tie#FxrKBt`i%LIOq;y+D=KC@4qDU~v+ f(6~}mk{LDPm@?qgq=nXI29L>Rh+8lDvl#pX8iYV; literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgDataSourceManager.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgDataSourceManager.class index d8f8f8717f161277ad448be893bbee0c9af7561d..c3c80f8ddd4699547759021404f25974d86c61d3 100644 GIT binary patch literal 8785 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 delta 1504 zcmZ8hOLP-e82;|$G0EgM2~FFy#WX-;Xp+7H1+*0uERU+C#Q=hq$B>3JP?I*9qT(Yy zz&DR@!RK-5js+ey*!no`JcorIj|-QsUAWVw9{)QtCdG3yxv%ej-~GS)&HOz6X^(d2 zpOx1u^ny(S9ZQs$@vh)gB%ZGCxf$6IGL(?NAZ}7Nsj%P5`U?Cx}!M2@wiQy)5{v#aggH>4vRlqrrbw3 zj^ar%;h*r&;FyZz94By+VNmG)LGh!1DZt>(6!JygSSpkJNwLT}YCpAz>DG3U3B<)G zt-p%jLQw?^*V>M^XtTPZXG%-uEMdiTAQ5M1$ryU}?4-VMP(L-FQ}HZA@Qi*=ADY*T z`Js$aE)?@(rfobD%$9SyksDt{;m($drwe&TcBYknHYPsqIMNr~RVo=3qpUAX=8U=0 zY$b@%fD0GpMZC`O0un*_drJ(5ySy%p1@SIJN}eW^I2>@}vW6?Ts-nhm4dU~~@EYb& z@t&x4CKP-izUkatbxxJU!}nu3N&OgR|Jt{Cy2~JZ6x^xf~h*% zCaI(KCs)u7EThMP?-9F200m9vE_i8KGOx2X+r&hJGdN2EUbJDJdJ3Kqs_5q!mDqDbQho8UkKhphyTO;Yv*bi+_m3x>up%qZCe+ni!-o9gy#tF1N$2h70;-M-Cgu^ z^yy({(IeE#qBVJm%hTE0zDD=B0kh+-GR3iik-Jfgsw znr3K5&2I)^3&nUVebu(DB9GYQ|Ao8yym=IQlWe6!v{gngWZRmN(R(7*U&m9;ELc$< zB^$>~ZYyx1hHIgQYZ_j}OGMoWoD7I{=`YJSY{b0xS4i+xdStFHk^F18fH%zEn|K@V Q;5w~oWWWu)j}OuHFXAcT-~a#s diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgDatabaseConfigLoader.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgDatabaseConfigLoader.class index dacb3502fa70d7246d89b6399925e44154c51c01..9120d513ecc0f764eebfb9ece38809bf6a5038a6 100644 GIT binary patch delta 3228 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 diff --git a/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgDatabaseConfigWatcher.class b/code/fms/fms-api/target/classes/cn/g3soft/fmsapi/database/OrgDatabaseConfigWatcher.class new file mode 100644 index 0000000000000000000000000000000000000000..9c18dc1402bb5924638abb071ddb0ec110959f1e GIT binary patch literal 6943 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 H>>&3a=-IO# delta 419 zcmZvYO-{m46otRn(vr5shKf{CP!WG9YW*8Hbp<+g4<-y;0BcY;B0+-D=mH!$bK+Wz z=M`d%aq#ZD=iPJ9J@-xG+c13ldU^%UI39)_$bzJgT`M6=|2uiWTHFt*zGEb)V-&( Ue=Vs4J=pI{IaC%FS;7&)H{*gMBLDyZ diff --git a/code/fms/fms-api/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/code/fms/fms-api/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst index 702d8937..3248525f 100644 --- a/code/fms/fms-api/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +++ b/code/fms/fms-api/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -2,6 +2,7 @@ cn\g3soft\fmsapi\service\LoginLogService.class cn\g3soft\fmsapi\controller\FileController.class cn\g3soft\fmsapi\utils\ParamUtils.class cn\g3soft\fmsapi\utils\JwtUtils.class +cn\g3soft\fmsapi\database\OrgDataSourceManager$PoolEntry.class cn\g3soft\fmsapi\FmsApiApplication.class cn\g3soft\fmsapi\utils\DbUtils$TableMetadata.class cn\g3soft\fmsapi\utils\JwtUtils$TokenClaims.class @@ -40,6 +41,7 @@ cn\g3soft\fmsapi\utils\DbUtils.class cn\g3soft\fmsapi\exception\SaveObjectException.class cn\g3soft\fmsapi\service\AuthService.class cn\g3soft\fmsapi\config\AuthProperties.class +cn\g3soft\fmsapi\database\OrgDatabaseConfigWatcher.class cn\g3soft\fmsapi\database\OrgRoutingDataSource.class cn\g3soft\fmsapi\utils\ApiResponse.class cn\g3soft\fmsapi\utils\snowflake\core\SnowWorkerM2.class diff --git a/code/fms/fms-api/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/code/fms/fms-api/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst index 38bed198..ff26c2f6 100644 --- a/code/fms/fms-api/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +++ b/code/fms/fms-api/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -8,6 +8,7 @@ D:\workspace\code\fms\fms-api\src\main\java\cn\g3soft\fmsapi\controller\FileCont D:\workspace\code\fms\fms-api\src\main\java\cn\g3soft\fmsapi\database\OrgContext.java D:\workspace\code\fms\fms-api\src\main\java\cn\g3soft\fmsapi\database\OrgDatabaseConfig.java D:\workspace\code\fms\fms-api\src\main\java\cn\g3soft\fmsapi\database\OrgDatabaseConfigLoader.java +D:\workspace\code\fms\fms-api\src\main\java\cn\g3soft\fmsapi\database\OrgDatabaseConfigWatcher.java D:\workspace\code\fms\fms-api\src\main\java\cn\g3soft\fmsapi\database\OrgDatabaseProperties.java D:\workspace\code\fms\fms-api\src\main\java\cn\g3soft\fmsapi\database\OrgDataSourceFactory.java D:\workspace\code\fms\fms-api\src\main\java\cn\g3soft\fmsapi\database\OrgDataSourceManager.java diff --git a/code/fms/fms-api/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst b/code/fms/fms-api/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst index 567edb45..544190ff 100644 --- a/code/fms/fms-api/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst +++ b/code/fms/fms-api/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst @@ -1,10 +1,14 @@ -cn\g3soft\fmsapi\database\OrgDatabaseConfigLoaderTests.class -cn\g3soft\fmsapi\utils\snowflake\DefaultIdGeneratorTests.class -cn\g3soft\fmsapi\database\OrgDataSourceFactoryTests.class cn\g3soft\fmsapi\service\AuthServiceTests.class cn\g3soft\fmsapi\utils\JwtUtilsTests.class +cn\g3soft\fmsapi\database\OrgDataSourceManagerTests$1.class +cn\g3soft\fmsapi\service\DataSaveServiceTests$JdbcFixture.class +cn\g3soft\fmsapi\service\DataSaveServiceTests.class +cn\g3soft\fmsapi\database\OrgDatabaseConfigLoaderTests.class +cn\g3soft\fmsapi\utils\snowflake\DefaultIdGeneratorTests.class +cn\g3soft\fmsapi\database\OrgDatabaseConfigWatcherTests$1.class +cn\g3soft\fmsapi\database\OrgDataSourceFactoryTests.class +cn\g3soft\fmsapi\database\OrgDatabaseConfigWatcherTests.class +cn\g3soft\fmsapi\database\OrgDataSourceManagerTests.class cn\g3soft\fmsapi\exception\GlobalExceptionHandlerTests.class cn\g3soft\fmsapi\FmsApiApplicationTests.class -cn\g3soft\fmsapi\service\DataSaveServiceTests$JdbcFixture.class cn\g3soft\fmsapi\service\ActiveSessionRegistryTests.class -cn\g3soft\fmsapi\service\DataSaveServiceTests.class diff --git a/code/fms/fms-api/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst b/code/fms/fms-api/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst index 8fe569d9..4ffc47d6 100644 --- a/code/fms/fms-api/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst +++ b/code/fms/fms-api/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst @@ -1,5 +1,7 @@ D:\workspace\code\fms\fms-api\src\test\java\cn\g3soft\fmsapi\database\OrgDatabaseConfigLoaderTests.java +D:\workspace\code\fms\fms-api\src\test\java\cn\g3soft\fmsapi\database\OrgDatabaseConfigWatcherTests.java D:\workspace\code\fms\fms-api\src\test\java\cn\g3soft\fmsapi\database\OrgDataSourceFactoryTests.java +D:\workspace\code\fms\fms-api\src\test\java\cn\g3soft\fmsapi\database\OrgDataSourceManagerTests.java D:\workspace\code\fms\fms-api\src\test\java\cn\g3soft\fmsapi\exception\GlobalExceptionHandlerTests.java D:\workspace\code\fms\fms-api\src\test\java\cn\g3soft\fmsapi\FmsApiApplicationTests.java D:\workspace\code\fms\fms-api\src\test\java\cn\g3soft\fmsapi\service\ActiveSessionRegistryTests.java diff --git a/code/fms/fms-api/target/surefire-reports/2026-08-28T14-29-00_642.dumpstream b/code/fms/fms-api/target/surefire-reports/2026-08-28T14-29-00_642.dumpstream new file mode 100644 index 00000000..585140ba --- /dev/null +++ b/code/fms/fms-api/target/surefire-reports/2026-08-28T14-29-00_642.dumpstream @@ -0,0 +1,5 @@ +# Created at 2026-08-28T14:29:04.429 +Boot Manifest-JAR contains absolute paths in classpath 'D:\workspace\code\fms\fms-api\target\test-classes' +Hint: -Djdk.net.URLClassPath.disableClassPathURLCheck=true +'other' has different root + diff --git a/code/fms/fms-api/target/surefire-reports/2026-08-28T14-30-18_735.dumpstream b/code/fms/fms-api/target/surefire-reports/2026-08-28T14-30-18_735.dumpstream new file mode 100644 index 00000000..613ae82b --- /dev/null +++ b/code/fms/fms-api/target/surefire-reports/2026-08-28T14-30-18_735.dumpstream @@ -0,0 +1,5 @@ +# Created at 2026-08-28T14:30:19.263 +Boot Manifest-JAR contains absolute paths in classpath 'D:\workspace\code\fms\fms-api\target\test-classes' +Hint: -Djdk.net.URLClassPath.disableClassPathURLCheck=true +'other' has different root + diff --git a/code/fms/fms-api/target/surefire-reports/2026-08-28T14-30-47_680.dumpstream b/code/fms/fms-api/target/surefire-reports/2026-08-28T14-30-47_680.dumpstream new file mode 100644 index 00000000..a59c75ac --- /dev/null +++ b/code/fms/fms-api/target/surefire-reports/2026-08-28T14-30-47_680.dumpstream @@ -0,0 +1,5 @@ +# Created at 2026-08-28T14:30:48.153 +Boot Manifest-JAR contains absolute paths in classpath 'D:\workspace\code\fms\fms-api\target\test-classes' +Hint: -Djdk.net.URLClassPath.disableClassPathURLCheck=true +'other' has different root + diff --git a/code/fms/fms-api/target/surefire-reports/2026-08-28T14-31-11_898.dumpstream b/code/fms/fms-api/target/surefire-reports/2026-08-28T14-31-11_898.dumpstream new file mode 100644 index 00000000..a0df49e2 --- /dev/null +++ b/code/fms/fms-api/target/surefire-reports/2026-08-28T14-31-11_898.dumpstream @@ -0,0 +1,5 @@ +# Created at 2026-08-28T14:31:12.371 +Boot Manifest-JAR contains absolute paths in classpath 'D:\workspace\code\fms\fms-api\target\test-classes' +Hint: -Djdk.net.URLClassPath.disableClassPathURLCheck=true +'other' has different root + diff --git a/code/fms/fms-api/target/surefire-reports/2026-08-28T14-53-51_760.dumpstream b/code/fms/fms-api/target/surefire-reports/2026-08-28T14-53-51_760.dumpstream new file mode 100644 index 00000000..f64d613f --- /dev/null +++ b/code/fms/fms-api/target/surefire-reports/2026-08-28T14-53-51_760.dumpstream @@ -0,0 +1,5 @@ +# Created at 2026-08-28T14:53:52.332 +Boot Manifest-JAR contains absolute paths in classpath 'D:\workspace\code\fms\fms-api\target\test-classes' +Hint: -Djdk.net.URLClassPath.disableClassPathURLCheck=true +'other' has different root + diff --git a/code/fms/fms-api/target/surefire-reports/2026-08-28T14-54-41_379.dumpstream b/code/fms/fms-api/target/surefire-reports/2026-08-28T14-54-41_379.dumpstream new file mode 100644 index 00000000..b358d67b --- /dev/null +++ b/code/fms/fms-api/target/surefire-reports/2026-08-28T14-54-41_379.dumpstream @@ -0,0 +1,5 @@ +# Created at 2026-08-28T14:54:41.751 +Boot Manifest-JAR contains absolute paths in classpath 'D:\workspace\code\fms\fms-api\target\test-classes' +Hint: -Djdk.net.URLClassPath.disableClassPathURLCheck=true +'other' has different root + diff --git a/code/fms/fms-api/target/surefire-reports/2026-08-28T14-54-59_867.dumpstream b/code/fms/fms-api/target/surefire-reports/2026-08-28T14-54-59_867.dumpstream new file mode 100644 index 00000000..33eddefb --- /dev/null +++ b/code/fms/fms-api/target/surefire-reports/2026-08-28T14-54-59_867.dumpstream @@ -0,0 +1,5 @@ +# Created at 2026-08-28T14:55:00.351 +Boot Manifest-JAR contains absolute paths in classpath 'D:\workspace\code\fms\fms-api\target\test-classes' +Hint: -Djdk.net.URLClassPath.disableClassPathURLCheck=true +'other' has different root + diff --git a/code/fms/fms-api/target/surefire-reports/2026-08-28T15-10-34_347.dumpstream b/code/fms/fms-api/target/surefire-reports/2026-08-28T15-10-34_347.dumpstream new file mode 100644 index 00000000..a1a5c89d --- /dev/null +++ b/code/fms/fms-api/target/surefire-reports/2026-08-28T15-10-34_347.dumpstream @@ -0,0 +1,5 @@ +# Created at 2026-08-28T15:10:35.538 +Boot Manifest-JAR contains absolute paths in classpath 'D:\workspace\code\fms\fms-api\target\test-classes' +Hint: -Djdk.net.URLClassPath.disableClassPathURLCheck=true +'other' has different root + diff --git a/code/fms/fms-api/target/surefire-reports/2026-08-28T15-14-07_984.dumpstream b/code/fms/fms-api/target/surefire-reports/2026-08-28T15-14-07_984.dumpstream new file mode 100644 index 00000000..020367fc --- /dev/null +++ b/code/fms/fms-api/target/surefire-reports/2026-08-28T15-14-07_984.dumpstream @@ -0,0 +1,5 @@ +# Created at 2026-08-28T15:14:08.649 +Boot Manifest-JAR contains absolute paths in classpath 'D:\workspace\code\fms\fms-api\target\test-classes' +Hint: -Djdk.net.URLClassPath.disableClassPathURLCheck=true +'other' has different root + diff --git a/code/fms/fms-api/target/surefire-reports/2026-08-28T15-18-38_687.dumpstream b/code/fms/fms-api/target/surefire-reports/2026-08-28T15-18-38_687.dumpstream new file mode 100644 index 00000000..34c67e2c --- /dev/null +++ b/code/fms/fms-api/target/surefire-reports/2026-08-28T15-18-38_687.dumpstream @@ -0,0 +1,5 @@ +# Created at 2026-08-28T15:18:39.321 +Boot Manifest-JAR contains absolute paths in classpath 'D:\workspace\code\fms\fms-api\target\test-classes' +Hint: -Djdk.net.URLClassPath.disableClassPathURLCheck=true +'other' has different root + diff --git a/code/fms/fms-api/target/surefire-reports/2026-08-28T15-28-11_237.dumpstream b/code/fms/fms-api/target/surefire-reports/2026-08-28T15-28-11_237.dumpstream new file mode 100644 index 00000000..4cd6e1c3 --- /dev/null +++ b/code/fms/fms-api/target/surefire-reports/2026-08-28T15-28-11_237.dumpstream @@ -0,0 +1,5 @@ +# Created at 2026-08-28T15:28:12.719 +Boot Manifest-JAR contains absolute paths in classpath 'D:\workspace\code\fms\fms-api\target\test-classes' +Hint: -Djdk.net.URLClassPath.disableClassPathURLCheck=true +'other' has different root + diff --git a/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.FmsApiApplicationTests.xml b/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.FmsApiApplicationTests.xml index 505bb764..79bc274f 100644 --- a/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.FmsApiApplicationTests.xml +++ b/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.FmsApiApplicationTests.xml @@ -1,30 +1,30 @@ - + - - + + - + - + - - + + - + - + - - + + @@ -33,44 +33,43 @@ - + - - + + - - - - - + + + + - + - + - - + + - - + - + - - + + - + - + - - + + - + - + - - + + - + - - + + - - - - - + + + + - + - - + + - - - + \ No newline at end of file diff --git a/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.database.OrgDataSourceManagerTests.xml b/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.database.OrgDataSourceManagerTests.xml new file mode 100644 index 00000000..31a1afd5 --- /dev/null +++ b/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.database.OrgDataSourceManagerTests.xml @@ -0,0 +1,279 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.database.OrgDatabaseConfigLoaderTests.xml b/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.database.OrgDatabaseConfigLoaderTests.xml index 258069e8..38b3719c 100644 --- a/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.database.OrgDatabaseConfigLoaderTests.xml +++ b/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.database.OrgDatabaseConfigLoaderTests.xml @@ -1,63 +1,62 @@ - + - - + + - + - + - - + + - + - + - - + + - + - - + + - - - - - + + + + - + - - + + - + \ No newline at end of file diff --git a/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.database.OrgDatabaseConfigWatcherTests.xml b/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.database.OrgDatabaseConfigWatcherTests.xml new file mode 100644 index 00000000..377ee16b --- /dev/null +++ b/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.database.OrgDatabaseConfigWatcherTests.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.exception.GlobalExceptionHandlerTests.xml b/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.exception.GlobalExceptionHandlerTests.xml index 2f6072d1..ed3d225c 100644 --- a/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.exception.GlobalExceptionHandlerTests.xml +++ b/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.exception.GlobalExceptionHandlerTests.xml @@ -1,66 +1,65 @@ - + - - + + - + - + - - + + - + - + - - + + - + - - + + - - - - - + + + + - + - - + + - - + - - + - + - - + + - + - + - - + + - + - + - - + + @@ -33,38 +33,37 @@ - + - - + + - - - - - + + + + - + - + - - + + - - + + \ No newline at end of file diff --git a/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.service.AuthServiceTests.xml b/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.service.AuthServiceTests.xml index 1ff8e3d6..8263ee21 100644 --- a/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.service.AuthServiceTests.xml +++ b/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.service.AuthServiceTests.xml @@ -1,30 +1,30 @@ - + - - + + - + - + - - + + - + - + - - + + @@ -33,37 +33,36 @@ - + - - + + - - - - - + + + + - + - + - - + + - + \ No newline at end of file diff --git a/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.service.DataSaveServiceTests.xml b/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.service.DataSaveServiceTests.xml index 327ee952..581cde61 100644 --- a/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.service.DataSaveServiceTests.xml +++ b/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.service.DataSaveServiceTests.xml @@ -1,30 +1,30 @@ - + - - + + - + - + - - + + - + - + - - + + @@ -33,41 +33,40 @@ - + - - + + - - - - - + + + + - + - + - - + + - - - - - + + + + + \ No newline at end of file diff --git a/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.utils.JwtUtilsTests.xml b/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.utils.JwtUtilsTests.xml index cc6c9c6f..d455d8dd 100644 --- a/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.utils.JwtUtilsTests.xml +++ b/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.utils.JwtUtilsTests.xml @@ -1,30 +1,30 @@ - + - - + + - + - + - - + + - + - + - - + + @@ -33,37 +33,36 @@ - + - - + + - - - - - + + + + - + - + - - + + - + \ No newline at end of file diff --git a/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.utils.snowflake.DefaultIdGeneratorTests.xml b/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.utils.snowflake.DefaultIdGeneratorTests.xml index f786d7db..fa912327 100644 --- a/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.utils.snowflake.DefaultIdGeneratorTests.xml +++ b/code/fms/fms-api/target/surefire-reports/TEST-cn.g3soft.fmsapi.utils.snowflake.DefaultIdGeneratorTests.xml @@ -1,30 +1,30 @@ - + - - + + - + - + - - + + - + - + - - + + @@ -33,42 +33,41 @@ - + - - + + - - - - - + + + + - + - + - - + + - - - - - - + + + + + + \ No newline at end of file diff --git a/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.FmsApiApplicationTests.txt b/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.FmsApiApplicationTests.txt index 4530b8a7..7a2d0b43 100644 --- a/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.FmsApiApplicationTests.txt +++ b/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.FmsApiApplicationTests.txt @@ -1,4 +1,4 @@ ------------------------------------------------------------------------------- Test set: cn.g3soft.fmsapi.FmsApiApplicationTests ------------------------------------------------------------------------------- -Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.644 s -- in cn.g3soft.fmsapi.FmsApiApplicationTests +Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 33.85 s -- in cn.g3soft.fmsapi.FmsApiApplicationTests diff --git a/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.database.OrgDataSourceFactoryTests.txt b/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.database.OrgDataSourceFactoryTests.txt index 54dd7516..79efa0bb 100644 --- a/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.database.OrgDataSourceFactoryTests.txt +++ b/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.database.OrgDataSourceFactoryTests.txt @@ -1,11 +1,4 @@ ------------------------------------------------------------------------------- Test set: cn.g3soft.fmsapi.database.OrgDataSourceFactoryTests ------------------------------------------------------------------------------- -Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.145 s <<< FAILURE! -- in cn.g3soft.fmsapi.database.OrgDataSourceFactoryTests -cn.g3soft.fmsapi.database.OrgDataSourceFactoryTests.databaseConnectionFailureDoesNotRetry -- Time elapsed: 0.144 s <<< FAILURE! -org.opentest4j.AssertionFailedError: - -expected: 0 - but was: 3 - at cn.g3soft.fmsapi.database.OrgDataSourceFactoryTests.databaseConnectionFailureDoesNotRetry(OrgDataSourceFactoryTests.java:25) - +Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.050 s -- in cn.g3soft.fmsapi.database.OrgDataSourceFactoryTests diff --git a/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.database.OrgDataSourceManagerTests.txt b/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.database.OrgDataSourceManagerTests.txt new file mode 100644 index 00000000..d3783dec --- /dev/null +++ b/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.database.OrgDataSourceManagerTests.txt @@ -0,0 +1,4 @@ +------------------------------------------------------------------------------- +Test set: cn.g3soft.fmsapi.database.OrgDataSourceManagerTests +------------------------------------------------------------------------------- +Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.608 s -- in cn.g3soft.fmsapi.database.OrgDataSourceManagerTests diff --git a/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.database.OrgDatabaseConfigLoaderTests.txt b/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.database.OrgDatabaseConfigLoaderTests.txt index d03a4e93..5154356d 100644 --- a/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.database.OrgDatabaseConfigLoaderTests.txt +++ b/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.database.OrgDatabaseConfigLoaderTests.txt @@ -1,4 +1,4 @@ ------------------------------------------------------------------------------- Test set: cn.g3soft.fmsapi.database.OrgDatabaseConfigLoaderTests ------------------------------------------------------------------------------- -Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.139 s -- in cn.g3soft.fmsapi.database.OrgDatabaseConfigLoaderTests +Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.646 s -- in cn.g3soft.fmsapi.database.OrgDatabaseConfigLoaderTests diff --git a/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.database.OrgDatabaseConfigWatcherTests.txt b/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.database.OrgDatabaseConfigWatcherTests.txt new file mode 100644 index 00000000..c706cc83 --- /dev/null +++ b/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.database.OrgDatabaseConfigWatcherTests.txt @@ -0,0 +1,4 @@ +------------------------------------------------------------------------------- +Test set: cn.g3soft.fmsapi.database.OrgDatabaseConfigWatcherTests +------------------------------------------------------------------------------- +Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.088 s -- in cn.g3soft.fmsapi.database.OrgDatabaseConfigWatcherTests diff --git a/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.exception.GlobalExceptionHandlerTests.txt b/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.exception.GlobalExceptionHandlerTests.txt index 2d971bcc..fb2119ff 100644 --- a/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.exception.GlobalExceptionHandlerTests.txt +++ b/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.exception.GlobalExceptionHandlerTests.txt @@ -1,4 +1,4 @@ ------------------------------------------------------------------------------- Test set: cn.g3soft.fmsapi.exception.GlobalExceptionHandlerTests ------------------------------------------------------------------------------- -Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.014 s -- in cn.g3soft.fmsapi.exception.GlobalExceptionHandlerTests +Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.251 s -- in cn.g3soft.fmsapi.exception.GlobalExceptionHandlerTests diff --git a/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.service.ActiveSessionRegistryTests.txt b/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.service.ActiveSessionRegistryTests.txt index a8084c22..f35ef334 100644 --- a/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.service.ActiveSessionRegistryTests.txt +++ b/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.service.ActiveSessionRegistryTests.txt @@ -1,4 +1,4 @@ ------------------------------------------------------------------------------- Test set: cn.g3soft.fmsapi.service.ActiveSessionRegistryTests ------------------------------------------------------------------------------- -Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.003 s -- in cn.g3soft.fmsapi.service.ActiveSessionRegistryTests +Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.017 s -- in cn.g3soft.fmsapi.service.ActiveSessionRegistryTests diff --git a/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.service.AuthServiceTests.txt b/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.service.AuthServiceTests.txt index d7686c52..0be27a90 100644 --- a/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.service.AuthServiceTests.txt +++ b/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.service.AuthServiceTests.txt @@ -1,4 +1,4 @@ ------------------------------------------------------------------------------- Test set: cn.g3soft.fmsapi.service.AuthServiceTests ------------------------------------------------------------------------------- -Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.201 s -- in cn.g3soft.fmsapi.service.AuthServiceTests +Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.118 s -- in cn.g3soft.fmsapi.service.AuthServiceTests diff --git a/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.service.DataSaveServiceTests.txt b/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.service.DataSaveServiceTests.txt index 55cee72a..4b7c089b 100644 --- a/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.service.DataSaveServiceTests.txt +++ b/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.service.DataSaveServiceTests.txt @@ -1,4 +1,4 @@ ------------------------------------------------------------------------------- Test set: cn.g3soft.fmsapi.service.DataSaveServiceTests ------------------------------------------------------------------------------- -Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.434 s -- in cn.g3soft.fmsapi.service.DataSaveServiceTests +Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 7.593 s -- in cn.g3soft.fmsapi.service.DataSaveServiceTests diff --git a/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.utils.JwtUtilsTests.txt b/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.utils.JwtUtilsTests.txt index 42f67e43..d27834dc 100644 --- a/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.utils.JwtUtilsTests.txt +++ b/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.utils.JwtUtilsTests.txt @@ -1,4 +1,4 @@ ------------------------------------------------------------------------------- Test set: cn.g3soft.fmsapi.utils.JwtUtilsTests ------------------------------------------------------------------------------- -Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.114 s -- in cn.g3soft.fmsapi.utils.JwtUtilsTests +Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.991 s -- in cn.g3soft.fmsapi.utils.JwtUtilsTests diff --git a/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.utils.snowflake.DefaultIdGeneratorTests.txt b/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.utils.snowflake.DefaultIdGeneratorTests.txt index 45dec5f6..e13571f4 100644 --- a/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.utils.snowflake.DefaultIdGeneratorTests.txt +++ b/code/fms/fms-api/target/surefire-reports/cn.g3soft.fmsapi.utils.snowflake.DefaultIdGeneratorTests.txt @@ -1,4 +1,4 @@ ------------------------------------------------------------------------------- Test set: cn.g3soft.fmsapi.utils.snowflake.DefaultIdGeneratorTests ------------------------------------------------------------------------------- -Tests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.633 s -- in cn.g3soft.fmsapi.utils.snowflake.DefaultIdGeneratorTests +Tests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.258 s -- in cn.g3soft.fmsapi.utils.snowflake.DefaultIdGeneratorTests diff --git a/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/database/OrgDataSourceManagerTests$1.class b/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/database/OrgDataSourceManagerTests$1.class new file mode 100644 index 0000000000000000000000000000000000000000..87d0c86ead1cca794c354990502c89bb2ef0e896 GIT binary patch literal 1973 zcmbVN+fy4=9R5xcvPoDjB5f&XsTN}bhUL;)Y$}!#u+ea-0TjH=$>tEYZg$h%P0=@B zoYB!*$7*N#%!5Q^;HW84H10}OL=j@s9cl&;q{q3)Nn*b*8 zeiQ)&6@*kYAF9~NsxSrcL z%nz?g=MCQ*Mmf?*v1QW@n7+TS$pk2ii z=wKMwSD6fN(zaHO5<{fIU3cAfiVU5JWbO*T#x;{$B`xnchE+;O(TO7pj;c6@E{4&4 zl|RZCgSyHMbAH=U$D?Lq=thr%r&K(R6AbNp{h*M=)hah#h9ikx;&X*7Lif_i6$K|5 zdUwRKrYTC?%zNAuQy=Q0;u*FT!Lua4_2c(jpMSS?>(0Y_H-G>2`okMP{js?jLofPd zI-c8Op}|9PNx}0BaosL!+%yVQBU;g^8bz&Q+oqP0$DMMCA&#hA`c(`_m5M98SzZ=0 zS1A>PGEu>*V@k}BieZTf6J}9fwPIAoX$gr`UEx^1TpTZ|I4g0@61RQdikDQJli+aC zF|G+msywgaWeM#dXvV*tG>QJY2XVZj;)1NZkgOsF7b#?)Irplmu2 zUAt*tSaa-kUNA*Exx_G*I8+lz(a@w_6bx-S!xFRAa)DY*swK~Jw$9BZ?iljDaT$_b z$8h#A=K6-yR97>?TeFMv+#yaq;gGVh?g*+824i=CjjZo)Xs4Ib$=tz;gZzk!e@l|# zSiPM0a!;rx+bLzKF~fSJSyPYY4?|22vwA}^P?8Q*yJWb9+D89ihF2QA$Lcz!4Vjq} z|2r55Wf4&_r!3vHU78+swpF~1cgPt_tT)Ce!%Bl}&nOEmQ+2p(&vc!M|K1U+k2Rvn z{m3kh=}Wfj(d5ZmmT>&y7cNfH@Q=`UfVH>F@lU4!{hKjO`>S*sr(NE}`Uidi>)(L# zvyZt%YZ#2yPmrbcXdQ*u@H)K_-q*&EqjMAG?c;U=&tSHp_D=)}04%;xLrec%u)BD& zhU2%mtTza@#A6wNotu}4vCLr}A?faRe2ch@EL|w2HitL~#m zHZ27Ty<2+Gma8o-&;q4EK^Rhw-jvdl-q+Xrpa(taK@0t7SCS=LPVCyBwX2zVGw=QL z-v8c=pL+3;#{jg6#sJDtuAoB2EcgWGj%Y`OBtc4=4gYOt%tg;p&fkh zHPco^@6?jokZ$$qw!@_ZNqsciA}~-_N{do$_2HSV*6PO+0o9C0n^Kx>kD69gs(+@6 z8Y~m29JLHbZ#R=MV@O~@Rt*>FUdJ+$L#_4weyk9v=-b)b7r-jiDp;*z4b}=Y&rB78 z(}r|s91nr5rPd2a%tT0w8-s)}6t&VuG?X&UcxZ=w9hatI^#WDtq@`<-VQnz3=M9`q z0~=MGg(g}a(~Nje)^d7;hiRdK5{{6Hb<)vh52h2TPU$ex)D0>&N|5H5Ne`j+VY;AB z05{`o1zS{{gL4IznbuH`@R_(TOgBhLWb26ctbm zdzTisb-HWa3>RI69u+T_e<uOWNY`95HP0V;`N1F`W0hT-Wsh9Ke8rgDNh@B}L@b zH%wh8h|c-b8pKT0Aab)XkL8 zr*LBWmN_bE(A&dW_d5x^oy*8(ny?vfQSny1jj(7gJ4r%v$0fHD@S(cWE^^A5r_d

&N`_tPFR$& zYSF6vB@(X+bA5?IwiVxw#ry{!|Q8%atbjzFu*A)qJOKzv4leT1H9V2U+ zo_weUa2v|y$46D%DWgKQb;nDT3|iT7w~CM99>xPZ_UbW)-EjM`9yu%*w~lIb|6HUL z)o83ar1ZTi?vv7IxYCYc>Cqmh1vjbe(Bs-M1)mUDKP|02wZf(r)v4M2DEA}7ygbeu z0gT~c1&^pWflqQ)>0EY7RzNX}q|3VLa7YqjDD4>W5RHkm45hQmYHlB!82*0hxy#Vw zQ}MXWIu)|SRq!c+&8IYSa*0{HK7c3i8CgPq*7f)QV-9yG zR!OwJ!0q)2*#ps!FAB_)4&tr6iv@#e9rNQ$0zGLfzHKBr7-_MOF!NeRb*m*5TEAgS z^E&>nZ&|--vfZEmVbXA_+FEte9ut+oRJd>LO+@KqIG!`F*4s+V$@EMlgW z&>Tlr8*A$hmO!?kDVq@$JVWOyX7cWop6qs6NHYGnRD2uX$yLd@wJs^i?gjI+OtgCS zBWayn)0)*Kp9;oTAmJ1*?q;JqwEuJvqq$eu-Zx__d1P;I{(Hi*+NmmNA%i^pH$;eXNk_ zpE1i!j2Ev9f2ZR2_yZFvV<)WHPNx|)Vul{|76_jN={%q%W-bY=K=ob{ zG&S!xY(pl>WYTop6vNE^zXG$SrFl1}kZWVvNLD~>)rks_hB(%^P8O1tNTjila{ zP7LZ+pG>XtSYa}q_iL6Rzh@gOoMD4AZ=PX-myBX_vE>Cvn=XCSW3WA}8}076ON6W= zV}@nZAy`99@65#7C*hq!1=h__FRFA}ucJi{cWNouLIwZHRYLB0DxY#mtofvED|o4B zRK7=G=@ght1i0ENk8iv~SMR9dM22;{&mk@&C+wN#aWlo0n8(L6*yb#QQ+RHt?hKpJ z9!+LG1_GV9Hm)TGqgw4rfUI3dWoKT^_7kjZ3gNy8)O1gEXVNSgiDcGA_vqAR^ii?7 zJSjRh&#Ng4{`E7APED=i;ai%J21P}BpU2b!o)<_;T59^_@ zYaXZ2^1xNn_VI5|7uJheA#cSCwPcj#e{I+_52sVD1$iKwX9`-11pAJq^upoO@AQ^D zxoqJPF*~z73iOiMxW#HmGO1f`-K*P*SV@{HSx)C;+AB^cohlSjOXrx5D7VU;tl&tZ zh_wQ%C+$TkSQWv`o|=;nWzcwgfaN?!mhr45|9F;!z#sYMhpL3(#LhmUENx6I^UPFwFZl@jGrq|i%iF<9N0E^g!GVc`9GW-VFqCBLUe& zc+TdxE&MzeThW4cUeSd4b^zz&GF*TWUd^Q0dW=_M$N9X9&ug&L1+{^8*I<7Z)OKtT zWrVj}B9VtTJrUmHMezEkgtt5gFP`Bt(&NG<;C045t`Np)de}A^lk>1Jh|Bo z58!C<6%XTh8IN^WWkGlZuLr-rIe1+L?~uXu-Z9*i!TU?bS(p~(P&k|aO#m(;3Ijyp zAW^u4E^;YRI7AdQc~3`dqQpd}RForij*Jzc(VR!)U>=R#6Vd3MCK{5nJlc7QXvi2F zI2G;WqN-6wPL^=SqbeUlIm($BHytz${M+k33(04Mpu`DEf}ogmjTGtT2+7Ch1&G6o z>NH(rlmv7+B}dWE=K)5IBp)wM8neJ`MpZse2J#SfOoXVX2%@Rtq)h%_h5;{W{dq#A z@OkJ2ZaeT0?&xZgzLCL4n&<#`HPQ5spTPa>d7!1Dx+3@>spg^TiVPk-fhP_y6@0p7 z)(Na)|L0nK)xO~8t1ILHU+~H5S>B5$;k%b>cp1av^9 zJU1X~zwBxo_>`;p8{9lxFteB54;9ShcL7A+gcndD=1J{EP|W93>ivYM;u}{M)hHJW e#6tcpXUk%KTOn47)xO!hdU1cm8c`=2Q2jq9DhJa5 literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/database/OrgDatabaseConfigWatcherTests$1.class b/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/database/OrgDatabaseConfigWatcherTests$1.class new file mode 100644 index 0000000000000000000000000000000000000000..0c37f74f2981628288000c981905259c282373ce GIT binary patch literal 1740 zcmbVNT~`xV6y2AEObA0mRaDwqlp-cTGNPh2p|+Jkp@yIf#pUPnW^xJBPG<1Vjcwoh zZ~EH4gtf|A_T4|we^WhY5`<+R1oCjdW}kES+2@>_H~;8h%b*Vl3rQRONC_0( zz-eBM!cF6Bwj#OhISpxKT}H}T)6EL4*Tc4U)sl2gsDBek&3l1oZV4oE`3(zcfk`)P zInwv)Wacz<+iN)6Vdy&*UhaFyz(RtDK^r!P1guDznrtb7kzDmB`ARyz44Tfm(O%Fj z=QlFQV%Wl`HcsHA!1PC%GV6sf*z}sjuC0B6Q@Q;AJu`<98>g8=ia0#bK3{d##wg#D zZbwQ7#EWsC+xUWUgN%EBp2e3o#u%JxXz!KM4834uoS`QOT8U2=d>KU?A%C8*agha2 zvV0aUQPgqF(M#W1zgJ!S)m7WZ3j+&zfw6r#R(xMIrN3^ZQSU(+f@%+?akrGduObso%nHoss>igZ+bPn8utCYqR=q&g+O0ZyW@e;N zHFTxFA+^W*-eJ;gd67W*xK~chmMIWO!AXt*~)dYD%3^npmY=tw?_X z@1WSdN>TM z^98634%OkrW{B$4{drYx$62*dAvr(fMc#XFipSM3-g{^EyZ2xn7TKDLXfbg8FrtT{ zn;Z)(0ymCXoi^^EO3Fn>YD3!ga%#Mmaw=^txeenAPS>avmcxiZ_DT>a9e1>fa2^GE zM)}8MKOCAb)9NNf_hf1Z;;;DgER88T1|H%Hji-AiS8)4?YloZ|UR2cPkLu7mT14!-&W*-~;BxksbP9ZdaG z_>=ryL>@DkBwtKt3K<$7BaJ5*!c&ak8P4H3pZ~qVFxaOIj#cSg??_=p_^uCI{nq2l~h!WC`-S bNMj$C@I$;?#*bKyPnJ=mIYYM;Xr|vg>vPC) literal 0 HcmV?d00001 diff --git a/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/database/OrgDatabaseConfigWatcherTests.class b/code/fms/fms-api/target/test-classes/cn/g3soft/fmsapi/database/OrgDatabaseConfigWatcherTests.class new file mode 100644 index 0000000000000000000000000000000000000000..f6a9a4173740772978c8448812efb864c5409780 GIT binary patch literal 7213 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 Xs} -import { computed, defineComponent, h, onBeforeUnmount, onMounted, provide, ref, watch } from 'vue' +import { computed, defineComponent, h, provide, ref, watch } from 'vue' import { useRoute } from 'vue-router' import { useAppStore } from '@/stores/app' import { Splitter, SplitterPanel } from '@/components/ui' @@ -106,36 +106,6 @@ function toggleSidebar() { appStore.setSidebar(true) } provide('toggleSidebar', toggleSidebar) - -/* ---------- 内容可视高度(页面撑满用) ---------- - * 实测 .fms-content 可视高度(clientHeight - 纵向 padding),写入 CSS 变量 - * --fms-content-height(随 DOM 继承给页面所有后代)。 - * 需要「固定高度 + 内部滚动」的页面(如工具栏固定、表格内部滚动)直接写: - * height: var(--fms-content-height) - * 替代 calc(100vh - 116px) 这类魔数——顶栏/页签/padding 等布局内部变化时自动跟随, - * 页面无需改动。 */ -const contentRef = ref(null) -let contentResizeObserver = null - -function syncContentHeight() { - const el = contentRef.value - if (!el) return - const style = getComputedStyle(el) - const height = - el.clientHeight - parseFloat(style.paddingTop || '0') - parseFloat(style.paddingBottom || '0') - el.style.setProperty('--fms-content-height', `${height}px`) -} - -onMounted(() => { - // ResizeObserver 首次 observe 会立即回调一次,初始值无需额外同步 - contentResizeObserver = new ResizeObserver(syncContentHeight) - if (contentRef.value) contentResizeObserver.observe(contentRef.value) -}) - -onBeforeUnmount(() => { - contentResizeObserver?.disconnect() - contentResizeObserver = null -})