commit a817943dde1b48f017775d8c97c837cc6c7af832
Author: 齊 <23>
Date: Tue Apr 21 16:45:14 2026 +0800
新建
diff --git a/application-tenant/pom.xml b/application-tenant/pom.xml
new file mode 100644
index 0000000..35816f6
--- /dev/null
+++ b/application-tenant/pom.xml
@@ -0,0 +1,17 @@
+
+
+
+ apelet
+ apelet_saas
+ 1.0.0
+
+ 4.0.0
+
+ application-tenant
+ pom
+
+ tenant-admin
+
+
\ No newline at end of file
diff --git a/application-tenant/tenant-admin/pom.xml b/application-tenant/tenant-admin/pom.xml
new file mode 100644
index 0000000..2f65d87
--- /dev/null
+++ b/application-tenant/tenant-admin/pom.xml
@@ -0,0 +1,152 @@
+
+
+
+ application-tenant
+ apelet
+ 1.0.0
+
+ 4.0.0
+
+ tenant-admin
+ 1.0.0
+ tenant-admin
+ jar
+
+
+
+ com.anji-plus
+ spring-boot-starter-captcha
+ ${ajcaptcha.version}
+
+
+ apelet
+ common-qy
+ 1.0.0
+
+
+ apelet
+ common-redis
+ 1.0.0
+
+
+ apelet
+ common-ext
+ 1.0.0
+
+
+ apelet
+ common-dict
+ 1.0.0
+
+
+ apelet
+ common-datafilter
+ 1.0.0
+
+
+ apelet
+ common-mobile
+ 1.0.0
+
+
+ apelet
+ common-core
+ 1.0.0
+
+
+ apelet
+ common-log
+ 1.0.0
+
+
+ apelet
+ common-datasync
+ 1.0.0
+
+
+ apelet
+ common-aliyun-oss
+ 1.0.0
+
+
+
+ apelet
+ common-dbutil
+ 1.0.0
+
+
+ apelet
+ common-swagger
+ 1.0.0
+
+
+
+ apelet
+ common-generator
+ 1.0.0
+
+
+
+ apelet
+ common-eas
+ 1.0.0
+
+
+ apelet
+ common-online
+ 1.0.0
+
+
+ com.baomidou
+ mybatis-plus-generator
+ ${mybatisplus.version}
+
+
+ cn.afterturn
+ easypoi-base
+ 4.4.0
+
+
+ cn.afterturn
+ easypoi-web
+ 4.4.0
+
+
+ cn.afterturn
+ easypoi-annotation
+ 4.4.0
+
+
+ org.jetbrains
+ annotations
+ 24.0.1
+
+
+ com.xuxueli
+ xxl-job-core
+ 2.3.1
+ compile
+
+
+ org.springframework.boot
+ spring-boot-starter-quartz
+
+
+ org.quartz-scheduler
+ quartz
+ 2.3.2
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring-boot.version}
+
+
+
+
\ No newline at end of file
diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/TenantAdminApplication.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/TenantAdminApplication.java
new file mode 100644
index 0000000..3a0d02d
--- /dev/null
+++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/TenantAdminApplication.java
@@ -0,0 +1,26 @@
+package apelet.tenantadmin;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+/**
+ * 租户运营管理服务启动类。
+ *
+ * @author guifc
+ * @date 2023-08-04
+ */
+@ComponentScan("apelet")
+@EnableScheduling
+@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
+@MapperScan("apelet.common.**.dao")
+public class TenantAdminApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(TenantAdminApplication.class, args);
+ }
+}
diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/ApplicationConfig.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/ApplicationConfig.java
new file mode 100644
index 0000000..a709dce
--- /dev/null
+++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/ApplicationConfig.java
@@ -0,0 +1,59 @@
+package apelet.tenantadmin.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 租户运营管理服务的配置类
+ *
+ * @author guifc
+ * @date 2023-08-04
+ */
+@Data
+@Configuration
+@ConfigurationProperties(prefix = "application")
+public class ApplicationConfig {
+
+ /**
+ * token的Http Request Header的key
+ */
+ private String tokenHeaderKey;
+ /**
+ * token在过期之前,但是已经需要被刷新时,response返回的header信息的key。
+ */
+ private String refreshedTokenHeaderKey;
+ /**
+ * token 加密用的密钥,该值的长度最少10个字符(过短会报错)。
+ */
+ private String tokenSigningKey;
+ /**
+ * 令牌的过期时间,单位毫秒
+ */
+ private Long expiration;
+ /**
+ * 用户密码被重置之后的缺省密码
+ */
+ private String defaultUserPassword;
+ /**
+ * 上传文件的基础目录
+ */
+ private String uploadFileBaseDir;
+ /**
+ * 授信ip列表,没有填写表示全部信任。多个ip之间逗号分隔,如: http://10.10.10.1:8080,http://10.10.10.2:8080
+ */
+ private String credentialIpList;
+ /**
+ * 发送租户的同步数据到消息队列的主题。
+ */
+ private String tenantSyncTopic;
+ /**
+ * Session会话和用户权限在Redis中的过期时间(秒)。
+ * 缺省值是 one day
+ */
+ private int sessionExpiredSeconds = 86400;
+ /**
+ * 是否排他登录。
+ */
+ private Boolean excludeLogin = false;
+}
diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/DataSourceType.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/DataSourceType.java
new file mode 100644
index 0000000..2c97e0b
--- /dev/null
+++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/DataSourceType.java
@@ -0,0 +1,50 @@
+package apelet.tenantadmin.config;
+
+import apelet.common.core.constant.ApplicationConstant;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 表示数据源类型的常量对象。
+ *
+ * @author guifc
+ * @date 2023-08-04
+ */
+public final class DataSourceType {
+
+ /**
+ * 租户管理数据源。
+ */
+ public static final int TENANT_ADMIN = ApplicationConstant.TENANT_ADMIN_DATASOURCE_TYPE;
+ /**
+ * 租户业务通用数据源,如全局编码字典、在线表单、流程和报表等数据。
+ */
+ public static final int TENANT_COMMON = ApplicationConstant.TENANT_COMMON_DATASOURCE_TYPE;
+ public static final int OPERATION_LOG = ApplicationConstant.OPERATION_LOG_DATASOURCE_TYPE;
+ public static final int GLOBAL_DICT = ApplicationConstant.COMMON_GLOBAL_DICT_TYPE;
+
+ private static final Map TYPE_MAP = new HashMap<>(2);
+ static {
+ TYPE_MAP.put("tenant-admin", TENANT_ADMIN);
+ TYPE_MAP.put("tenant-common", TENANT_COMMON);
+ TYPE_MAP.put("operation-log", OPERATION_LOG);
+ TYPE_MAP.put("global-dict", GLOBAL_DICT);
+ }
+
+ /**
+ * 根据名称获取字典类型。
+ *
+ * @param name 数据源在配置中的名称。
+ * @return 返回可用于多数据源切换的数据源类型。
+ */
+ public static Integer getDataSourceTypeByName(String name) {
+ return TYPE_MAP.get(name);
+ }
+
+ /**
+ * 私有构造函数,明确标识该常量类的作用。
+ */
+ private DataSourceType() {
+ }
+}
diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/FilterConfig.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/FilterConfig.java
new file mode 100644
index 0000000..43dd5ba
--- /dev/null
+++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/FilterConfig.java
@@ -0,0 +1,61 @@
+package apelet.tenantadmin.config;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+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;
+
+import javax.servlet.Filter;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * 这里主要配置Web的各种过滤器和监听器等Servlet容器组件。
+ *
+ * @author guifc
+ * @date 2023-08-04
+ */
+@Configuration
+public class FilterConfig {
+
+ /**
+ * 配置Ajax跨域过滤器。
+ */
+ @Bean
+ public CorsFilter corsFilterRegistration(ApplicationConfig applicationConfig) {
+ UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
+ CorsConfiguration corsConfiguration = new CorsConfiguration();
+ if (StringUtils.isNotBlank(applicationConfig.getCredentialIpList())) {
+ if ("*".equals(applicationConfig.getCredentialIpList())) {
+ corsConfiguration.addAllowedOriginPattern("*");
+ } else {
+ String[] credentialIpList = StringUtils.split(applicationConfig.getCredentialIpList(), ",");
+ if (credentialIpList.length > 0) {
+ for (String ip : credentialIpList) {
+ corsConfiguration.addAllowedOrigin(ip);
+ }
+ }
+ }
+ corsConfiguration.addAllowedHeader("*");
+ corsConfiguration.addAllowedMethod("*");
+ corsConfiguration.addExposedHeader(applicationConfig.getRefreshedTokenHeaderKey());
+ corsConfiguration.setAllowCredentials(true);
+ configSource.registerCorsConfiguration("/**", corsConfiguration);
+ }
+ return new CorsFilter(configSource);
+ }
+
+ @Bean
+ public FilterRegistrationBean characterEncodingFilterRegistration() {
+ FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>(
+ new org.springframework.web.filter.CharacterEncodingFilter());
+ filterRegistrationBean.addUrlPatterns("/*");
+ filterRegistrationBean.addInitParameter("encoding", StandardCharsets.UTF_8.name());
+ // forceEncoding强制response也被编码,另外即使request中已经设置encoding,forceEncoding也会重新设置
+ filterRegistrationBean.addInitParameter("forceEncoding", "true");
+ filterRegistrationBean.setAsyncSupported(true);
+ return filterRegistrationBean;
+ }
+}
diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/InterceptorConfig.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/InterceptorConfig.java
new file mode 100644
index 0000000..0b1a0f1
--- /dev/null
+++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/InterceptorConfig.java
@@ -0,0 +1,24 @@
+package apelet.tenantadmin.config;
+
+import apelet.tenantadmin.interceptor.AuthenticationInterceptor;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * 所有的项目拦截器都在这里集中配置
+ *
+ * @author guifc
+ * @date 2023-08-04
+ */
+@Configuration
+public class InterceptorConfig implements WebMvcConfigurer {
+
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ registry.addInterceptor(new AuthenticationInterceptor())
+ .addPathPatterns("/tenantadmin/**")
+ .addPathPatterns("/botp/**")
+ .addPathPatterns("/api/data-move/**");
+ }
+}
diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/MultiDataSourceConfig.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/MultiDataSourceConfig.java
new file mode 100644
index 0000000..2a39305
--- /dev/null
+++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/config/MultiDataSourceConfig.java
@@ -0,0 +1,66 @@
+package apelet.tenantadmin.config;
+
+import apelet.common.core.config.BaseMultiDataSourceConfig;
+import apelet.common.core.config.DynamicDataSource;
+import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+import javax.sql.DataSource;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 多数据源配置对象。
+ *
+ * @author guifc
+ * @date 2023-08-04
+ */
+@Configuration
+@EnableTransactionManagement
+@MapperScan(value = {"apelet.tenantadmin.upms.dao","apelet.tenantadmin.tenant.dao", "apelet.common.*.dao"})
+public class MultiDataSourceConfig extends BaseMultiDataSourceConfig {
+
+ @Bean(initMethod = "init", destroyMethod = "close")
+ @ConfigurationProperties(prefix = "spring.datasource.druid.tenant-admin")
+ public DataSource tenantAdminDataSource() {
+ return super.applyCommonProps(DruidDataSourceBuilder.create().build());
+ }
+
+ /**
+ * 默认生成的用于保存租户业务通用数据的数据源,如全局编码字典、在线表单、工作流和报表打印等基础数据。
+ * 我们还是非常推荐使用独立的数据源,这样便于今后的数据迁移。
+ */
+ @Bean(initMethod = "init", destroyMethod = "close")
+ @ConfigurationProperties(prefix = "spring.datasource.druid.tenant-common")
+ public DataSource tenantCommonDataSource() {
+ return super.applyCommonProps(DruidDataSourceBuilder.create().build());
+ }
+
+ /**
+ * 默认生成的用于保存操作日志的数据源,可根据需求修改。
+ * 我们还是非常推荐使用独立的数据源,这样便于今后的数据迁移。
+ */
+ @Bean(initMethod = "init", destroyMethod = "close")
+ @ConfigurationProperties(prefix = "spring.datasource.druid.operation-log")
+ public DataSource operationLogDataSource() {
+ return super.applyCommonProps(DruidDataSourceBuilder.create().build());
+ }
+
+ @Bean
+ @Primary
+ public DynamicDataSource dataSource() {
+ Map