diff --git a/OrangeFormsOpen-MybatisFlex/.DS_Store b/OrangeFormsOpen-MybatisFlex/.DS_Store
new file mode 100644
index 00000000..5542a459
Binary files /dev/null and b/OrangeFormsOpen-MybatisFlex/.DS_Store differ
diff --git a/OrangeFormsOpen-MybatisFlex/.gitignore b/OrangeFormsOpen-MybatisFlex/.gitignore
new file mode 100644
index 00000000..e3fa94cd
--- /dev/null
+++ b/OrangeFormsOpen-MybatisFlex/.gitignore
@@ -0,0 +1,26 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+/.mvn/*
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/build/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
\ No newline at end of file
diff --git a/OrangeFormsOpen-MybatisFlex/README.md b/OrangeFormsOpen-MybatisFlex/README.md
new file mode 100644
index 00000000..980a205f
--- /dev/null
+++ b/OrangeFormsOpen-MybatisFlex/README.md
@@ -0,0 +1,21 @@
+### 服务接口文档
+---
+- Knife4j
+ - 服务启动后,Knife4j的文档入口地址 [http://localhost:8082/doc.html#/plus](http://localhost:8082/doc.html#/plus)
+- Postman
+ - 无需启动服务,即可将当前工程的接口导出成Postman格式。在工程的common/common-tools/模块下,找到ExportApiApp文件,并执行main函数。
+
+### 服务启动环境依赖
+---
+
+执行docker-compose up -d 命令启动下面依赖的服务。
+执行docker-compose down 命令停止下面服务。
+
+- Redis
+ - 版本:4
+ - 端口: 6379
+ - 推荐客户端工具 [AnotherRedisDesktopManager](https://github.com/qishibo/AnotherRedisDesktopManager)
+- Minio
+ - 版本:8.4.5
+ - 控制台URL:需要配置Nginx,将请求导入到我们缺省设置的19000端口,之后可通过浏览器操作minio。
+ - 缺省用户名密码:admin/admin123456
diff --git a/OrangeFormsOpen-MybatisFlex/application-webadmin/pom.xml b/OrangeFormsOpen-MybatisFlex/application-webadmin/pom.xml
new file mode 100644
index 00000000..a78c5df9
--- /dev/null
+++ b/OrangeFormsOpen-MybatisFlex/application-webadmin/pom.xml
@@ -0,0 +1,91 @@
+
+
+
+ com.orangeforms
+ OrangeFormsOpen
+ 1.0.0
+
+ 4.0.0
+
+ application-webadmin
+ 1.0.0
+ application-webadmin
+ jar
+
+
+
+
+ com.orangeforms
+ common-satoken
+ 1.0.0
+
+
+ com.orangeforms
+ common-ext
+ 1.0.0
+
+
+ com.orangeforms
+ common-redis
+ 1.0.0
+
+
+ com.orangeforms
+ common-online
+ 1.0.0
+
+
+ com.orangeforms
+ common-flow-online
+ 1.0.0
+
+
+ com.orangeforms
+ common-log
+ 1.0.0
+
+
+ com.orangeforms
+ common-minio
+ 1.0.0
+
+
+ com.orangeforms
+ common-sequence
+ 1.0.0
+
+
+ com.orangeforms
+ common-datafilter
+ 1.0.0
+
+
+ com.orangeforms
+ common-swagger
+ 1.0.0
+
+
+ com.orangeforms
+ common-dict
+ 1.0.0
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring-boot.version}
+
+
+
+ repackage
+
+
+
+
+
+
+
diff --git a/OrangeFormsOpen-MybatisFlex/application-webadmin/src/main/java/com/orangeforms/webadmin/WebAdminApplication.java b/OrangeFormsOpen-MybatisFlex/application-webadmin/src/main/java/com/orangeforms/webadmin/WebAdminApplication.java
new file mode 100644
index 00000000..86a9458a
--- /dev/null
+++ b/OrangeFormsOpen-MybatisFlex/application-webadmin/src/main/java/com/orangeforms/webadmin/WebAdminApplication.java
@@ -0,0 +1,23 @@
+package com.orangeforms.webadmin;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.scheduling.annotation.EnableAsync;
+
+/**
+ * 应用服务启动类。
+ *
+ * @author Jerry
+ * @date 2024-07-02
+ */
+@EnableAsync
+@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
+@ComponentScan("com.orangeforms")
+public class WebAdminApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(WebAdminApplication.class, args);
+ }
+}
diff --git a/OrangeFormsOpen-MybatisFlex/application-webadmin/src/main/java/com/orangeforms/webadmin/app/util/FlowIdentityExtHelper.java b/OrangeFormsOpen-MybatisFlex/application-webadmin/src/main/java/com/orangeforms/webadmin/app/util/FlowIdentityExtHelper.java
new file mode 100644
index 00000000..d5198b82
--- /dev/null
+++ b/OrangeFormsOpen-MybatisFlex/application-webadmin/src/main/java/com/orangeforms/webadmin/app/util/FlowIdentityExtHelper.java
@@ -0,0 +1,244 @@
+package com.orangeforms.webadmin.app.util;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import com.orangeforms.common.flow.util.BaseFlowIdentityExtHelper;
+import com.orangeforms.common.flow.util.FlowCustomExtFactory;
+import com.orangeforms.common.flow.vo.FlowUserInfoVo;
+import com.orangeforms.webadmin.upms.model.SysDept;
+import com.orangeforms.webadmin.upms.model.SysUser;
+import com.orangeforms.webadmin.upms.model.constant.SysUserStatus;
+import com.orangeforms.webadmin.upms.model.SysDeptPost;
+import com.orangeforms.webadmin.upms.service.SysDeptService;
+import com.orangeforms.webadmin.upms.service.SysUserService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import jakarta.annotation.PostConstruct;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 为流程提供所需的用户身份相关的等扩展信息的帮助类。
+ *
+ * @author Jerry
+ * @date 2024-07-02
+ */
+@Slf4j
+@Component
+public class FlowIdentityExtHelper implements BaseFlowIdentityExtHelper {
+
+ @Autowired
+ private SysDeptService sysDeptService;
+ @Autowired
+ private SysUserService sysUserService;
+ @Autowired
+ private FlowCustomExtFactory flowCustomExtFactory;
+
+ @PostConstruct
+ public void doRegister() {
+ flowCustomExtFactory.registerFlowIdentityExtHelper(this);
+ }
+
+ @Override
+ public Long getLeaderDeptPostId(Long deptId) {
+ List deptPostIdList = sysDeptService.getLeaderDeptPostIdList(deptId);
+ return CollUtil.isEmpty(deptPostIdList) ? null : deptPostIdList.get(0);
+ }
+
+ @Override
+ public Long getUpLeaderDeptPostId(Long deptId) {
+ List deptPostIdList = sysDeptService.getUpLeaderDeptPostIdList(deptId);
+ return CollUtil.isEmpty(deptPostIdList) ? null : deptPostIdList.get(0);
+ }
+
+ @Override
+ public Map getDeptPostIdMap(Long deptId, Set postIdSet) {
+ Set postIdSet2 = postIdSet.stream().map(Long::valueOf).collect(Collectors.toSet());
+ List deptPostList = sysDeptService.getSysDeptPostList(deptId, postIdSet2);
+ if (CollUtil.isEmpty(deptPostList)) {
+ return null;
+ }
+ Map resultMap = new HashMap<>(deptPostList.size());
+ deptPostList.forEach(sysDeptPost ->
+ resultMap.put(sysDeptPost.getPostId().toString(), sysDeptPost.getDeptPostId().toString()));
+ return resultMap;
+ }
+
+ @Override
+ public Map getSiblingDeptPostIdMap(Long deptId, Set postIdSet) {
+ Set postIdSet2 = postIdSet.stream().map(Long::valueOf).collect(Collectors.toSet());
+ List deptPostList = sysDeptService.getSiblingSysDeptPostList(deptId, postIdSet2);
+ if (CollUtil.isEmpty(deptPostList)) {
+ return null;
+ }
+ Map resultMap = new HashMap<>(deptPostList.size());
+ for (SysDeptPost deptPost : deptPostList) {
+ String deptPostId = resultMap.get(deptPost.getPostId().toString());
+ if (deptPostId != null) {
+ deptPostId = deptPostId + "," + deptPost.getDeptPostId();
+ } else {
+ deptPostId = deptPost.getDeptPostId().toString();
+ }
+ resultMap.put(deptPost.getPostId().toString(), deptPostId);
+ }
+ return resultMap;
+ }
+
+ @Override
+ public Map getUpDeptPostIdMap(Long deptId, Set postIdSet) {
+ SysDept sysDept = sysDeptService.getById(deptId);
+ if (sysDept == null || sysDept.getParentId() == null) {
+ return null;
+ }
+ return getDeptPostIdMap(sysDept.getParentId(), postIdSet);
+ }
+
+ @Override
+ public Set getUsernameListByRoleIds(Set roleIdSet) {
+ Set usernameSet = new HashSet<>();
+ Set roleIdSet2 = roleIdSet.stream().map(Long::valueOf).collect(Collectors.toSet());
+ SysUser filter = new SysUser();
+ filter.setUserStatus(SysUserStatus.STATUS_NORMAL);
+ for (Long roleId : roleIdSet2) {
+ List userList = sysUserService.getSysUserListByRoleId(roleId, filter, null);
+ this.extractAndAppendUsernameList(usernameSet, userList);
+ }
+ return usernameSet;
+ }
+
+ @Override
+ public List getUserInfoListByRoleIds(Set roleIdSet) {
+ List resultList = new LinkedList<>();
+ Set roleIdSet2 = roleIdSet.stream().map(Long::valueOf).collect(Collectors.toSet());
+ SysUser filter = new SysUser();
+ filter.setUserStatus(SysUserStatus.STATUS_NORMAL);
+ for (Long roleId : roleIdSet2) {
+ List userList = sysUserService.getSysUserListByRoleId(roleId, filter, null);
+ if (CollUtil.isNotEmpty(userList)) {
+ resultList.addAll(BeanUtil.copyToList(userList, FlowUserInfoVo.class));
+ }
+ }
+ return resultList;
+ }
+
+ @Override
+ public Set getUsernameListByDeptIds(Set deptIdSet) {
+ Set usernameSet = new HashSet<>();
+ Set deptIdSet2 = deptIdSet.stream().map(Long::valueOf).collect(Collectors.toSet());
+ for (Long deptId : deptIdSet2) {
+ SysUser filter = new SysUser();
+ filter.setDeptId(deptId);
+ filter.setUserStatus(SysUserStatus.STATUS_NORMAL);
+ List userList = sysUserService.getSysUserList(filter, null);
+ this.extractAndAppendUsernameList(usernameSet, userList);
+ }
+ return usernameSet;
+ }
+
+ @Override
+ public List getUserInfoListByDeptIds(Set deptIdSet) {
+ List resultList = new LinkedList<>();
+ Set deptIdSet2 = deptIdSet.stream().map(Long::valueOf).collect(Collectors.toSet());
+ for (Long deptId : deptIdSet2) {
+ SysUser filter = new SysUser();
+ filter.setDeptId(deptId);
+ filter.setUserStatus(SysUserStatus.STATUS_NORMAL);
+ List userList = sysUserService.getSysUserList(filter, null);
+ if (CollUtil.isNotEmpty(userList)) {
+ resultList.addAll(BeanUtil.copyToList(userList, FlowUserInfoVo.class));
+ }
+ }
+ return resultList;
+ }
+
+ @Override
+ public Set getUsernameListByPostIds(Set postIdSet) {
+ Set usernameSet = new HashSet<>();
+ Set postIdSet2 = postIdSet.stream().map(Long::valueOf).collect(Collectors.toSet());
+ SysUser filter = new SysUser();
+ filter.setUserStatus(SysUserStatus.STATUS_NORMAL);
+ for (Long postId : postIdSet2) {
+ List userList = sysUserService.getSysUserListByPostId(postId, filter, null);
+ this.extractAndAppendUsernameList(usernameSet, userList);
+ }
+ return usernameSet;
+ }
+
+ @Override
+ public List getUserInfoListByPostIds(Set postIdSet) {
+ List resultList = new LinkedList<>();
+ Set postIdSet2 = postIdSet.stream().map(Long::valueOf).collect(Collectors.toSet());
+ SysUser filter = new SysUser();
+ filter.setUserStatus(SysUserStatus.STATUS_NORMAL);
+ for (Long postId : postIdSet2) {
+ List userList = sysUserService.getSysUserListByPostId(postId, filter, null);
+ if (CollUtil.isNotEmpty(userList)) {
+ resultList.addAll(BeanUtil.copyToList(userList, FlowUserInfoVo.class));
+ }
+ }
+ return resultList;
+ }
+
+ @Override
+ public Set getUsernameListByDeptPostIds(Set deptPostIdSet) {
+ Set usernameSet = new HashSet<>();
+ Set deptPostIdSet2 = deptPostIdSet.stream().map(Long::valueOf).collect(Collectors.toSet());
+ SysUser filter = new SysUser();
+ filter.setUserStatus(SysUserStatus.STATUS_NORMAL);
+ for (Long deptPostId : deptPostIdSet2) {
+ List userList = sysUserService.getSysUserListByDeptPostId(deptPostId, filter, null);
+ this.extractAndAppendUsernameList(usernameSet, userList);
+ }
+ return usernameSet;
+ }
+
+ @Override
+ public List getUserInfoListByDeptPostIds(Set deptPostIdSet) {
+ List resultList = new LinkedList<>();
+ Set deptPostIdSet2 = deptPostIdSet.stream().map(Long::valueOf).collect(Collectors.toSet());
+ SysUser filter = new SysUser();
+ filter.setUserStatus(SysUserStatus.STATUS_NORMAL);
+ for (Long deptPostId : deptPostIdSet2) {
+ List userList = sysUserService.getSysUserListByDeptPostId(deptPostId, filter, null);
+ if (CollUtil.isNotEmpty(userList)) {
+ resultList.addAll(BeanUtil.copyToList(userList, FlowUserInfoVo.class));
+ }
+ }
+ return resultList;
+ }
+
+ @Override
+ public List getUserInfoListByUsernameSet(Set usernameSet) {
+ List resultList = null;
+ List userList = sysUserService.getInList("loginName", usernameSet);
+ if (CollUtil.isNotEmpty(userList)) {
+ resultList = BeanUtil.copyToList(userList, FlowUserInfoVo.class);
+ }
+ return resultList;
+ }
+
+ @Override
+ public Boolean supprtDataPerm() {
+ return true;
+ }
+
+ @Override
+ public Map mapUserShowNameByLoginName(Set loginNameSet) {
+ if (CollUtil.isEmpty(loginNameSet)) {
+ return new HashMap<>(1);
+ }
+ Map resultMap = new HashMap<>(loginNameSet.size());
+ List userList = sysUserService.getInList("loginName", loginNameSet);
+ userList.forEach(user -> resultMap.put(user.getLoginName(), user.getShowName()));
+ return resultMap;
+ }
+
+ private void extractAndAppendUsernameList(Set resultUsernameList, List userList) {
+ List usernameList = userList.stream().map(SysUser::getLoginName).collect(Collectors.toList());
+ if (CollUtil.isNotEmpty(usernameList)) {
+ resultUsernameList.addAll(usernameList);
+ }
+ }
+}
diff --git a/OrangeFormsOpen-MybatisFlex/application-webadmin/src/main/java/com/orangeforms/webadmin/config/ApplicationConfig.java b/OrangeFormsOpen-MybatisFlex/application-webadmin/src/main/java/com/orangeforms/webadmin/config/ApplicationConfig.java
new file mode 100644
index 00000000..dd028f9d
--- /dev/null
+++ b/OrangeFormsOpen-MybatisFlex/application-webadmin/src/main/java/com/orangeforms/webadmin/config/ApplicationConfig.java
@@ -0,0 +1,38 @@
+package com.orangeforms.webadmin.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 应用程序自定义的程序属性配置文件。
+ *
+ * @author Jerry
+ * @date 2024-07-02
+ */
+@Data
+@Configuration
+@ConfigurationProperties(prefix = "application")
+public class ApplicationConfig {
+ /**
+ * 用户密码被重置之后的缺省密码
+ */
+ private String defaultUserPassword;
+ /**
+ * 上传文件的基础目录
+ */
+ private String uploadFileBaseDir;
+ /**
+ * 授信ip列表,没有填写表示全部信任。多个ip之间逗号分隔,如: http://10.10.10.1:8080,http://10.10.10.2:8080
+ */
+ private String credentialIpList;
+ /**
+ * Session的用户权限在Redis中的过期时间(秒)。一定要和sa-token.timeout
+ * 缺省值是 one day
+ */
+ private int sessionExpiredSeconds = 86400;
+ /**
+ * 是否排他登录。
+ */
+ private Boolean excludeLogin = false;
+}
diff --git a/OrangeFormsOpen-MybatisFlex/application-webadmin/src/main/java/com/orangeforms/webadmin/config/DataSourceType.java b/OrangeFormsOpen-MybatisFlex/application-webadmin/src/main/java/com/orangeforms/webadmin/config/DataSourceType.java
new file mode 100644
index 00000000..4820bda3
--- /dev/null
+++ b/OrangeFormsOpen-MybatisFlex/application-webadmin/src/main/java/com/orangeforms/webadmin/config/DataSourceType.java
@@ -0,0 +1,47 @@
+package com.orangeforms.webadmin.config;
+
+import com.orangeforms.common.core.constant.ApplicationConstant;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 表示数据源类型的常量对象。
+ *
+ * @author Jerry
+ * @date 2024-07-02
+ */
+public final class DataSourceType {
+
+ public static final int MAIN = 0;
+ /**
+ * 以下所有数据源的类都型是固定值。如果有冲突,请修改上面定义的业务服务的数据源类型值。
+ */
+ public static final int OPERATION_LOG = ApplicationConstant.OPERATION_LOG_DATASOURCE_TYPE;
+ public static final int GLOBAL_DICT = ApplicationConstant.COMMON_GLOBAL_DICT_TYPE;
+ public static final int COMMON_FLOW_AND_ONLINE = ApplicationConstant.COMMON_FLOW_AND_ONLINE_DATASOURCE_TYPE;
+
+ private static final Map TYPE_MAP = new HashMap<>(8);
+ static {
+ TYPE_MAP.put("main", MAIN);
+ TYPE_MAP.put("operation-log", OPERATION_LOG);
+ TYPE_MAP.put("global-dict", GLOBAL_DICT);
+ TYPE_MAP.put("common-flow-online", COMMON_FLOW_AND_ONLINE);
+ }
+
+ /**
+ * 根据名称获取字典类型。
+ *
+ * @param name 数据源在配置中的名称。
+ * @return 返回可用于多数据源切换的数据源类型。
+ */
+ public static Integer getDataSourceTypeByName(String name) {
+ return TYPE_MAP.get(name);
+ }
+
+ /**
+ * 私有构造函数,明确标识该常量类的作用。
+ */
+ private DataSourceType() {
+ }
+}
diff --git a/OrangeFormsOpen-MybatisFlex/application-webadmin/src/main/java/com/orangeforms/webadmin/config/FilterConfig.java b/OrangeFormsOpen-MybatisFlex/application-webadmin/src/main/java/com/orangeforms/webadmin/config/FilterConfig.java
new file mode 100644
index 00000000..350602db
--- /dev/null
+++ b/OrangeFormsOpen-MybatisFlex/application-webadmin/src/main/java/com/orangeforms/webadmin/config/FilterConfig.java
@@ -0,0 +1,60 @@
+package com.orangeforms.webadmin.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 jakarta.servlet.Filter;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * 这里主要配置Web的各种过滤器和监听器等Servlet容器组件。
+ *
+ * @author Jerry
+ * @date 2024-07-02
+ */
+@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.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/OrangeFormsOpen-MybatisFlex/application-webadmin/src/main/java/com/orangeforms/webadmin/config/InterceptorConfig.java b/OrangeFormsOpen-MybatisFlex/application-webadmin/src/main/java/com/orangeforms/webadmin/config/InterceptorConfig.java
new file mode 100644
index 00000000..1d75ac6d
--- /dev/null
+++ b/OrangeFormsOpen-MybatisFlex/application-webadmin/src/main/java/com/orangeforms/webadmin/config/InterceptorConfig.java
@@ -0,0 +1,21 @@
+package com.orangeforms.webadmin.config;
+
+import com.orangeforms.webadmin.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 Jerry
+ * @date 2024-07-02
+ */
+@Configuration
+public class InterceptorConfig implements WebMvcConfigurer {
+
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ registry.addInterceptor(new AuthenticationInterceptor()).addPathPatterns("/admin/**");
+ }
+}
diff --git a/OrangeFormsOpen-MybatisFlex/application-webadmin/src/main/java/com/orangeforms/webadmin/config/MultiDataSourceConfig.java b/OrangeFormsOpen-MybatisFlex/application-webadmin/src/main/java/com/orangeforms/webadmin/config/MultiDataSourceConfig.java
new file mode 100644
index 00000000..bb09bf79
--- /dev/null
+++ b/OrangeFormsOpen-MybatisFlex/application-webadmin/src/main/java/com/orangeforms/webadmin/config/MultiDataSourceConfig.java
@@ -0,0 +1,77 @@
+package com.orangeforms.webadmin.config;
+
+import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
+import com.orangeforms.common.core.config.BaseMultiDataSourceConfig;
+import com.orangeforms.common.core.config.DynamicDataSource;
+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 org.mybatis.spring.annotation.MapperScan;
+
+import javax.sql.DataSource;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 多数据源配置对象。
+ *
+ * @author Jerry
+ * @date 2024-07-02
+ */
+@Configuration
+@EnableTransactionManagement
+@MapperScan(value = {"com.orangeforms.webadmin.*.dao", "com.orangeforms.common.*.dao"})
+public class MultiDataSourceConfig extends BaseMultiDataSourceConfig {
+
+ @Bean(initMethod = "init", destroyMethod = "close")
+ @ConfigurationProperties(prefix = "spring.datasource.druid.main")
+ public DataSource mainDataSource() {
+ 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(initMethod = "init", destroyMethod = "close")
+ @ConfigurationProperties(prefix = "spring.datasource.druid.global-dict")
+ public DataSource globalDictDataSource() {
+ return super.applyCommonProps(DruidDataSourceBuilder.create().build());
+ }
+
+ /**
+ * 默认生成的用于在线表单内部表的数据源,可根据需求修改。
+ * 这里我们还是非常推荐使用独立数据源,这样便于今后的服务拆分。
+ */
+ @Bean(initMethod = "init", destroyMethod = "close")
+ @ConfigurationProperties(prefix = "spring.datasource.druid.common-flow-online")
+ public DataSource commonFlowAndOnlineDataSource() {
+ return super.applyCommonProps(DruidDataSourceBuilder.create().build());
+ }
+
+ @Bean
+ @Primary
+ public DynamicDataSource dataSource() {
+ Map