diff --git a/modules/call/src/main/java/com/bytedesk/call/freeswitch/FreeSwitchDataSourceConfig.java b/modules/call/src/main/java/com/bytedesk/call/freeswitch/FreeSwitchDataSourceConfig.java index 7f8581e147..d6678521a7 100644 --- a/modules/call/src/main/java/com/bytedesk/call/freeswitch/FreeSwitchDataSourceConfig.java +++ b/modules/call/src/main/java/com/bytedesk/call/freeswitch/FreeSwitchDataSourceConfig.java @@ -93,20 +93,26 @@ public class FreeSwitchDataSourceConfig { @Bean(name = "freeswitchEntityManagerFactory") public LocalContainerEntityManagerFactoryBean freeswitchEntityManagerFactory( EntityManagerFactoryBuilder builder, - @Qualifier("freeswitchDataSource") DataSource dataSource) { + @Qualifier("freeswitchDataSource") DataSource dataSource, + org.springframework.core.env.Environment env) { log.info("Creating FreeSWITCH EntityManagerFactory"); + + // 允许通过属性覆盖,默认在启用时对 bytedesk_freeswitch 执行表结构创建/更新 + // 支持值:none|validate|update|create|create-drop + String ddlAuto = env.getProperty("bytedesk.datasource.freeswitch.ddl-auto", "update"); + log.info("FreeSWITCH JPA ddl-auto={}", ddlAuto); + + java.util.Map jpaProps = new java.util.HashMap<>(); + jpaProps.put("hibernate.hbm2ddl.auto", ddlAuto); + jpaProps.put("hibernate.show_sql", "false"); + jpaProps.put("hibernate.format_sql", "false"); + jpaProps.put("hibernate.jdbc.time_zone", "Asia/Shanghai"); + return builder .dataSource(dataSource) - .packages("com.bytedesk.call.freeswitch") // FreeSWITCH 实体类所在包 + .packages("com.bytedesk.call.freeswitch") // 仅扫描 freeswitch 实体 .persistenceUnit("freeswitch") - .properties(java.util.Map.of( - // 只读取,不创建/更新表结构 - "hibernate.hbm2ddl.auto", "none", - "hibernate.show_sql", "false", - "hibernate.format_sql", "false", - // 设置 Hibernate 时区 - "hibernate.jdbc.time_zone", "Asia/Shanghai" - )) + .properties(jpaProps) .build(); } diff --git a/modules/call/src/main/java/com/bytedesk/call/freeswitch/FreeSwitchEntityScanCustomizer.java b/modules/call/src/main/java/com/bytedesk/call/freeswitch/FreeSwitchEntityScanCustomizer.java new file mode 100644 index 0000000000..2dcfb30429 --- /dev/null +++ b/modules/call/src/main/java/com/bytedesk/call/freeswitch/FreeSwitchEntityScanCustomizer.java @@ -0,0 +1,93 @@ +/* + * 目的:当 bytedesk.datasource.freeswitch.enabled 未开启时,将 com.bytedesk.call.freeswitch + * 从主库 entityManagerFactory 的 packagesToScan 中剔除,避免主库对 freeswitch 实体建表/写入。 + * 位置:freeswitch 模块内部;不修改全局 starter 配置,完全满足“在 freeswitch 文件夹里面配置”。 + */ +package com.bytedesk.call.freeswitch; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import org.springframework.beans.BeansException; +import org.springframework.beans.PropertyValue; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.config.TypedStringValue; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; + +@Configuration +@ConditionalOnProperty( + prefix = "bytedesk.datasource.freeswitch", + name = "enabled", + havingValue = "false", + matchIfMissing = true +) +public class FreeSwitchEntityScanCustomizer { + + private static final String PRIMARY_EMF_BEAN_NAME = "entityManagerFactory"; + private static final String PACKAGES_TO_SCAN_PROP = "packagesToScan"; + private static final String FREESWITCH_PACKAGE = "com.bytedesk.call.freeswitch"; + + @Bean + public static BeanFactoryPostProcessor freeSwitchEntityExclusionPostProcessor() { + return new BeanFactoryPostProcessor() { + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + if (!beanFactory.containsBeanDefinition(PRIMARY_EMF_BEAN_NAME)) { + return; + } + BeanDefinition bd = beanFactory.getBeanDefinition(PRIMARY_EMF_BEAN_NAME); + PropertyValue pv = bd.getPropertyValues().getPropertyValue(PACKAGES_TO_SCAN_PROP); + if (pv == null) { + // 主库未显式设置 packagesToScan,Spring Boot 通常会回退为应用主包; + // 此处尽量安全处理:不做任何变更,避免误伤。 + return; + } + Object value = pv.getValue(); + List packages = new ArrayList<>(); + if (value instanceof String[]) { + for (String s : (String[]) value) { + if (StringUtils.hasText(s)) packages.add(s); + } + } else if (value instanceof List) { + for (Object o : (List) value) { + if (o instanceof String && StringUtils.hasText((String) o)) { + packages.add((String) o); + } else if (o instanceof TypedStringValue) { + String s = ((TypedStringValue) o).getValue(); + if (StringUtils.hasText(s)) packages.add(s); + } + } + } else if (value instanceof TypedStringValue) { + String s = ((TypedStringValue) value).getValue(); + if (StringUtils.hasText(s)) packages.add(s); + } + + if (packages.isEmpty()) { + return; + } + + // 过滤掉 freeswitch 包及其子包 + List filtered = packages.stream() + .filter(p -> !isSameOrChildPackage(p, FREESWITCH_PACKAGE)) + .toList(); + + if (!filtered.equals(packages)) { + bd.getPropertyValues().add(PACKAGES_TO_SCAN_PROP, filtered.toArray(String[]::new)); + } + } + + private boolean isSameOrChildPackage(String pkg, String target) { + if (!StringUtils.hasText(pkg) || !StringUtils.hasText(target)) return false; + if (Objects.equals(pkg, target)) return true; + return pkg.startsWith(target + "."); + } + }; + } +} diff --git a/starter/src/main/resources/compose.yaml b/starter/src/main/resources/compose.yaml index b85b6296fc..d35d240ba9 100644 --- a/starter/src/main/resources/compose.yaml +++ b/starter/src/main/resources/compose.yaml @@ -239,13 +239,15 @@ services: volumes: # 配置文件目录 - 使用本地配置文件覆盖容器内的配置(经验证实际使用 /usr/local/freeswitch/etc/freeswitch) - ../../../../deploy/freeswitch/conf:/usr/local/freeswitch/etc/freeswitch - # 语音文件目录 # 数据持久化目录 - - freeswitch_data:/usr/local/freeswitch/db + # - freeswitch_data:/usr/local/freeswitch/db + - ../../../../deploy/freeswitch/db:/usr/local/freeswitch/db # 日志目录 - - freeswitch_logs:/usr/local/freeswitch/log + # - freeswitch_logs:/usr/local/freeswitch/log + - ../../../../deploy/freeswitch/log:/usr/local/freeswitch/log # 录音目录 - - freeswitch_recordings:/usr/local/freeswitch/recordings + # - freeswitch_recordings:/usr/local/freeswitch/recordings + - ../../../../deploy/freeswitch/recordings:/usr/local/freeswitch/recordings depends_on: - bytedesk-mysql networks: