初始化项目提交,并添加 flowable 7 的 dm 兼容
This commit is contained in:
19
yudao-module-bpm/yudao-module-bpm-server/Dockerfile
Normal file
19
yudao-module-bpm/yudao-module-bpm-server/Dockerfile
Normal file
@@ -0,0 +1,19 @@
|
||||
## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性
|
||||
## 感谢复旦核博士的建议!灰子哥,牛皮!
|
||||
FROM eclipse-temurin:21-jre
|
||||
|
||||
## 创建目录,并使用它作为工作目录
|
||||
RUN mkdir -p /yudao-module-bpm-server
|
||||
WORKDIR /yudao-module-bpm-server
|
||||
## 将后端项目的 Jar 文件,复制到镜像中
|
||||
COPY ./target/yudao-module-bpm-server.jar app.jar
|
||||
|
||||
## 设置 TZ 时区
|
||||
## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖
|
||||
ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms512m -Xmx512m"
|
||||
|
||||
## 暴露后端项目的 48080 端口
|
||||
EXPOSE 48083
|
||||
|
||||
## 启动后端项目
|
||||
CMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar
|
||||
136
yudao-module-bpm/yudao-module-bpm-server/pom.xml
Normal file
136
yudao-module-bpm/yudao-module-bpm-server/pom.xml
Normal file
@@ -0,0 +1,136 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-module-bpm</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>yudao-module-bpm-server</artifactId>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>
|
||||
bpm 包下,业务流程管理(Business Process Management),我们放工作流的功能,基于 Flowable 6 版本实现。
|
||||
例如说:流程定义、表单配置、审核中心(我的申请、我的待办、我的已办)等等 </description>
|
||||
|
||||
<dependencies>
|
||||
<!-- Spring Cloud 基础 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-env</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 依赖服务 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-module-bpm-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-module-system-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 业务组件 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-data-permission</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- DB 相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-mybatis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- RPC 远程调用相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-rpc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Registry 注册中心相关 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Config 配置中心相关 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 服务保障相关 TODO 芋艿:暂时去掉 -->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>cn.iocoder.cloud</groupId>-->
|
||||
<!-- <artifactId>yudao-spring-boot-starter-protection</artifactId>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<!-- Test 测试相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-test</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 监控相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-monitor</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 工具类相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-excel</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Flowable 工作流相关 -->
|
||||
<dependency>
|
||||
<groupId>org.flowable</groupId>
|
||||
<artifactId>flowable-spring-boot-starter-process</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flowable</groupId>
|
||||
<artifactId>flowable-spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<!-- 设置构建的 jar 包名 -->
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<!-- 打包 -->
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,781 @@
|
||||
//
|
||||
// Source code recreated from a .class file by IntelliJ IDEA
|
||||
// (powered by FernFlower decompiler)
|
||||
//
|
||||
|
||||
package com.alibaba.druid.pool;
|
||||
|
||||
import com.alibaba.cloud.commons.lang.StringUtils;
|
||||
import com.alibaba.druid.VERSION;
|
||||
import com.alibaba.druid.support.logging.Log;
|
||||
import com.alibaba.druid.support.logging.LogFactory;
|
||||
import com.alibaba.druid.util.JdbcUtils;
|
||||
import com.alibaba.druid.util.MySqlUtils;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLWarning;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DruidPooledStatement extends PoolableWrapper implements Statement {
|
||||
private static final Log LOG = LogFactory.getLog(DruidPooledStatement.class);
|
||||
private final Statement stmt;
|
||||
protected DruidPooledConnection conn;
|
||||
protected List<ResultSet> resultSetTrace;
|
||||
protected boolean closed;
|
||||
protected int fetchRowPeak = -1;
|
||||
protected int exceptionCount;
|
||||
|
||||
public DruidPooledStatement(DruidPooledConnection conn, Statement stmt) {
|
||||
super(stmt);
|
||||
this.conn = conn;
|
||||
this.stmt = stmt;
|
||||
}
|
||||
|
||||
protected void addResultSetTrace(ResultSet resultSet) {
|
||||
if (this.resultSetTrace == null) {
|
||||
this.resultSetTrace = new ArrayList(1);
|
||||
} else if (this.resultSetTrace.size() > 0) {
|
||||
int lastIndex = this.resultSetTrace.size() - 1;
|
||||
ResultSet lastResultSet = (ResultSet)this.resultSetTrace.get(lastIndex);
|
||||
|
||||
try {
|
||||
if (lastResultSet.isClosed()) {
|
||||
this.resultSetTrace.set(lastIndex, resultSet);
|
||||
return;
|
||||
}
|
||||
} catch (SQLException var5) {
|
||||
}
|
||||
}
|
||||
|
||||
this.resultSetTrace.add(resultSet);
|
||||
}
|
||||
|
||||
protected void recordFetchRowCount(int fetchRowCount) {
|
||||
if (this.fetchRowPeak < fetchRowCount) {
|
||||
this.fetchRowPeak = fetchRowCount;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public int getFetchRowPeak() {
|
||||
return this.fetchRowPeak;
|
||||
}
|
||||
|
||||
protected SQLException checkException(Throwable error) throws SQLException {
|
||||
String sql = null;
|
||||
if (this instanceof DruidPooledPreparedStatement) {
|
||||
sql = ((DruidPooledPreparedStatement)this).getSql();
|
||||
}
|
||||
|
||||
this.handleSocketTimeout(error);
|
||||
++this.exceptionCount;
|
||||
return this.conn.handleException(error, sql);
|
||||
}
|
||||
|
||||
protected SQLException checkException(Throwable error, String sql) throws SQLException {
|
||||
this.handleSocketTimeout(error);
|
||||
++this.exceptionCount;
|
||||
return this.conn.handleException(error, sql);
|
||||
}
|
||||
|
||||
protected void handleSocketTimeout(Throwable error) throws SQLException {
|
||||
if (this.conn != null && this.conn.transactionInfo == null && this.conn.holder != null) {
|
||||
DruidDataSource dataSource = null;
|
||||
DruidConnectionHolder holder = this.conn.holder;
|
||||
if (holder.dataSource instanceof DruidDataSource) {
|
||||
dataSource = (DruidDataSource)holder.dataSource;
|
||||
}
|
||||
|
||||
if (dataSource != null) {
|
||||
if (dataSource.killWhenSocketReadTimeout) {
|
||||
SQLException sqlException = null;
|
||||
if (error instanceof SQLException) {
|
||||
sqlException = (SQLException)error;
|
||||
}
|
||||
|
||||
if (sqlException != null) {
|
||||
Throwable cause = error.getCause();
|
||||
boolean socketReadTimeout = cause instanceof SocketTimeoutException && "Read timed out".equals(cause.getMessage());
|
||||
if (socketReadTimeout) {
|
||||
if (JdbcUtils.isMysqlDbType(dataSource.dbTypeName)) {
|
||||
String killQuery = MySqlUtils.buildKillQuerySql(this.conn.getConnection(), (SQLException)error);
|
||||
if (killQuery != null) {
|
||||
DruidPooledConnection killQueryConn = null;
|
||||
Statement killQueryStmt = null;
|
||||
|
||||
try {
|
||||
killQueryConn = dataSource.getConnection(1000L);
|
||||
if (killQueryConn != null) {
|
||||
killQueryStmt = killQueryConn.createStatement();
|
||||
killQueryStmt.execute(killQuery);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(killQuery + " success.");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LOG.warn(killQuery + " error.", ex);
|
||||
return;
|
||||
} finally {
|
||||
JdbcUtils.close(killQueryStmt);
|
||||
JdbcUtils.close(killQueryConn);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public DruidPooledConnection getPoolableConnection() {
|
||||
return this.conn;
|
||||
}
|
||||
|
||||
public Statement getStatement() {
|
||||
return this.stmt;
|
||||
}
|
||||
|
||||
protected void checkOpen() throws SQLException {
|
||||
if (this.closed) {
|
||||
Throwable disableError = null;
|
||||
if (this.conn != null) {
|
||||
disableError = this.conn.getDisableError();
|
||||
}
|
||||
|
||||
if (disableError != null) {
|
||||
throw new SQLException("statement is closed", disableError);
|
||||
} else {
|
||||
throw new SQLException("statement is closed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void clearResultSet() {
|
||||
if (this.resultSetTrace != null) {
|
||||
for(ResultSet rs : this.resultSetTrace) {
|
||||
try {
|
||||
if (!rs.isClosed()) {
|
||||
rs.close();
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
LOG.error("clearResultSet error", ex);
|
||||
}
|
||||
}
|
||||
|
||||
this.resultSetTrace.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void incrementExecuteCount() {
|
||||
DruidPooledConnection conn = this.getPoolableConnection();
|
||||
if (conn != null) {
|
||||
DruidConnectionHolder holder = conn.getConnectionHolder();
|
||||
if (holder != null) {
|
||||
DruidAbstractDataSource dataSource = holder.getDataSource();
|
||||
if (dataSource != null) {
|
||||
dataSource.incrementExecuteCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void incrementExecuteBatchCount() {
|
||||
DruidPooledConnection conn = this.getPoolableConnection();
|
||||
if (conn != null) {
|
||||
DruidConnectionHolder holder = conn.getConnectionHolder();
|
||||
if (holder != null) {
|
||||
if (holder.getDataSource() != null) {
|
||||
DruidAbstractDataSource dataSource = holder.getDataSource();
|
||||
if (dataSource != null) {
|
||||
dataSource.incrementExecuteBatchCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void incrementExecuteUpdateCount() {
|
||||
DruidPooledConnection conn = this.getPoolableConnection();
|
||||
if (conn != null) {
|
||||
DruidConnectionHolder holder = conn.getConnectionHolder();
|
||||
if (holder != null) {
|
||||
DruidAbstractDataSource dataSource = holder.getDataSource();
|
||||
if (dataSource != null) {
|
||||
dataSource.incrementExecuteUpdateCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void incrementExecuteQueryCount() {
|
||||
DruidPooledConnection conn = this.conn;
|
||||
if (conn != null) {
|
||||
DruidConnectionHolder holder = conn.holder;
|
||||
if (holder != null) {
|
||||
DruidAbstractDataSource dataSource = holder.dataSource;
|
||||
if (dataSource != null) {
|
||||
++dataSource.executeQueryCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void transactionRecord(String sql) throws SQLException {
|
||||
this.conn.transactionRecord(sql);
|
||||
}
|
||||
|
||||
public final ResultSet executeQuery(String sql) throws SQLException {
|
||||
this.checkOpen();
|
||||
this.incrementExecuteQueryCount();
|
||||
this.transactionRecord(sql);
|
||||
this.conn.beforeExecute();
|
||||
|
||||
ResultSet var3;
|
||||
try {
|
||||
ResultSet rs = this.stmt.executeQuery(sql);
|
||||
if (rs != null) {
|
||||
DruidPooledResultSet poolableResultSet = new DruidPooledResultSet(this, rs);
|
||||
this.addResultSetTrace(poolableResultSet);
|
||||
DruidPooledResultSet var4 = poolableResultSet;
|
||||
return var4;
|
||||
}
|
||||
|
||||
var3 = rs;
|
||||
} catch (Throwable t) {
|
||||
this.errorCheck(t);
|
||||
throw this.checkException(t, sql);
|
||||
} finally {
|
||||
this.conn.afterExecute();
|
||||
}
|
||||
|
||||
return var3;
|
||||
}
|
||||
|
||||
public final int executeUpdate(String sql) throws SQLException {
|
||||
this.checkOpen();
|
||||
this.incrementExecuteUpdateCount();
|
||||
this.transactionRecord(sql);
|
||||
this.conn.beforeExecute();
|
||||
|
||||
int var2;
|
||||
try {
|
||||
var2 = this.stmt.executeUpdate(sql);
|
||||
} catch (Throwable t) {
|
||||
this.errorCheck(t);
|
||||
throw this.checkException(t, sql);
|
||||
} finally {
|
||||
this.conn.afterExecute();
|
||||
}
|
||||
|
||||
return var2;
|
||||
}
|
||||
|
||||
protected final void errorCheck(Throwable t) {
|
||||
String errorClassName = t.getClass().getName();
|
||||
if (errorClassName.endsWith(".CommunicationsException") && this.conn.holder != null && this.conn.holder.dataSource.testWhileIdle) {
|
||||
DruidConnectionHolder holder = this.conn.holder;
|
||||
DruidAbstractDataSource dataSource = holder.dataSource;
|
||||
long currentTimeMillis = System.currentTimeMillis();
|
||||
long lastActiveTimeMillis = holder.lastActiveTimeMillis;
|
||||
if (lastActiveTimeMillis < holder.lastKeepTimeMillis) {
|
||||
lastActiveTimeMillis = holder.lastKeepTimeMillis;
|
||||
}
|
||||
|
||||
long idleMillis = currentTimeMillis - lastActiveTimeMillis;
|
||||
long lastValidIdleMillis = currentTimeMillis - holder.lastActiveTimeMillis;
|
||||
String errorMsg = "CommunicationsException, druid version " + VERSION.getVersionNumber() + ", jdbcUrl : " + dataSource.jdbcUrl + ", testWhileIdle " + dataSource.testWhileIdle + ", idle millis " + idleMillis + ", minIdle " + dataSource.minIdle + ", poolingCount " + dataSource.getPoolingCount() + ", timeBetweenEvictionRunsMillis " + dataSource.timeBetweenEvictionRunsMillis + ", lastValidIdleMillis " + lastValidIdleMillis + ", driver " + dataSource.driver.getClass().getName();
|
||||
if (dataSource.exceptionSorter != null) {
|
||||
errorMsg = errorMsg + ", exceptionSorter " + dataSource.exceptionSorter.getClass().getName();
|
||||
}
|
||||
|
||||
LOG.error(errorMsg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public final int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
|
||||
this.checkOpen();
|
||||
this.incrementExecuteUpdateCount();
|
||||
this.transactionRecord(sql);
|
||||
this.conn.beforeExecute();
|
||||
|
||||
int var3;
|
||||
try {
|
||||
var3 = this.stmt.executeUpdate(sql, autoGeneratedKeys);
|
||||
} catch (Throwable t) {
|
||||
this.errorCheck(t);
|
||||
throw this.checkException(t, sql);
|
||||
} finally {
|
||||
this.conn.afterExecute();
|
||||
}
|
||||
|
||||
return var3;
|
||||
}
|
||||
|
||||
public final int executeUpdate(String sql, int[] columnIndexes) throws SQLException {
|
||||
this.checkOpen();
|
||||
this.incrementExecuteUpdateCount();
|
||||
this.transactionRecord(sql);
|
||||
this.conn.beforeExecute();
|
||||
|
||||
int var3;
|
||||
try {
|
||||
var3 = this.stmt.executeUpdate(sql, columnIndexes);
|
||||
} catch (Throwable t) {
|
||||
this.errorCheck(t);
|
||||
throw this.checkException(t, sql);
|
||||
} finally {
|
||||
this.conn.afterExecute();
|
||||
}
|
||||
|
||||
return var3;
|
||||
}
|
||||
|
||||
public final int executeUpdate(String sql, String[] columnNames) throws SQLException {
|
||||
this.checkOpen();
|
||||
this.incrementExecuteUpdateCount();
|
||||
this.transactionRecord(sql);
|
||||
this.conn.beforeExecute();
|
||||
|
||||
int var3;
|
||||
try {
|
||||
var3 = this.stmt.executeUpdate(sql, columnNames);
|
||||
} catch (Throwable t) {
|
||||
this.errorCheck(t);
|
||||
throw this.checkException(t, sql);
|
||||
} finally {
|
||||
this.conn.afterExecute();
|
||||
}
|
||||
|
||||
return var3;
|
||||
}
|
||||
|
||||
public final boolean execute(String sql, int autoGeneratedKeys) throws SQLException {
|
||||
this.checkOpen();
|
||||
this.incrementExecuteCount();
|
||||
this.transactionRecord(sql);
|
||||
this.conn.beforeExecute();
|
||||
|
||||
boolean var3;
|
||||
try {
|
||||
var3 = this.stmt.execute(sql, autoGeneratedKeys);
|
||||
} catch (Throwable t) {
|
||||
this.errorCheck(t);
|
||||
throw this.checkException(t, sql);
|
||||
} finally {
|
||||
this.conn.afterExecute();
|
||||
}
|
||||
|
||||
return var3;
|
||||
}
|
||||
|
||||
public final boolean execute(String sql, int[] columnIndexes) throws SQLException {
|
||||
this.checkOpen();
|
||||
this.incrementExecuteCount();
|
||||
this.transactionRecord(sql);
|
||||
this.conn.beforeExecute();
|
||||
|
||||
boolean var3;
|
||||
try {
|
||||
var3 = this.stmt.execute(sql, columnIndexes);
|
||||
} catch (Throwable t) {
|
||||
this.errorCheck(t);
|
||||
throw this.checkException(t, sql);
|
||||
} finally {
|
||||
this.conn.afterExecute();
|
||||
}
|
||||
|
||||
return var3;
|
||||
}
|
||||
|
||||
public final boolean execute(String sql, String[] columnNames) throws SQLException {
|
||||
this.checkOpen();
|
||||
this.incrementExecuteCount();
|
||||
this.transactionRecord(sql);
|
||||
this.conn.beforeExecute();
|
||||
|
||||
boolean var3;
|
||||
try {
|
||||
var3 = this.stmt.execute(sql, columnNames);
|
||||
} catch (Throwable t) {
|
||||
this.errorCheck(t);
|
||||
throw this.checkException(t, sql);
|
||||
} finally {
|
||||
this.conn.afterExecute();
|
||||
}
|
||||
|
||||
return var3;
|
||||
}
|
||||
|
||||
|
||||
public int getMaxFieldSize() throws SQLException {
|
||||
this.checkOpen();
|
||||
|
||||
try {
|
||||
return this.stmt.getMaxFieldSize();
|
||||
} catch (Throwable t) {
|
||||
throw this.checkException(t);
|
||||
}
|
||||
}
|
||||
|
||||
public void close() throws SQLException {
|
||||
if (!this.closed) {
|
||||
this.clearResultSet();
|
||||
if (this.stmt != null) {
|
||||
this.stmt.close();
|
||||
}
|
||||
|
||||
this.closed = true;
|
||||
DruidConnectionHolder connHolder = this.conn.getConnectionHolder();
|
||||
if (connHolder != null) {
|
||||
connHolder.removeTrace(this);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void setMaxFieldSize(int max) throws SQLException {
|
||||
this.checkOpen();
|
||||
|
||||
try {
|
||||
this.stmt.setMaxFieldSize(max);
|
||||
} catch (Throwable t) {
|
||||
throw this.checkException(t);
|
||||
}
|
||||
}
|
||||
|
||||
public final int getMaxRows() throws SQLException {
|
||||
this.checkOpen();
|
||||
|
||||
try {
|
||||
return this.stmt.getMaxRows();
|
||||
} catch (Throwable t) {
|
||||
throw this.checkException(t);
|
||||
}
|
||||
}
|
||||
|
||||
public void setMaxRows(int max) throws SQLException {
|
||||
this.checkOpen();
|
||||
|
||||
try {
|
||||
this.stmt.setMaxRows(max);
|
||||
} catch (Throwable t) {
|
||||
throw this.checkException(t);
|
||||
}
|
||||
}
|
||||
|
||||
public final void setEscapeProcessing(boolean enable) throws SQLException {
|
||||
this.checkOpen();
|
||||
|
||||
try {
|
||||
this.stmt.setEscapeProcessing(enable);
|
||||
} catch (Throwable t) {
|
||||
throw this.checkException(t);
|
||||
}
|
||||
}
|
||||
|
||||
public final int getQueryTimeout() throws SQLException {
|
||||
this.checkOpen();
|
||||
|
||||
try {
|
||||
return this.stmt.getQueryTimeout();
|
||||
} catch (Throwable t) {
|
||||
throw this.checkException(t);
|
||||
}
|
||||
}
|
||||
|
||||
public void setQueryTimeout(int seconds) throws SQLException {
|
||||
this.checkOpen();
|
||||
|
||||
try {
|
||||
this.stmt.setQueryTimeout(seconds);
|
||||
} catch (Throwable t) {
|
||||
throw this.checkException(t);
|
||||
}
|
||||
}
|
||||
|
||||
public final void cancel() throws SQLException {
|
||||
this.checkOpen();
|
||||
|
||||
try {
|
||||
this.stmt.cancel();
|
||||
} catch (Throwable t) {
|
||||
throw this.checkException(t);
|
||||
}
|
||||
}
|
||||
|
||||
public final SQLWarning getWarnings() throws SQLException {
|
||||
this.checkOpen();
|
||||
|
||||
try {
|
||||
return this.stmt.getWarnings();
|
||||
} catch (Throwable t) {
|
||||
throw this.checkException(t);
|
||||
}
|
||||
}
|
||||
|
||||
public final void clearWarnings() throws SQLException {
|
||||
this.checkOpen();
|
||||
|
||||
try {
|
||||
this.stmt.clearWarnings();
|
||||
} catch (Throwable t) {
|
||||
throw this.checkException(t);
|
||||
}
|
||||
}
|
||||
|
||||
public final void setCursorName(String name) throws SQLException {
|
||||
this.checkOpen();
|
||||
|
||||
try {
|
||||
this.stmt.setCursorName(name);
|
||||
} catch (Throwable t) {
|
||||
throw this.checkException(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean execute(String sql) throws SQLException {
|
||||
checkOpen();
|
||||
|
||||
incrementExecuteCount();
|
||||
transactionRecord(sql);
|
||||
|
||||
try {
|
||||
if (StringUtils.isNotEmpty(sql)){
|
||||
sql = sql.replace("TRUE", "1");
|
||||
sql = sql.replace("FALSE", "0");
|
||||
}
|
||||
return stmt.execute(sql);
|
||||
} catch (Throwable t) {
|
||||
errorCheck(t);
|
||||
throw checkException(t, sql);
|
||||
}
|
||||
}
|
||||
|
||||
public final ResultSet getResultSet() throws SQLException {
|
||||
this.checkOpen();
|
||||
|
||||
try {
|
||||
ResultSet rs = this.stmt.getResultSet();
|
||||
if (rs == null) {
|
||||
return null;
|
||||
} else {
|
||||
DruidPooledResultSet poolableResultSet = new DruidPooledResultSet(this, rs);
|
||||
this.addResultSetTrace(poolableResultSet);
|
||||
return poolableResultSet;
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
throw this.checkException(t);
|
||||
}
|
||||
}
|
||||
|
||||
public final int getUpdateCount() throws SQLException {
|
||||
this.checkOpen();
|
||||
|
||||
try {
|
||||
return this.stmt.getUpdateCount();
|
||||
} catch (Throwable t) {
|
||||
throw this.checkException(t);
|
||||
}
|
||||
}
|
||||
|
||||
public final boolean getMoreResults() throws SQLException {
|
||||
this.checkOpen();
|
||||
|
||||
try {
|
||||
boolean moreResults = this.stmt.getMoreResults();
|
||||
if (this.resultSetTrace != null && this.resultSetTrace.size() > 0) {
|
||||
ResultSet lastResultSet = (ResultSet)this.resultSetTrace.get(this.resultSetTrace.size() - 1);
|
||||
if (lastResultSet instanceof DruidPooledResultSet) {
|
||||
DruidPooledResultSet pooledResultSet = (DruidPooledResultSet)lastResultSet;
|
||||
pooledResultSet.closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return moreResults;
|
||||
} catch (Throwable t) {
|
||||
throw this.checkException(t);
|
||||
}
|
||||
}
|
||||
|
||||
public void setFetchDirection(int direction) throws SQLException {
|
||||
this.checkOpen();
|
||||
|
||||
try {
|
||||
this.stmt.setFetchDirection(direction);
|
||||
} catch (Throwable t) {
|
||||
throw this.checkException(t);
|
||||
}
|
||||
}
|
||||
|
||||
public final int getFetchDirection() throws SQLException {
|
||||
this.checkOpen();
|
||||
|
||||
try {
|
||||
return this.stmt.getFetchDirection();
|
||||
} catch (Throwable t) {
|
||||
throw this.checkException(t);
|
||||
}
|
||||
}
|
||||
|
||||
public void setFetchSize(int rows) throws SQLException {
|
||||
this.checkOpen();
|
||||
|
||||
try {
|
||||
this.stmt.setFetchSize(rows);
|
||||
} catch (Throwable t) {
|
||||
throw this.checkException(t);
|
||||
}
|
||||
}
|
||||
|
||||
public final int getFetchSize() throws SQLException {
|
||||
this.checkOpen();
|
||||
|
||||
try {
|
||||
return this.stmt.getFetchSize();
|
||||
} catch (Throwable t) {
|
||||
throw this.checkException(t);
|
||||
}
|
||||
}
|
||||
|
||||
public final int getResultSetConcurrency() throws SQLException {
|
||||
this.checkOpen();
|
||||
|
||||
try {
|
||||
return this.stmt.getResultSetConcurrency();
|
||||
} catch (Throwable t) {
|
||||
throw this.checkException(t);
|
||||
}
|
||||
}
|
||||
|
||||
public final int getResultSetType() throws SQLException {
|
||||
this.checkOpen();
|
||||
|
||||
try {
|
||||
return this.stmt.getResultSetType();
|
||||
} catch (Throwable t) {
|
||||
throw this.checkException(t);
|
||||
}
|
||||
}
|
||||
|
||||
public final void addBatch(String sql) throws SQLException {
|
||||
this.checkOpen();
|
||||
this.transactionRecord(sql);
|
||||
|
||||
try {
|
||||
this.stmt.addBatch(sql);
|
||||
} catch (Throwable t) {
|
||||
throw this.checkException(t, sql);
|
||||
}
|
||||
}
|
||||
|
||||
public final void clearBatch() throws SQLException {
|
||||
if (!this.closed) {
|
||||
try {
|
||||
this.stmt.clearBatch();
|
||||
} catch (Throwable t) {
|
||||
throw this.checkException(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int[] executeBatch() throws SQLException {
|
||||
this.checkOpen();
|
||||
this.incrementExecuteBatchCount();
|
||||
this.conn.beforeExecute();
|
||||
|
||||
int[] var1;
|
||||
try {
|
||||
var1 = this.stmt.executeBatch();
|
||||
} catch (Throwable t) {
|
||||
this.errorCheck(t);
|
||||
throw this.checkException(t);
|
||||
} finally {
|
||||
this.conn.afterExecute();
|
||||
}
|
||||
|
||||
return var1;
|
||||
}
|
||||
|
||||
public final Connection getConnection() throws SQLException {
|
||||
this.checkOpen();
|
||||
return this.conn;
|
||||
}
|
||||
|
||||
public final boolean getMoreResults(int current) throws SQLException {
|
||||
this.checkOpen();
|
||||
|
||||
try {
|
||||
boolean results = this.stmt.getMoreResults(current);
|
||||
if (this.resultSetTrace != null && this.resultSetTrace.size() > 0) {
|
||||
ResultSet lastResultSet = (ResultSet)this.resultSetTrace.get(this.resultSetTrace.size() - 1);
|
||||
if (lastResultSet instanceof DruidPooledResultSet) {
|
||||
DruidPooledResultSet pooledResultSet = (DruidPooledResultSet)lastResultSet;
|
||||
pooledResultSet.closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
} catch (Throwable t) {
|
||||
throw this.checkException(t);
|
||||
}
|
||||
}
|
||||
|
||||
public final ResultSet getGeneratedKeys() throws SQLException {
|
||||
this.checkOpen();
|
||||
|
||||
try {
|
||||
ResultSet rs = this.stmt.getGeneratedKeys();
|
||||
DruidPooledResultSet poolableResultSet = new DruidPooledResultSet(this, rs);
|
||||
this.addResultSetTrace(poolableResultSet);
|
||||
return poolableResultSet;
|
||||
} catch (Throwable t) {
|
||||
throw this.checkException(t);
|
||||
}
|
||||
}
|
||||
|
||||
public final int getResultSetHoldability() throws SQLException {
|
||||
this.checkOpen();
|
||||
|
||||
try {
|
||||
return this.stmt.getResultSetHoldability();
|
||||
} catch (Throwable t) {
|
||||
throw this.checkException(t);
|
||||
}
|
||||
}
|
||||
|
||||
public final boolean isClosed() throws SQLException {
|
||||
return this.closed;
|
||||
}
|
||||
|
||||
public final void setPoolable(boolean poolable) throws SQLException {
|
||||
if (!poolable) {
|
||||
throw new SQLException("not support");
|
||||
}
|
||||
}
|
||||
|
||||
public final boolean isPoolable() throws SQLException {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return this.stmt.toString();
|
||||
}
|
||||
|
||||
public void closeOnCompletion() throws SQLException {
|
||||
this.stmt.closeOnCompletion();
|
||||
}
|
||||
|
||||
public boolean isCloseOnCompletion() throws SQLException {
|
||||
return this.stmt.isCloseOnCompletion();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,546 @@
|
||||
//
|
||||
// Source code recreated from a .class file by IntelliJ IDEA
|
||||
// (powered by FernFlower decompiler)
|
||||
//
|
||||
|
||||
package liquibase.database.core;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import liquibase.CatalogAndSchema;
|
||||
import liquibase.GlobalConfiguration;
|
||||
import liquibase.Scope;
|
||||
import liquibase.database.AbstractJdbcDatabase;
|
||||
import liquibase.database.DatabaseConnection;
|
||||
import liquibase.database.OfflineConnection;
|
||||
import liquibase.database.jvm.JdbcConnection;
|
||||
import liquibase.exception.DatabaseException;
|
||||
import liquibase.exception.UnexpectedLiquibaseException;
|
||||
import liquibase.exception.ValidationErrors;
|
||||
import liquibase.executor.ExecutorService;
|
||||
import liquibase.statement.DatabaseFunction;
|
||||
import liquibase.statement.SequenceCurrentValueFunction;
|
||||
import liquibase.statement.SequenceNextValueFunction;
|
||||
import liquibase.statement.UniqueConstraint;
|
||||
import liquibase.statement.core.RawCallStatement;
|
||||
import liquibase.statement.core.RawParameterizedSqlStatement;
|
||||
import liquibase.structure.DatabaseObject;
|
||||
import liquibase.structure.core.Catalog;
|
||||
import liquibase.structure.core.Column;
|
||||
import liquibase.structure.core.Index;
|
||||
import liquibase.structure.core.PrimaryKey;
|
||||
import liquibase.structure.core.Schema;
|
||||
import liquibase.util.JdbcUtil;
|
||||
import liquibase.util.StringUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public class DmDatabase extends AbstractJdbcDatabase {
|
||||
private static final String PROXY_USER_REGEX = ".*(?:thin|oci)\\:(.+)/@.*";
|
||||
public static final Pattern PROXY_USER_PATTERN = Pattern.compile(".*(?:thin|oci)\\:(.+)/@.*");
|
||||
private static final String VERSION_REGEX = "(\\d+)\\.(\\d+)\\..*";
|
||||
private static final Pattern VERSION_PATTERN = Pattern.compile("(\\d+)\\.(\\d+)\\..*");
|
||||
public static final String PRODUCT_NAME = "DM DBMS";
|
||||
private static final ResourceBundle coreBundle = ResourceBundle.getBundle("liquibase/i18n/liquibase-core");
|
||||
protected final int SHORT_IDENTIFIERS_LENGTH = 30;
|
||||
protected final int LONG_IDENTIFIERS_LEGNTH = 128;
|
||||
public static final int ORACLE_12C_MAJOR_VERSION = 12;
|
||||
public static final int ORACLE_23C_MAJOR_VERSION = 23;
|
||||
private final Set<String> reservedWords = new HashSet();
|
||||
private Set<String> userDefinedTypes;
|
||||
private Map<String, String> savedSessionNlsSettings;
|
||||
private Boolean canAccessDbaRecycleBin;
|
||||
private Integer databaseMajorVersion;
|
||||
private Integer databaseMinorVersion;
|
||||
|
||||
public DmDatabase() {
|
||||
super.unquotedObjectsAreUppercased = true;
|
||||
super.setCurrentDateTimeFunction("SYSTIMESTAMP");
|
||||
this.dateFunctions.add(new DatabaseFunction("SYSDATE"));
|
||||
this.dateFunctions.add(new DatabaseFunction("SYSTIMESTAMP"));
|
||||
this.dateFunctions.add(new DatabaseFunction("CURRENT_TIMESTAMP"));
|
||||
super.sequenceNextValueFunction = "%s.nextval";
|
||||
super.sequenceCurrentValueFunction = "%s.currval";
|
||||
}
|
||||
|
||||
public int getPriority() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
private void tryProxySession(String url, Connection con) {
|
||||
Matcher m = PROXY_USER_PATTERN.matcher(url);
|
||||
if (m.matches()) {
|
||||
Properties props = new Properties();
|
||||
props.put("PROXY_USER_NAME", m.group(1));
|
||||
|
||||
try {
|
||||
Method method = con.getClass().getMethod("openProxySession", Integer.TYPE, Properties.class);
|
||||
method.setAccessible(true);
|
||||
method.invoke(con, 1, props);
|
||||
} catch (Exception e) {
|
||||
Scope.getCurrentScope().getLog(this.getClass()).info("Could not open proxy session on OracleDatabase: " + e.getCause().getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Method method = con.getClass().getMethod("isProxySession");
|
||||
method.setAccessible(true);
|
||||
boolean b = (Boolean)method.invoke(con);
|
||||
if (!b) {
|
||||
Scope.getCurrentScope().getLog(this.getClass()).info("Proxy session not established on OracleDatabase: ");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Scope.getCurrentScope().getLog(this.getClass()).info("Could not open proxy session on OracleDatabase: " + e.getCause().getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void setConnection(DatabaseConnection conn) {
|
||||
this.reservedWords.addAll(Arrays.asList("GROUP", "USER", "SESSION", "PASSWORD", "RESOURCE", "START", "SIZE", "UID", "DESC", "ORDER"));
|
||||
Connection sqlConn = null;
|
||||
if (!(conn instanceof OfflineConnection)) {
|
||||
try {
|
||||
if (conn instanceof JdbcConnection) {
|
||||
sqlConn = ((JdbcConnection)conn).getWrappedConnection();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new UnexpectedLiquibaseException(e);
|
||||
}
|
||||
|
||||
if (sqlConn != null) {
|
||||
this.tryProxySession(conn.getURL(), sqlConn);
|
||||
|
||||
try {
|
||||
this.reservedWords.addAll(Arrays.asList(sqlConn.getMetaData().getSQLKeywords().toUpperCase().split(",\\s*")));
|
||||
} catch (SQLException e) {
|
||||
Scope.getCurrentScope().getLog(this.getClass()).info("Could get sql keywords on OracleDatabase: " + e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
Method method = sqlConn.getClass().getMethod("setRemarksReporting", Boolean.TYPE);
|
||||
method.setAccessible(true);
|
||||
method.invoke(sqlConn, true);
|
||||
} catch (Exception e) {
|
||||
Scope.getCurrentScope().getLog(this.getClass()).info("Could not set remarks reporting on OracleDatabase: " + e.getMessage());
|
||||
}
|
||||
|
||||
CallableStatement statement = null;
|
||||
|
||||
try {
|
||||
statement = sqlConn.prepareCall("{call DBMS_UTILITY.DB_VERSION(?,?)}");
|
||||
statement.registerOutParameter(1, 12);
|
||||
statement.registerOutParameter(2, 12);
|
||||
statement.execute();
|
||||
String compatibleVersion = statement.getString(2);
|
||||
if (compatibleVersion != null) {
|
||||
Matcher majorVersionMatcher = VERSION_PATTERN.matcher(compatibleVersion);
|
||||
if (majorVersionMatcher.matches()) {
|
||||
this.databaseMajorVersion = Integer.valueOf(majorVersionMatcher.group(1));
|
||||
this.databaseMinorVersion = Integer.valueOf(majorVersionMatcher.group(2));
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
String message = "Cannot read from DBMS_UTILITY.DB_VERSION: " + e.getMessage();
|
||||
Scope.getCurrentScope().getLog(this.getClass()).info("Could not set check compatibility mode on OracleDatabase, assuming not running in any sort of compatibility mode: " + message);
|
||||
} finally {
|
||||
JdbcUtil.closeStatement(statement);
|
||||
}
|
||||
|
||||
if (GlobalConfiguration.DDL_LOCK_TIMEOUT.getCurrentValue() != null) {
|
||||
int timeoutValue = (Integer)GlobalConfiguration.DDL_LOCK_TIMEOUT.getCurrentValue();
|
||||
Scope.getCurrentScope().getLog(this.getClass()).fine("Setting DDL_LOCK_TIMEOUT value to " + timeoutValue);
|
||||
String sql = "ALTER SESSION SET DDL_LOCK_TIMEOUT=" + timeoutValue;
|
||||
PreparedStatement ddlLockTimeoutStatement = null;
|
||||
|
||||
try {
|
||||
ddlLockTimeoutStatement = sqlConn.prepareStatement(sql);
|
||||
ddlLockTimeoutStatement.execute();
|
||||
} catch (SQLException sqle) {
|
||||
Scope.getCurrentScope().getUI().sendErrorMessage("Unable to set the DDL_LOCK_TIMEOUT_VALUE: " + sqle.getMessage(), sqle);
|
||||
Scope.getCurrentScope().getLog(this.getClass()).warning("Unable to set the DDL_LOCK_TIMEOUT_VALUE: " + sqle.getMessage(), sqle);
|
||||
} finally {
|
||||
JdbcUtil.closeStatement(ddlLockTimeoutStatement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.setConnection(conn);
|
||||
}
|
||||
|
||||
public String getShortName() {
|
||||
return "dm";
|
||||
}
|
||||
|
||||
protected String getDefaultDatabaseProductName() {
|
||||
return PRODUCT_NAME;
|
||||
}
|
||||
|
||||
public int getDatabaseMajorVersion() throws DatabaseException {
|
||||
return this.databaseMajorVersion == null ? super.getDatabaseMajorVersion() : this.databaseMajorVersion;
|
||||
}
|
||||
|
||||
public int getDatabaseMinorVersion() throws DatabaseException {
|
||||
return this.databaseMinorVersion == null ? super.getDatabaseMinorVersion() : this.databaseMinorVersion;
|
||||
}
|
||||
|
||||
public Integer getDefaultPort() {
|
||||
return 5236;
|
||||
}
|
||||
|
||||
public String getJdbcCatalogName(CatalogAndSchema schema) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getJdbcSchemaName(CatalogAndSchema schema) {
|
||||
return this.correctObjectName(schema.getCatalogName() == null ? schema.getSchemaName() : schema.getCatalogName(), Schema.class);
|
||||
}
|
||||
|
||||
protected String getAutoIncrementClause(String generationType, Boolean defaultOnNull) {
|
||||
if (StringUtil.isEmpty(generationType)) {
|
||||
return super.getAutoIncrementClause();
|
||||
} else {
|
||||
String autoIncrementClause = "GENERATED %s AS IDENTITY";
|
||||
String generationStrategy = generationType;
|
||||
if (Boolean.TRUE.equals(defaultOnNull) && generationType.toUpperCase().equals("BY DEFAULT")) {
|
||||
generationStrategy = generationType + " ON NULL";
|
||||
}
|
||||
|
||||
return String.format(autoIncrementClause, generationStrategy);
|
||||
}
|
||||
}
|
||||
|
||||
public String generatePrimaryKeyName(String tableName) {
|
||||
return tableName.length() > 27 ? "PK_" + tableName.toUpperCase(Locale.US).substring(0, 27) : "PK_" + tableName.toUpperCase(Locale.US);
|
||||
}
|
||||
|
||||
public boolean supportsInitiallyDeferrableColumns() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isReservedWord(String objectName) {
|
||||
return this.reservedWords.contains(objectName.toUpperCase());
|
||||
}
|
||||
|
||||
public boolean supportsSequences() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean supports(Class<? extends DatabaseObject> object) {
|
||||
return Schema.class.isAssignableFrom(object) ? false : super.supports(object);
|
||||
}
|
||||
|
||||
public boolean supportsSchemas() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected String getConnectionCatalogName() throws DatabaseException {
|
||||
if (this.getConnection() instanceof OfflineConnection) {
|
||||
return this.getConnection().getCatalog();
|
||||
} else if (!(this.getConnection() instanceof JdbcConnection)) {
|
||||
return this.defaultCatalogName;
|
||||
} else {
|
||||
try {
|
||||
return (String)((ExecutorService)Scope.getCurrentScope().getSingleton(ExecutorService.class)).getExecutor("jdbc", this).queryForObject(new RawCallStatement("select sys_context( 'userenv', 'current_schema' ) from dual"), String.class);
|
||||
} catch (Exception e) {
|
||||
Scope.getCurrentScope().getLog(this.getClass()).info("Error getting default schema", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCorrectDatabaseImplementation(DatabaseConnection conn) throws DatabaseException {
|
||||
return "oracle".equalsIgnoreCase(conn.getDatabaseProductName());
|
||||
}
|
||||
|
||||
public String getDefaultDriver(String url) {
|
||||
return url.startsWith("jdbc:dm") ? "dm.jdbc.driver.DmDriver" : null;
|
||||
}
|
||||
|
||||
public String getDefaultCatalogName() {
|
||||
String defaultCatalogName = super.getDefaultCatalogName();
|
||||
if (Boolean.TRUE.equals(GlobalConfiguration.PRESERVE_SCHEMA_CASE.getCurrentValue())) {
|
||||
return defaultCatalogName;
|
||||
} else {
|
||||
return defaultCatalogName == null ? null : defaultCatalogName.toUpperCase(Locale.US);
|
||||
}
|
||||
}
|
||||
|
||||
public String getDateLiteral(String isoDate) {
|
||||
String normalLiteral = super.getDateLiteral(isoDate);
|
||||
if (this.isDateOnly(isoDate)) {
|
||||
return "TO_DATE(" + normalLiteral + ", 'YYYY-MM-DD')";
|
||||
} else if (this.isTimeOnly(isoDate)) {
|
||||
return "TO_DATE(" + normalLiteral + ", 'HH24:MI:SS')";
|
||||
} else if (this.isTimestamp(isoDate)) {
|
||||
return "TO_TIMESTAMP(" + normalLiteral + ", 'YYYY-MM-DD HH24:MI:SS.FF')";
|
||||
} else if (this.isDateTime(isoDate)) {
|
||||
int seppos = normalLiteral.lastIndexOf(46);
|
||||
if (seppos != -1) {
|
||||
normalLiteral = normalLiteral.substring(0, seppos) + "'";
|
||||
}
|
||||
|
||||
return "TO_DATE(" + normalLiteral + ", 'YYYY-MM-DD HH24:MI:SS')";
|
||||
} else {
|
||||
return "UNSUPPORTED:" + isoDate;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isSystemObject(DatabaseObject example) {
|
||||
if (example == null) {
|
||||
return false;
|
||||
} else if (this.isLiquibaseObject(example)) {
|
||||
return false;
|
||||
} else {
|
||||
if (example instanceof Schema) {
|
||||
if ("SYSTEM".equals(example.getName()) || "SYS".equals(example.getName()) || "CTXSYS".equals(example.getName()) || "XDB".equals(example.getName())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ("SYSTEM".equals(example.getSchema().getCatalogName()) || "SYS".equals(example.getSchema().getCatalogName()) || "CTXSYS".equals(example.getSchema().getCatalogName()) || "XDB".equals(example.getSchema().getCatalogName())) {
|
||||
return true;
|
||||
}
|
||||
} else if (this.isSystemObject(example.getSchema())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (example instanceof Catalog) {
|
||||
if ("SYSTEM".equals(example.getName()) || "SYS".equals(example.getName()) || "CTXSYS".equals(example.getName()) || "XDB".equals(example.getName())) {
|
||||
return true;
|
||||
}
|
||||
} else if (example.getName() != null) {
|
||||
if (example.getName().startsWith("BIN$")) {
|
||||
boolean filteredInOriginalQuery = this.canAccessDbaRecycleBin();
|
||||
if (!filteredInOriginalQuery) {
|
||||
filteredInOriginalQuery = StringUtil.trimToEmpty(example.getSchema().getName()).equalsIgnoreCase(this.getConnection().getConnectionUserName());
|
||||
}
|
||||
|
||||
if (!filteredInOriginalQuery) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !(example instanceof PrimaryKey) && !(example instanceof Index) && !(example instanceof UniqueConstraint);
|
||||
}
|
||||
|
||||
if (example.getName().startsWith("AQ$")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (example.getName().startsWith("DR$")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (example.getName().startsWith("SYS_IOT_OVER")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((example.getName().startsWith("MDRT_") || example.getName().startsWith("MDRS_")) && example.getName().endsWith("$")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (example.getName().startsWith("MLOG$_")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (example.getName().startsWith("RUPD$_")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (example.getName().startsWith("WM$_")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ("CREATE$JAVA$LOB$TABLE".equals(example.getName())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ("JAVA$CLASS$MD5$TABLE".equals(example.getName())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (example.getName().startsWith("ISEQ$$_")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (example.getName().startsWith("USLOG$")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (example.getName().startsWith("SYS_FBA")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return super.isSystemObject(example);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean supportsTablespaces() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean supportsAutoIncrement() {
|
||||
boolean isAutoIncrementSupported = false;
|
||||
|
||||
try {
|
||||
if (this.getDatabaseMajorVersion() >= 12) {
|
||||
isAutoIncrementSupported = true;
|
||||
}
|
||||
} catch (DatabaseException var3) {
|
||||
isAutoIncrementSupported = false;
|
||||
}
|
||||
|
||||
return isAutoIncrementSupported;
|
||||
}
|
||||
|
||||
public boolean supportsRestrictForeignKeys() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getDataTypeMaxParameters(String dataTypeName) {
|
||||
if ("BINARY_FLOAT".equals(dataTypeName.toUpperCase())) {
|
||||
return 0;
|
||||
} else {
|
||||
return "BINARY_DOUBLE".equals(dataTypeName.toUpperCase()) ? 0 : super.getDataTypeMaxParameters(dataTypeName);
|
||||
}
|
||||
}
|
||||
|
||||
public String getSystemTableWhereClause(String tableNameColumn) {
|
||||
List<String> clauses = new ArrayList(Arrays.asList("BIN$", "AQ$", "DR$", "SYS_IOT_OVER", "MLOG$_", "RUPD$_", "WM$_", "ISEQ$$_", "USLOG$", "SYS_FBA"));
|
||||
clauses.replaceAll((s) -> tableNameColumn + " NOT LIKE '" + s + "%'");
|
||||
return "(" + StringUtil.join(clauses, " AND ") + ")";
|
||||
}
|
||||
|
||||
public boolean jdbcCallsCatalogsSchemas() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Set<String> getUserDefinedTypes() {
|
||||
if (this.userDefinedTypes == null) {
|
||||
this.userDefinedTypes = new HashSet();
|
||||
if (this.getConnection() != null && !(this.getConnection() instanceof OfflineConnection)) {
|
||||
try {
|
||||
try {
|
||||
this.userDefinedTypes.addAll(((ExecutorService)Scope.getCurrentScope().getSingleton(ExecutorService.class)).getExecutor("jdbc", this).queryForList(new RawParameterizedSqlStatement("SELECT DISTINCT TYPE_NAME FROM ALL_TYPES"), String.class));
|
||||
} catch (DatabaseException var2) {
|
||||
this.userDefinedTypes.addAll(((ExecutorService)Scope.getCurrentScope().getSingleton(ExecutorService.class)).getExecutor("jdbc", this).queryForList(new RawParameterizedSqlStatement("SELECT TYPE_NAME FROM USER_TYPES"), String.class));
|
||||
}
|
||||
} catch (DatabaseException var3) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.userDefinedTypes;
|
||||
}
|
||||
|
||||
public String generateDatabaseFunctionValue(DatabaseFunction databaseFunction) {
|
||||
if (databaseFunction != null && "current_timestamp".equalsIgnoreCase(databaseFunction.toString())) {
|
||||
return databaseFunction.toString();
|
||||
} else if (!(databaseFunction instanceof SequenceNextValueFunction) && !(databaseFunction instanceof SequenceCurrentValueFunction)) {
|
||||
return super.generateDatabaseFunctionValue(databaseFunction);
|
||||
} else {
|
||||
String quotedSeq = super.generateDatabaseFunctionValue(databaseFunction);
|
||||
return quotedSeq.replaceFirst("\"([^.\"]+)\\.([^.\"]+)\"", "\"$1\".\"$2\"");
|
||||
}
|
||||
}
|
||||
|
||||
public ValidationErrors validate() {
|
||||
ValidationErrors errors = super.validate();
|
||||
DatabaseConnection connection = this.getConnection();
|
||||
if (connection != null && !(connection instanceof OfflineConnection)) {
|
||||
if (!this.canAccessDbaRecycleBin()) {
|
||||
errors.addWarning(this.getDbaRecycleBinWarning());
|
||||
}
|
||||
|
||||
return errors;
|
||||
} else {
|
||||
Scope.getCurrentScope().getLog(this.getClass()).info("Cannot validate offline database");
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
|
||||
public String getDbaRecycleBinWarning() {
|
||||
return "Liquibase needs to access the DBA_RECYCLEBIN table so we can automatically handle the case where constraints are deleted and restored. Since Oracle doesn't properly restore the original table names referenced in the constraint, we use the information from the DBA_RECYCLEBIN to automatically correct this issue.\n\nThe user you used to connect to the database (" + this.getConnection().getConnectionUserName() + ") needs to have \"SELECT ON SYS.DBA_RECYCLEBIN\" permissions set before we can perform this operation. Please run the following SQL to set the appropriate permissions, and try running the command again.\n\n GRANT SELECT ON SYS.DBA_RECYCLEBIN TO " + this.getConnection().getConnectionUserName() + ";";
|
||||
}
|
||||
|
||||
public boolean canAccessDbaRecycleBin() {
|
||||
if (this.canAccessDbaRecycleBin == null) {
|
||||
DatabaseConnection connection = this.getConnection();
|
||||
if (connection == null || connection instanceof OfflineConnection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Statement statement = null;
|
||||
|
||||
try {
|
||||
statement = ((JdbcConnection)connection).createStatement();
|
||||
ResultSet resultSet = statement.executeQuery("select 1 from dba_recyclebin where 0=1");
|
||||
resultSet.close();
|
||||
this.canAccessDbaRecycleBin = true;
|
||||
} catch (Exception var7) {
|
||||
if (var7 instanceof SQLException && var7.getMessage().startsWith("ORA-00942")) {
|
||||
this.canAccessDbaRecycleBin = false;
|
||||
} else {
|
||||
Scope.getCurrentScope().getLog(this.getClass()).warning("Cannot check dba_recyclebin access", var7);
|
||||
this.canAccessDbaRecycleBin = false;
|
||||
}
|
||||
} finally {
|
||||
JdbcUtil.close((ResultSet)null, statement);
|
||||
}
|
||||
}
|
||||
|
||||
return this.canAccessDbaRecycleBin;
|
||||
}
|
||||
|
||||
public boolean supportsNotNullConstraintNames() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isValidOracleIdentifier(String identifier, Class<? extends DatabaseObject> type) {
|
||||
if (identifier != null && identifier.length() >= 1) {
|
||||
if (!identifier.matches("^(i?)[A-Z][A-Z0-9\\$\\_\\#]*$")) {
|
||||
return false;
|
||||
} else {
|
||||
return identifier.length() <= 128;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int getIdentifierMaximumLength() {
|
||||
try {
|
||||
if (this.getDatabaseMajorVersion() < 12) {
|
||||
return 30;
|
||||
} else {
|
||||
return this.getDatabaseMajorVersion() == 12 && this.getDatabaseMinorVersion() <= 1 ? 30 : 128;
|
||||
}
|
||||
} catch (DatabaseException ex) {
|
||||
throw new UnexpectedLiquibaseException("Cannot determine the Oracle database version number", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean supportsDatabaseChangeLogHistory() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String correctObjectName(String objectName, Class<? extends DatabaseObject> objectType) {
|
||||
return objectType.equals(Column.class) && StringUtils.startsWithIgnoreCase(objectName, "int") ? "NUMBER(*, 0)" : super.correctObjectName(objectName, objectType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package liquibase.datatype.core;
|
||||
|
||||
import liquibase.change.core.LoadDataChange;
|
||||
import liquibase.database.Database;
|
||||
import liquibase.database.core.*;
|
||||
import liquibase.datatype.DataTypeInfo;
|
||||
import liquibase.datatype.DatabaseDataType;
|
||||
import liquibase.datatype.LiquibaseDataType;
|
||||
import liquibase.exception.UnexpectedLiquibaseException;
|
||||
import liquibase.statement.DatabaseFunction;
|
||||
import liquibase.util.StringUtil;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@DataTypeInfo(name = "boolean", aliases = {"java.sql.Types.BOOLEAN", "java.lang.Boolean", "bit", "bool"}, minParameters = 0, maxParameters = 0, priority = LiquibaseDataType.PRIORITY_DEFAULT)
|
||||
public class BooleanType extends LiquibaseDataType {
|
||||
|
||||
@Override
|
||||
public DatabaseDataType toDatabaseDataType(Database database) {
|
||||
String originalDefinition = StringUtil.trimToEmpty(getRawDefinition());
|
||||
// if ((database instanceof Firebird3Database)) {
|
||||
// return new DatabaseDataType("BOOLEAN");
|
||||
// }
|
||||
|
||||
if ((database instanceof AbstractDb2Database) || (database instanceof FirebirdDatabase)) {
|
||||
return new DatabaseDataType("SMALLINT");
|
||||
} else if (database instanceof MSSQLDatabase) {
|
||||
return new DatabaseDataType(database.escapeDataTypeName("bit"));
|
||||
} else if (database instanceof MySQLDatabase) {
|
||||
if (originalDefinition.toLowerCase(Locale.US).startsWith("bit")) {
|
||||
return new DatabaseDataType("BIT", getParameters());
|
||||
}
|
||||
return new DatabaseDataType("BIT", 1);
|
||||
} else if (database instanceof OracleDatabase) {
|
||||
return new DatabaseDataType("NUMBER", 1);
|
||||
} else if ((database instanceof SybaseASADatabase) || (database instanceof SybaseDatabase)) {
|
||||
return new DatabaseDataType("BIT");
|
||||
} else if (database instanceof DerbyDatabase) {
|
||||
if (((DerbyDatabase) database).supportsBooleanDataType()) {
|
||||
return new DatabaseDataType("BOOLEAN");
|
||||
} else {
|
||||
return new DatabaseDataType("SMALLINT");
|
||||
}
|
||||
} else if (database.getClass().isAssignableFrom(DB2Database.class)) {
|
||||
if (((DB2Database) database).supportsBooleanDataType())
|
||||
return new DatabaseDataType("BOOLEAN");
|
||||
else
|
||||
return new DatabaseDataType("SMALLINT");
|
||||
} else if (database instanceof HsqlDatabase) {
|
||||
return new DatabaseDataType("BOOLEAN");
|
||||
} else if (database instanceof PostgresDatabase) {
|
||||
if (originalDefinition.toLowerCase(Locale.US).startsWith("bit")) {
|
||||
return new DatabaseDataType("BIT", getParameters());
|
||||
}
|
||||
} else if(database instanceof DmDatabase) {
|
||||
return new DatabaseDataType("bit");
|
||||
}
|
||||
|
||||
return super.toDatabaseDataType(database);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String objectToSql(Object value, Database database) {
|
||||
if ((value == null) || "null".equals(value.toString().toLowerCase(Locale.US))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String returnValue;
|
||||
if (value instanceof String) {
|
||||
value = ((String) value).replaceAll("'", "");
|
||||
if ("true".equals(((String) value).toLowerCase(Locale.US)) || "1".equals(value) || "b'1'".equals(((String) value).toLowerCase(Locale.US)) || "t".equals(((String) value).toLowerCase(Locale.US)) || ((String) value).toLowerCase(Locale.US).equals(this.getTrueBooleanValue(database).toLowerCase(Locale.US))) {
|
||||
returnValue = this.getTrueBooleanValue(database);
|
||||
} else if ("false".equals(((String) value).toLowerCase(Locale.US)) || "0".equals(value) || "b'0'".equals(
|
||||
((String) value).toLowerCase(Locale.US)) || "f".equals(((String) value).toLowerCase(Locale.US)) || ((String) value).toLowerCase(Locale.US).equals(this.getFalseBooleanValue(database).toLowerCase(Locale.US))) {
|
||||
returnValue = this.getFalseBooleanValue(database);
|
||||
} else {
|
||||
throw new UnexpectedLiquibaseException("Unknown boolean value: " + value);
|
||||
}
|
||||
} else if (value instanceof Long) {
|
||||
if (Long.valueOf(1).equals(value)) {
|
||||
returnValue = this.getTrueBooleanValue(database);
|
||||
} else {
|
||||
returnValue = this.getFalseBooleanValue(database);
|
||||
}
|
||||
} else if (value instanceof Number) {
|
||||
if (value.equals(1) || "1".equals(value.toString()) || "1.0".equals(value.toString())) {
|
||||
returnValue = this.getTrueBooleanValue(database);
|
||||
} else {
|
||||
returnValue = this.getFalseBooleanValue(database);
|
||||
}
|
||||
} else if (value instanceof DatabaseFunction) {
|
||||
return value.toString();
|
||||
} else if (value instanceof Boolean) {
|
||||
if (((Boolean) value)) {
|
||||
returnValue = this.getTrueBooleanValue(database);
|
||||
} else {
|
||||
returnValue = this.getFalseBooleanValue(database);
|
||||
}
|
||||
} else {
|
||||
throw new UnexpectedLiquibaseException("Cannot convert type " + value.getClass() + " to a boolean value");
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
protected boolean isNumericBoolean(Database database) {
|
||||
if (database instanceof DerbyDatabase) {
|
||||
return !((DerbyDatabase) database).supportsBooleanDataType();
|
||||
} else if (database.getClass().isAssignableFrom(DB2Database.class)) {
|
||||
return !((DB2Database) database).supportsBooleanDataType();
|
||||
}
|
||||
return (database instanceof Db2zDatabase) || (database instanceof DB2Database) || (database instanceof FirebirdDatabase) || (database instanceof
|
||||
MSSQLDatabase) || (database instanceof MySQLDatabase) || (database instanceof OracleDatabase) ||
|
||||
(database instanceof SQLiteDatabase) || (database instanceof SybaseASADatabase) || (database instanceof
|
||||
SybaseDatabase) || (database instanceof DmDatabase);
|
||||
}
|
||||
|
||||
/**
|
||||
* The database-specific value to use for "false" "boolean" columns.
|
||||
*/
|
||||
public String getFalseBooleanValue(Database database) {
|
||||
if (isNumericBoolean(database)) {
|
||||
return "0";
|
||||
}
|
||||
if (database instanceof InformixDatabase) {
|
||||
return "'f'";
|
||||
}
|
||||
return "FALSE";
|
||||
}
|
||||
|
||||
/**
|
||||
* The database-specific value to use for "true" "boolean" columns.
|
||||
*/
|
||||
public String getTrueBooleanValue(Database database) {
|
||||
if (isNumericBoolean(database)) {
|
||||
return "1";
|
||||
}
|
||||
if (database instanceof InformixDatabase) {
|
||||
return "'t'";
|
||||
}
|
||||
return "TRUE";
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoadDataChange.LOAD_DATA_TYPE getLoadTypeName() {
|
||||
return LoadDataChange.LOAD_DATA_TYPE.BOOLEAN;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
防止IDEA将`.`和`/`混为一谈
|
||||
@@ -0,0 +1,21 @@
|
||||
liquibase.database.core.CockroachDatabase
|
||||
liquibase.database.core.DB2Database
|
||||
liquibase.database.core.Db2zDatabase
|
||||
liquibase.database.core.DerbyDatabase
|
||||
#liquibase.database.core.Firebird3Database
|
||||
liquibase.database.core.FirebirdDatabase
|
||||
liquibase.database.core.H2Database
|
||||
liquibase.database.core.HsqlDatabase
|
||||
liquibase.database.core.InformixDatabase
|
||||
liquibase.database.core.Ingres9Database
|
||||
liquibase.database.core.MSSQLDatabase
|
||||
liquibase.database.core.MariaDBDatabase
|
||||
liquibase.database.core.MockDatabase
|
||||
liquibase.database.core.MySQLDatabase
|
||||
liquibase.database.core.OracleDatabase
|
||||
liquibase.database.core.PostgresDatabase
|
||||
liquibase.database.core.SQLiteDatabase
|
||||
liquibase.database.core.SybaseASADatabase
|
||||
liquibase.database.core.SybaseDatabase
|
||||
liquibase.database.core.DmDatabase
|
||||
liquibase.database.core.UnsupportedDatabase
|
||||
@@ -0,0 +1,113 @@
|
||||
--- #################### 注册中心 + 配置中心相关配置 ####################
|
||||
|
||||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
server-addr: 127.0.0.1:8848 # Nacos 服务器地址
|
||||
username: # Nacos 账号
|
||||
password: # Nacos 密码
|
||||
discovery: # 【配置中心】配置项
|
||||
namespace: dev # 命名空间。这里使用 dev 开发环境
|
||||
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
|
||||
metadata:
|
||||
version: 1.0.0 # 服务实例的版本号,可用于灰度发布
|
||||
config: # 【注册中心】配置项
|
||||
namespace: dev # 命名空间。这里使用 dev 开发环境
|
||||
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
|
||||
|
||||
--- #################### 数据库相关配置 ####################
|
||||
spring:
|
||||
# 数据源配置项
|
||||
autoconfigure:
|
||||
exclude:
|
||||
datasource:
|
||||
druid: # Druid 【监控】相关的全局配置
|
||||
web-stat-filter:
|
||||
enabled: true
|
||||
stat-view-servlet:
|
||||
enabled: true
|
||||
allow: # 设置白名单,不填则允许所有访问
|
||||
url-pattern: /druid/*
|
||||
login-username: # 控制台管理用户名和密码
|
||||
login-password:
|
||||
filter:
|
||||
stat:
|
||||
enabled: true
|
||||
log-slow-sql: true # 慢 SQL 记录
|
||||
slow-sql-millis: 100
|
||||
merge-sql: true
|
||||
wall:
|
||||
config:
|
||||
multi-statement-allow: true
|
||||
dynamic: # 多数据源配置
|
||||
druid: # Druid 【连接池】相关的全局配置
|
||||
initial-size: 5 # 初始连接数
|
||||
min-idle: 10 # 最小连接池数量
|
||||
max-active: 20 # 最大连接池数量
|
||||
max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒
|
||||
time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒
|
||||
min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒
|
||||
max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒
|
||||
validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效
|
||||
test-while-idle: true
|
||||
test-on-borrow: false
|
||||
test-on-return: false
|
||||
primary: master
|
||||
datasource:
|
||||
master:
|
||||
url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例
|
||||
username: root
|
||||
password: 123456
|
||||
slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改
|
||||
lazy: true # 开启懒加载,保证启动速度
|
||||
url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例
|
||||
username: root
|
||||
password: 123456
|
||||
|
||||
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
|
||||
data:
|
||||
redis:
|
||||
host: 400-infra.server.iocoder.cn # 地址
|
||||
port: 6379 # 端口
|
||||
database: 1 # 数据库索引
|
||||
# password: 123456 # 密码,建议生产环境开启
|
||||
|
||||
--- #################### MQ 消息队列相关配置 ####################
|
||||
|
||||
--- #################### 定时任务相关配置 ####################
|
||||
xxl:
|
||||
job:
|
||||
admin:
|
||||
addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址
|
||||
|
||||
--- #################### 服务保障相关配置 ####################
|
||||
|
||||
# Lock4j 配置项
|
||||
lock4j:
|
||||
acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒
|
||||
expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒
|
||||
|
||||
--- #################### 监控相关配置 ####################
|
||||
|
||||
# Actuator 监控端点的配置项
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator
|
||||
exposure:
|
||||
include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。
|
||||
|
||||
# Spring Boot Admin 配置项
|
||||
spring:
|
||||
boot:
|
||||
admin:
|
||||
# Spring Boot Admin Client 客户端的相关配置
|
||||
client:
|
||||
instance:
|
||||
service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]
|
||||
|
||||
--- #################### 芋道相关配置 ####################
|
||||
|
||||
# 芋道配置项,设置当前项目所有自定义的配置
|
||||
yudao:
|
||||
demo: true # 开启演示模式
|
||||
@@ -0,0 +1,137 @@
|
||||
--- #################### 注册中心 + 配置中心相关配置 ####################
|
||||
|
||||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
server-addr: 127.0.0.1:8848 # Nacos 服务器地址
|
||||
username: # Nacos 账号
|
||||
password: # Nacos 密码
|
||||
discovery: # 【配置中心】配置项
|
||||
namespace: dev # 命名空间。这里使用 dev 开发环境
|
||||
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
|
||||
metadata:
|
||||
version: 1.0.0 # 服务实例的版本号,可用于灰度发布
|
||||
config: # 【注册中心】配置项
|
||||
namespace: dev # 命名空间。这里使用 dev 开发环境
|
||||
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
|
||||
|
||||
--- #################### 数据库相关配置 ####################
|
||||
spring:
|
||||
# 数据源配置项
|
||||
autoconfigure:
|
||||
exclude:
|
||||
- de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # 禁用 Spring Boot Admin 的 Client 的自动配置
|
||||
datasource:
|
||||
druid: # Druid 【监控】相关的全局配置
|
||||
web-stat-filter:
|
||||
enabled: true
|
||||
stat-view-servlet:
|
||||
enabled: true
|
||||
allow: # 设置白名单,不填则允许所有访问
|
||||
url-pattern: /druid/*
|
||||
login-username: # 控制台管理用户名和密码
|
||||
login-password:
|
||||
filter:
|
||||
stat:
|
||||
enabled: true
|
||||
log-slow-sql: true # 慢 SQL 记录
|
||||
slow-sql-millis: 100
|
||||
merge-sql: true
|
||||
wall:
|
||||
config:
|
||||
multi-statement-allow: true
|
||||
dynamic: # 多数据源配置
|
||||
druid: # Druid 【连接池】相关的全局配置
|
||||
initial-size: 1 # 初始连接数
|
||||
min-idle: 1 # 最小连接池数量
|
||||
max-active: 20 # 最大连接池数量
|
||||
max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒
|
||||
time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒
|
||||
min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒
|
||||
max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒
|
||||
validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效
|
||||
test-while-idle: true
|
||||
test-on-borrow: false
|
||||
test-on-return: false
|
||||
primary: master
|
||||
datasource:
|
||||
master:
|
||||
url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例
|
||||
# url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例
|
||||
# url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例
|
||||
# url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例
|
||||
# url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro # SQLServer 连接的示例
|
||||
# url: jdbc:dm://172.16.46.247:1050?schema=RUOYI-VUE-PRO # DM 连接的示例
|
||||
username: root
|
||||
password: 123456
|
||||
# username: sa # SQL Server 连接的示例
|
||||
# password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W # SQL Server 连接的示例
|
||||
# username: SYSDBA # DM 连接的示例
|
||||
# password: pgbsci6ddJ6Sqj@e # DM 连接的示例
|
||||
slave: # 模拟从库,可根据自己需要修改
|
||||
lazy: true # 开启懒加载,保证启动速度
|
||||
url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # DM 连接的示例
|
||||
username: SYSDBA # DM 连接的示例
|
||||
password: pgbsci6ddJ6Sqj@e # DM 连接的示例
|
||||
|
||||
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
|
||||
data:
|
||||
redis:
|
||||
host: 127.0.0.1 # 地址
|
||||
port: 6379 # 端口
|
||||
database: 0 # 数据库索引
|
||||
# password: 123456 # 密码,建议生产环境开启
|
||||
|
||||
--- #################### MQ 消息队列相关配置 ####################
|
||||
|
||||
--- #################### 定时任务相关配置 ####################
|
||||
|
||||
xxl:
|
||||
job:
|
||||
enabled: false # 是否开启调度中心,默认为 true 开启
|
||||
admin:
|
||||
addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址
|
||||
|
||||
--- #################### 服务保障相关配置 ####################
|
||||
|
||||
# Lock4j 配置项
|
||||
lock4j:
|
||||
acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒
|
||||
expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒
|
||||
|
||||
--- #################### 监控相关配置 ####################
|
||||
|
||||
# Actuator 监控端点的配置项
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator
|
||||
exposure:
|
||||
include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。
|
||||
|
||||
# Spring Boot Admin 配置项
|
||||
spring:
|
||||
boot:
|
||||
admin:
|
||||
# Spring Boot Admin Client 客户端的相关配置
|
||||
client:
|
||||
instance:
|
||||
service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME]
|
||||
|
||||
# 日志文件配置
|
||||
logging:
|
||||
level:
|
||||
# 配置自己写的 MyBatis Mapper 打印日志
|
||||
cn.iocoder.yudao.module.bpm.dal.mysql: debug
|
||||
org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示
|
||||
|
||||
--- #################### 芋道相关配置 ####################
|
||||
|
||||
# 芋道配置项,设置当前项目所有自定义的配置
|
||||
yudao:
|
||||
env: # 多环境的配置项
|
||||
tag: ${HOSTNAME}
|
||||
security:
|
||||
mock-enable: true
|
||||
access-log: # 访问日志的配置项
|
||||
enable: false
|
||||
@@ -0,0 +1,136 @@
|
||||
spring:
|
||||
application:
|
||||
name: bpm-server
|
||||
|
||||
profiles:
|
||||
active: local
|
||||
|
||||
main:
|
||||
allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。
|
||||
allow-bean-definition-overriding: true # 允许 Bean 覆盖,例如说 Feign 等会存在重复定义的服务
|
||||
|
||||
config:
|
||||
import:
|
||||
- optional:classpath:application-${spring.profiles.active}.yaml # 加载【本地】配置
|
||||
- optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 加载【Nacos】的配置
|
||||
|
||||
# Servlet 配置
|
||||
servlet:
|
||||
# 文件上传相关配置项
|
||||
multipart:
|
||||
max-file-size: 16MB # 单个文件大小
|
||||
max-request-size: 32MB # 设置总上传的文件大小
|
||||
|
||||
# Jackson 配置项
|
||||
jackson:
|
||||
serialization:
|
||||
write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳
|
||||
write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401
|
||||
write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳
|
||||
fail-on-empty-beans: false # 允许序列化无属性的 Bean
|
||||
|
||||
# Cache 配置项
|
||||
cache:
|
||||
type: REDIS
|
||||
redis:
|
||||
time-to-live: 1h # 设置过期时间为 1 小时
|
||||
|
||||
server:
|
||||
port: 48083
|
||||
|
||||
logging:
|
||||
file:
|
||||
name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径
|
||||
|
||||
--- #################### 接口文档配置 ####################
|
||||
|
||||
springdoc:
|
||||
api-docs:
|
||||
enabled: true # 1. 是否开启 Swagger 接文档的元数据
|
||||
path: /v3/api-docs
|
||||
swagger-ui:
|
||||
enabled: true # 2.1 是否开启 Swagger 文档的官方 UI 界面
|
||||
path: /swagger-ui
|
||||
default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档
|
||||
|
||||
knife4j:
|
||||
enable: false # TODO 芋艿:需要关闭增强,具体原因见:https://github.com/xiaoymin/knife4j/issues/874
|
||||
setting:
|
||||
language: zh_cn
|
||||
|
||||
# 工作流 Flowable 配置
|
||||
flowable:
|
||||
# 1. false: 默认值,Flowable 启动时,对比数据库表中保存的版本,如果不匹配。将抛出异常
|
||||
# 2. true: 启动时会对数据库中所有表进行更新操作,如果表存在,不做处理,反之,自动创建表
|
||||
# 3. create_drop: 启动时自动创建表,关闭时自动删除表
|
||||
# 4. drop_create: 启动时,删除旧表,再创建新表
|
||||
database-schema-update: false # 设置为 false,可通过 https://github.com/flowable/flowable-sql 初始化
|
||||
db-history-used: true # flowable6 默认 true 生成信息表,无需手动设置
|
||||
check-process-definitions: false # 设置为 false,禁用 /resources/processes 自动部署 BPMN XML 流程
|
||||
history-level: audit # full:保存历史数据的最高级别,可保存全部流程相关细节,包括流程流转各节点参数
|
||||
|
||||
# MyBatis Plus 的配置项
|
||||
mybatis-plus:
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
|
||||
global-config:
|
||||
db-config:
|
||||
id-type: NONE # “智能”模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。
|
||||
# id-type: AUTO # 自增 ID,适合 MySQL 等直接自增的数据库
|
||||
# id-type: INPUT # 用户输入 ID,适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库
|
||||
# id-type: ASSIGN_ID # 分配 ID,默认使用雪花算法。注意,Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时,需要去除实体类上的 @KeySequence 注解
|
||||
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
|
||||
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
|
||||
banner: false # 关闭控制台的 Banner 打印
|
||||
type-aliases-package: ${yudao.info.base-package}.dal.dataobject
|
||||
encryptor:
|
||||
password: XDV71a+xqStEA3WH # 加解密的秘钥,可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成
|
||||
|
||||
mybatis-plus-join:
|
||||
banner: false # 关闭控制台的 Banner 打印
|
||||
|
||||
# Spring Data Redis 配置
|
||||
spring:
|
||||
data:
|
||||
redis:
|
||||
repositories:
|
||||
enabled: false # 项目未使用到 Spring Data Redis 的 Repository,所以直接禁用,保证启动速度
|
||||
|
||||
# VO 转换(数据翻译)相关
|
||||
easy-trans:
|
||||
is-enable-global: true # 启用全局翻译(拦截所有 SpringMVC ResponseBody 进行自动翻译 )。如果对于性能要求很高可关闭此配置,或通过 @IgnoreTrans 忽略某个接口
|
||||
|
||||
--- #################### RPC 远程调用相关配置 ####################
|
||||
|
||||
--- #################### MQ 消息队列相关配置 ####################
|
||||
|
||||
--- #################### 定时任务相关配置 ####################
|
||||
|
||||
xxl:
|
||||
job:
|
||||
executor:
|
||||
appname: ${spring.application.name} # 执行器 AppName
|
||||
logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径
|
||||
accessToken: default_token # 执行器通讯TOKEN
|
||||
|
||||
--- #################### 芋道相关配置 ####################
|
||||
|
||||
yudao:
|
||||
info:
|
||||
version: 1.0.0
|
||||
base-package: cn.iocoder.yudao.module.bpm
|
||||
web:
|
||||
admin-ui:
|
||||
url: http://dashboard.yudao.iocoder.cn # Admin 管理后台 UI 的地址
|
||||
xss:
|
||||
enable: false
|
||||
exclude-urls: # 如下 url,仅仅是为了演示,去掉配置也没关系
|
||||
- ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
|
||||
swagger:
|
||||
title: 管理后台
|
||||
description: 提供管理员管理的所有功能
|
||||
version: ${yudao.info.version}
|
||||
tenant: # 多租户相关配置项
|
||||
enable: true
|
||||
|
||||
debug: false
|
||||
@@ -0,0 +1,76 @@
|
||||
<configuration>
|
||||
<!-- 引用 Spring Boot 的 logback 基础配置 -->
|
||||
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
|
||||
<!-- 变量 yudao.info.base-package,基础业务包 -->
|
||||
<springProperty scope="context" name="yudao.info.base-package" source="yudao.info.base-package"/>
|
||||
<!-- 格式化输出:%d 表示日期,%X{tid} SkWalking 链路追踪编号,%thread 表示线程名,%-5level:级别从左显示 5 个字符宽度,%msg:日志消息,%n是换行符 -->
|
||||
<property name="PATTERN_DEFAULT" value="%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} | %highlight(${LOG_LEVEL_PATTERN:-%5p} ${PID:- }) | %boldYellow(%thread [%tid]) %boldGreen(%-40.40logger{39}) | %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
|
||||
|
||||
<!-- 控制台 Appender -->
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
|
||||
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
|
||||
<pattern>${PATTERN_DEFAULT}</pattern>
|
||||
</layout>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 文件 Appender -->
|
||||
<!-- 参考 Spring Boot 的 file-appender.xml 编写 -->
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
|
||||
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
|
||||
<pattern>${PATTERN_DEFAULT}</pattern>
|
||||
</layout>
|
||||
</encoder>
|
||||
<!-- 日志文件名 -->
|
||||
<file>${LOG_FILE}</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<!-- 滚动后的日志文件名 -->
|
||||
<fileNamePattern>${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz}</fileNamePattern>
|
||||
<!-- 启动服务时,是否清理历史日志,一般不建议清理 -->
|
||||
<cleanHistoryOnStart>${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false}</cleanHistoryOnStart>
|
||||
<!-- 日志文件,到达多少容量,进行滚动 -->
|
||||
<maxFileSize>${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB}</maxFileSize>
|
||||
<!-- 日志文件的总大小,0 表示不限制 -->
|
||||
<totalSizeCap>${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0}</totalSizeCap>
|
||||
<!-- 日志文件的保留天数 -->
|
||||
<maxHistory>${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-30}</maxHistory>
|
||||
</rollingPolicy>
|
||||
</appender>
|
||||
<!-- 异步写入日志,提升性能 -->
|
||||
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
|
||||
<!-- 不丢失日志。默认的,如果队列的 80% 已满,则会丢弃 TRACT、DEBUG、INFO 级别的日志 -->
|
||||
<discardingThreshold>0</discardingThreshold>
|
||||
<!-- 更改默认的队列的深度,该值会影响性能。默认值为 256 -->
|
||||
<queueSize>256</queueSize>
|
||||
<appender-ref ref="FILE"/>
|
||||
</appender>
|
||||
|
||||
<!-- SkyWalking GRPC 日志收集,实现日志中心。注意:SkyWalking 8.4.0 版本开始支持 -->
|
||||
<appender name="GRPC" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
|
||||
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
|
||||
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
|
||||
<pattern>${PATTERN_DEFAULT}</pattern>
|
||||
</layout>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 本地环境 -->
|
||||
<springProfile name="local">
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
<appender-ref ref="GRPC"/> <!-- 本地环境下,如果不想接入 SkyWalking 日志服务,可以注释掉本行 -->
|
||||
<appender-ref ref="ASYNC"/> <!-- 本地环境下,如果不想打印日志,可以注释掉本行 -->
|
||||
</root>
|
||||
</springProfile>
|
||||
<!-- 其它环境 -->
|
||||
<springProfile name="dev,test,stage,prod,default">
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
<appender-ref ref="ASYNC"/>
|
||||
<appender-ref ref="GRPC"/>
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
</configuration>
|
||||
@@ -0,0 +1,274 @@
|
||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.other.BpmTaskCandidateAssignEmptyStrategy;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
|
||||
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.bpmn.model.ExtensionElement;
|
||||
import org.flowable.bpmn.model.FlowElement;
|
||||
import org.flowable.bpmn.model.UserTask;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.internal.util.collections.Sets;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
|
||||
import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_NAMESPACE;
|
||||
import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_PREFIX;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* {@link BpmTaskCandidateInvoker} 的单元测试
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class BpmTaskCandidateInvokerTest extends BaseMockitoUnitTest {
|
||||
|
||||
private BpmTaskCandidateInvoker taskCandidateInvoker;
|
||||
|
||||
@Mock
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@Mock
|
||||
private BpmProcessInstanceService processInstanceService;
|
||||
|
||||
@Spy
|
||||
private BpmTaskCandidateStrategy userStrategy;
|
||||
@Mock
|
||||
private BpmTaskCandidateAssignEmptyStrategy emptyStrategy;
|
||||
|
||||
@Spy
|
||||
private List<BpmTaskCandidateStrategy> strategyList;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
userStrategy = new BpmTaskCandidateUserStrategy(); // 创建 strategy 实例
|
||||
when(emptyStrategy.getStrategy()).thenReturn(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY);
|
||||
strategyList = List.of(userStrategy, emptyStrategy); // 创建 strategyList
|
||||
taskCandidateInvoker = new BpmTaskCandidateInvoker(strategyList, adminUserApi);
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景:成功计算到候选人,但是移除了发起人的用户
|
||||
*/
|
||||
@Test
|
||||
public void testCalculateUsersByTask_some() {
|
||||
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
|
||||
// 准备参数
|
||||
String param = "1,2";
|
||||
DelegateExecution execution = mock(DelegateExecution.class);
|
||||
// mock 方法(DelegateExecution)
|
||||
UserTask userTask = mock(UserTask.class);
|
||||
String processInstanceId = randomString();
|
||||
when(execution.getProcessInstanceId()).thenReturn(processInstanceId);
|
||||
when(execution.getCurrentFlowElement()).thenReturn(userTask);
|
||||
when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)))
|
||||
.thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy().toString());
|
||||
when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM)))
|
||||
.thenReturn(param);
|
||||
// mock 方法(adminUserApi)
|
||||
AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L)
|
||||
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
||||
AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L)
|
||||
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
||||
Map<Long, AdminUserRespDTO> userMap = MapUtil.builder(user1.getId(), user1)
|
||||
.put(user2.getId(), user2).build();
|
||||
when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap);
|
||||
// mock 移除发起人的用户
|
||||
springUtilMockedStatic.when(() -> SpringUtil.getBean(BpmProcessInstanceService.class))
|
||||
.thenReturn(processInstanceService);
|
||||
ProcessInstance processInstance = mock(ProcessInstance.class);
|
||||
when(processInstanceService.getProcessInstance(eq(processInstanceId))).thenReturn(processInstance);
|
||||
when(processInstance.getStartUserId()).thenReturn("1");
|
||||
mockFlowElementExtensionElement(userTask, BpmnModelConstants.USER_TASK_ASSIGN_START_USER_HANDLER_TYPE,
|
||||
String.valueOf(BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType()));
|
||||
|
||||
// 调用
|
||||
Set<Long> results = taskCandidateInvoker.calculateUsersByTask(execution);
|
||||
// 断言
|
||||
assertEquals(asSet(2L), results);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景:没有计算到候选人,但是被禁用移除,最终通过 empty 进行分配
|
||||
*/
|
||||
@Test
|
||||
public void testCalculateUsersByTask_none() {
|
||||
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
|
||||
// 准备参数
|
||||
String param = "1,2";
|
||||
DelegateExecution execution = mock(DelegateExecution.class);
|
||||
// mock 方法(DelegateExecution)
|
||||
UserTask userTask = mock(UserTask.class);
|
||||
String processInstanceId = randomString();
|
||||
when(execution.getProcessInstanceId()).thenReturn(processInstanceId);
|
||||
when(execution.getCurrentFlowElement()).thenReturn(userTask);
|
||||
when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)))
|
||||
.thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy().toString());
|
||||
when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM)))
|
||||
.thenReturn(param);
|
||||
// mock 方法(adminUserApi)
|
||||
AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L)
|
||||
.setStatus(CommonStatusEnum.DISABLE.getStatus()));
|
||||
AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L)
|
||||
.setStatus(CommonStatusEnum.DISABLE.getStatus()));
|
||||
Map<Long, AdminUserRespDTO> userMap = MapUtil.builder(user1.getId(), user1)
|
||||
.put(user2.getId(), user2).build();
|
||||
when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap);
|
||||
// mock 方法(empty)
|
||||
when(emptyStrategy.calculateUsersByTask(same(execution), same(param)))
|
||||
.thenReturn(Sets.newSet(2L));
|
||||
// mock 移除发起人的用户
|
||||
springUtilMockedStatic.when(() -> SpringUtil.getBean(BpmProcessInstanceService.class))
|
||||
.thenReturn(processInstanceService);
|
||||
ProcessInstance processInstance = mock(ProcessInstance.class);
|
||||
when(processInstanceService.getProcessInstance(eq(processInstanceId))).thenReturn(processInstance);
|
||||
when(processInstance.getStartUserId()).thenReturn("1");
|
||||
|
||||
// 调用
|
||||
Set<Long> results = taskCandidateInvoker.calculateUsersByTask(execution);
|
||||
// 断言
|
||||
assertEquals(asSet(2L), results);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景:没有计算到候选人,但是被禁用移除,最终通过 empty 进行分配
|
||||
*/
|
||||
@Test
|
||||
public void testCalculateUsersByActivity_some() {
|
||||
try (MockedStatic<BpmnModelUtils> bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) {
|
||||
// 准备参数
|
||||
String param = "1,2";
|
||||
BpmnModel bpmnModel = mock(BpmnModel.class);
|
||||
String activityId = randomString();
|
||||
Long startUserId = 1L;
|
||||
String processDefinitionId = randomString();
|
||||
Map<String, Object> processVariables = new HashMap<>();
|
||||
// mock 方法(DelegateExecution)
|
||||
UserTask userTask = mock(UserTask.class);
|
||||
bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateStrategy(same(userTask)))
|
||||
.thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy());
|
||||
bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateParam(same(userTask)))
|
||||
.thenReturn(param);
|
||||
bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.getFlowElementById(same(bpmnModel), eq(activityId))).thenReturn(userTask);
|
||||
// mock 方法(adminUserApi)
|
||||
AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L)
|
||||
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
||||
AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L)
|
||||
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
||||
Map<Long, AdminUserRespDTO> userMap = MapUtil.builder(user1.getId(), user1)
|
||||
.put(user2.getId(), user2).build();
|
||||
when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap);
|
||||
// mock 移除发起人的用户
|
||||
bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignStartUserHandlerType(same(userTask)))
|
||||
.thenReturn(BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType());
|
||||
|
||||
// 调用
|
||||
Set<Long> results = taskCandidateInvoker.calculateUsersByActivity(bpmnModel, activityId,
|
||||
startUserId, processDefinitionId, processVariables);
|
||||
// 断言
|
||||
assertEquals(asSet(2L), results);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景:成功计算到候选人,但是移除了发起人的用户
|
||||
*/
|
||||
@Test
|
||||
public void testCalculateUsersByActivity_none() {
|
||||
try (MockedStatic<BpmnModelUtils> bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) {
|
||||
// 准备参数
|
||||
String param = "1,2";
|
||||
BpmnModel bpmnModel = mock(BpmnModel.class);
|
||||
String activityId = randomString();
|
||||
Long startUserId = 1L;
|
||||
String processDefinitionId = randomString();
|
||||
Map<String, Object> processVariables = new HashMap<>();
|
||||
// mock 方法(DelegateExecution)
|
||||
UserTask userTask = mock(UserTask.class);
|
||||
bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateStrategy(same(userTask)))
|
||||
.thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy());
|
||||
bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateParam(same(userTask)))
|
||||
.thenReturn(param);
|
||||
bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.getFlowElementById(same(bpmnModel), eq(activityId))).thenReturn(userTask);
|
||||
// mock 方法(adminUserApi)
|
||||
AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L)
|
||||
.setStatus(CommonStatusEnum.DISABLE.getStatus()));
|
||||
AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L)
|
||||
.setStatus(CommonStatusEnum.DISABLE.getStatus()));
|
||||
Map<Long, AdminUserRespDTO> userMap = MapUtil.builder(user1.getId(), user1)
|
||||
.put(user2.getId(), user2).build();
|
||||
when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap);
|
||||
// mock 方法(empty)
|
||||
when(emptyStrategy.calculateUsersByActivity(same(bpmnModel), eq(activityId),
|
||||
eq(param), same(startUserId), same(processDefinitionId), same(processVariables)))
|
||||
.thenReturn(Sets.newSet(2L));
|
||||
|
||||
// 调用
|
||||
Set<Long> results = taskCandidateInvoker.calculateUsersByActivity(bpmnModel, activityId,
|
||||
startUserId, processDefinitionId, processVariables);
|
||||
// 断言
|
||||
assertEquals(asSet(2L), results);
|
||||
}
|
||||
}
|
||||
|
||||
private static void mockFlowElementExtensionElement(FlowElement element, String name, String value) {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
ExtensionElement extensionElement = new ExtensionElement();
|
||||
extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE);
|
||||
extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX);
|
||||
extensionElement.setElementText(value);
|
||||
extensionElement.setName(name);
|
||||
// mock
|
||||
Map<String, List<ExtensionElement>> extensionElements = element.getExtensionElements();
|
||||
if (extensionElements == null) {
|
||||
extensionElements = new LinkedHashMap<>();
|
||||
}
|
||||
extensionElements.put(name, Collections.singletonList(extensionElement));
|
||||
when(element.getExtensionElements()).thenReturn(extensionElements);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveDisableUsers() {
|
||||
// 准备参数. 1L 可以找到;2L 是禁用的;3L 找不到
|
||||
Set<Long> assigneeUserIds = asSet(1L, 2L, 3L);
|
||||
// mock 方法
|
||||
AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L)
|
||||
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
||||
AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L)
|
||||
.setStatus(CommonStatusEnum.DISABLE.getStatus()));
|
||||
Map<Long, AdminUserRespDTO> userMap = MapUtil.builder(user1.getId(), user1)
|
||||
.put(user2.getId(), user2).build();
|
||||
when(adminUserApi.getUserMap(eq(assigneeUserIds))).thenReturn(userMap);
|
||||
|
||||
// 调用
|
||||
taskCandidateInvoker.removeDisableUsers(assigneeUserIds);
|
||||
// 断言
|
||||
assertEquals(asSet(1L), assigneeUserIds);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.expression;
|
||||
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class BpmTaskAssignLeaderExpressionTest extends BaseMockitoUnitTest {
|
||||
|
||||
@InjectMocks
|
||||
private BpmTaskAssignLeaderExpression expression;
|
||||
|
||||
@Mock
|
||||
private AdminUserApi adminUserApi;
|
||||
@Mock
|
||||
private DeptApi deptApi;
|
||||
|
||||
@Mock
|
||||
private BpmProcessInstanceService processInstanceService;
|
||||
|
||||
@Test
|
||||
public void testCalculateUsers_noDept() {
|
||||
// 准备参数
|
||||
DelegateExecution execution = mockDelegateExecution(1L);
|
||||
// mock 方法(startUser)
|
||||
AdminUserRespDTO startUser = randomPojo(AdminUserRespDTO.class, o -> o.setDeptId(10L));
|
||||
when(adminUserApi.getUser(eq(1L))).thenReturn(success(startUser));
|
||||
// mock 方法(getStartUserDept)没有部门
|
||||
when(deptApi.getDept(eq(10L))).thenReturn(success(null));
|
||||
|
||||
// 调用
|
||||
Set<Long> result = expression.calculateUsers(execution, 1);
|
||||
// 断言
|
||||
assertEquals(0, result.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateUsers_noParentDept() {
|
||||
// 准备参数
|
||||
DelegateExecution execution = mockDelegateExecution(1L);
|
||||
// mock 方法(startUser)
|
||||
AdminUserRespDTO startUser = randomPojo(AdminUserRespDTO.class, o -> o.setDeptId(10L));
|
||||
when(adminUserApi.getUser(eq(1L))).thenReturn(success(startUser));
|
||||
DeptRespDTO startUserDept = randomPojo(DeptRespDTO.class, o -> o.setId(10L).setParentId(100L)
|
||||
.setLeaderUserId(20L));
|
||||
// mock 方法(getDept)
|
||||
when(deptApi.getDept(eq(10L))).thenReturn(success(startUserDept));
|
||||
when(deptApi.getDept(eq(100L))).thenReturn(success(null));
|
||||
|
||||
// 调用
|
||||
Set<Long> result = expression.calculateUsers(execution, 2);
|
||||
// 断言
|
||||
assertEquals(asSet(20L), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateUsers_existParentDept() {
|
||||
// 准备参数
|
||||
DelegateExecution execution = mockDelegateExecution(1L);
|
||||
// mock 方法(startUser)
|
||||
AdminUserRespDTO startUser = randomPojo(AdminUserRespDTO.class, o -> o.setDeptId(10L));
|
||||
when(adminUserApi.getUser(eq(1L))).thenReturn(success(startUser));
|
||||
DeptRespDTO startUserDept = randomPojo(DeptRespDTO.class, o -> o.setId(10L).setParentId(100L)
|
||||
.setLeaderUserId(20L));
|
||||
when(deptApi.getDept(eq(10L))).thenReturn(success(startUserDept));
|
||||
// mock 方法(父 dept)
|
||||
DeptRespDTO parentDept = randomPojo(DeptRespDTO.class, o -> o.setId(100L).setParentId(1000L)
|
||||
.setLeaderUserId(200L));
|
||||
when(deptApi.getDept(eq(100L))).thenReturn(success(parentDept));
|
||||
|
||||
// 调用
|
||||
Set<Long> result = expression.calculateUsers(execution, 2);
|
||||
// 断言
|
||||
assertEquals(asSet(200L), result);
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
private DelegateExecution mockDelegateExecution(Long startUserId) {
|
||||
ExecutionEntityImpl execution = new ExecutionEntityImpl();
|
||||
execution.setProcessInstanceId(randomString());
|
||||
// mock 返回 startUserId
|
||||
ExecutionEntityImpl processInstance = new ExecutionEntityImpl();
|
||||
processInstance.setStartUserId(String.valueOf(startUserId));
|
||||
when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId())))
|
||||
.thenReturn(processInstance);
|
||||
return execution;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||
import org.assertj.core.util.Sets;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class BpmTaskCandidateDeptLeaderMultiStrategyTest extends BaseMockitoUnitTest {
|
||||
|
||||
@InjectMocks
|
||||
private BpmTaskCandidateDeptLeaderMultiStrategy strategy;
|
||||
|
||||
@Mock
|
||||
private DeptApi deptApi;
|
||||
|
||||
@Test
|
||||
public void testCalculateUsers() {
|
||||
// 准备参数
|
||||
String param = "10,20|2";
|
||||
// mock 方法
|
||||
when(deptApi.getDept(any())).thenAnswer((Answer< CommonResult<DeptRespDTO>>) invocationOnMock -> {
|
||||
Long deptId = invocationOnMock.getArgument(0);
|
||||
return success(randomPojo(DeptRespDTO.class, o -> o.setId(deptId).setParentId(deptId * 100).setLeaderUserId(deptId + 1)));
|
||||
});
|
||||
|
||||
// 调用
|
||||
Set<Long> userIds = strategy.calculateUsers(param);
|
||||
// 断言结果
|
||||
assertEquals(Sets.newLinkedHashSet(11L, 1001L, 21L, 2001L), userIds);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||
import org.assertj.core.util.Sets;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class BpmTaskCandidateDeptLeaderStrategyTest extends BaseMockitoUnitTest {
|
||||
|
||||
@InjectMocks
|
||||
private BpmTaskCandidateDeptLeaderStrategy strategy;
|
||||
|
||||
@Mock
|
||||
private DeptApi deptApi;
|
||||
|
||||
@Test
|
||||
public void testCalculateUsers() {
|
||||
// 准备参数
|
||||
String param = "10,20";
|
||||
// mock 方法
|
||||
when(deptApi.getDeptList(eq(SetUtils.asSet(10L, 20L)))).thenReturn(success(asList(
|
||||
randomPojo(DeptRespDTO.class, o -> o.setId(10L).setParentId(10L).setLeaderUserId(11L)),
|
||||
randomPojo(DeptRespDTO.class, o -> o.setId(20L).setParentId(20L).setLeaderUserId(21L)))));
|
||||
|
||||
// 调用
|
||||
Set<Long> userIds = strategy.calculateUsers(param);
|
||||
// 断言结果
|
||||
assertEquals(Sets.newLinkedHashSet(11L, 21L), userIds);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import org.assertj.core.util.Sets;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class BpmTaskCandidateDeptMemberStrategyTest extends BaseMockitoUnitTest {
|
||||
|
||||
@InjectMocks
|
||||
private BpmTaskCandidateDeptMemberStrategy strategy;
|
||||
|
||||
@Mock
|
||||
private DeptApi deptApi;
|
||||
@Mock
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@Test
|
||||
public void testCalculateUsers() {
|
||||
// 准备参数
|
||||
String param = "10,20";
|
||||
// mock 方法
|
||||
when(adminUserApi.getUserListByDeptIds(eq(SetUtils.asSet(10L, 20L)))).thenReturn(success(asList(
|
||||
randomPojo(AdminUserRespDTO.class, o -> o.setId(11L)),
|
||||
randomPojo(AdminUserRespDTO.class, o -> o.setId(21L)))));
|
||||
|
||||
// 调用
|
||||
Set<Long> userIds = strategy.calculateUsers(param);
|
||||
// 断言结果
|
||||
assertEquals(Sets.newLinkedHashSet(11L, 21L), userIds);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import org.assertj.core.util.Sets;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest extends BaseMockitoUnitTest {
|
||||
|
||||
@InjectMocks
|
||||
private BpmTaskCandidateStartUserDeptLeaderMultiStrategy strategy;
|
||||
|
||||
@Mock
|
||||
private BpmProcessInstanceService processInstanceService;
|
||||
|
||||
@Mock
|
||||
private AdminUserApi adminUserApi;
|
||||
@Mock
|
||||
private DeptApi deptApi;
|
||||
|
||||
@Test
|
||||
public void testCalculateUsersByTask() {
|
||||
// 准备参数
|
||||
String param = "2";
|
||||
// mock 方法(获得流程发起人)
|
||||
Long startUserId = 1L;
|
||||
ProcessInstance processInstance = mock(ProcessInstance.class);
|
||||
DelegateExecution execution = mock(DelegateExecution.class);
|
||||
when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance);
|
||||
when(processInstance.getStartUserId()).thenReturn(startUserId.toString());
|
||||
// mock 方法(获取发起人的 multi 部门负责人)
|
||||
mockGetStartUserDept(startUserId);
|
||||
|
||||
// 调用
|
||||
Set<Long> userIds = strategy.calculateUsersByTask(execution, param);
|
||||
// 断言
|
||||
assertEquals(Sets.newLinkedHashSet(11L, 1001L), userIds);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateUsersByActivity() {
|
||||
// 准备参数
|
||||
String param = "2";
|
||||
// mock 方法
|
||||
Long startUserId = 1L;
|
||||
mockGetStartUserDept(startUserId);
|
||||
|
||||
// 调用
|
||||
Set<Long> userIds = strategy.calculateUsersByActivity(null, null, param,
|
||||
startUserId, null, null);
|
||||
// 断言
|
||||
assertEquals(Sets.newLinkedHashSet(11L, 1001L), userIds);
|
||||
}
|
||||
|
||||
private void mockGetStartUserDept(Long startUserId) {
|
||||
when(adminUserApi.getUser(eq(startUserId))).thenReturn(
|
||||
success(randomPojo(AdminUserRespDTO.class, o -> o.setId(startUserId).setDeptId(10L))));
|
||||
when(deptApi.getDept(any())).thenAnswer((Answer< CommonResult<DeptRespDTO>>) invocationOnMock -> {
|
||||
Long deptId = invocationOnMock.getArgument(0);
|
||||
return success(randomPojo(DeptRespDTO.class, o -> o.setId(deptId).setParentId(deptId * 100).setLeaderUserId(deptId + 1)));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import org.assertj.core.util.Sets;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class BpmTaskCandidateStartUserDeptLeaderStrategyTest extends BaseMockitoUnitTest {
|
||||
|
||||
@InjectMocks
|
||||
private BpmTaskCandidateStartUserDeptLeaderStrategy strategy;
|
||||
|
||||
@Mock
|
||||
private BpmProcessInstanceService processInstanceService;
|
||||
|
||||
@Mock
|
||||
private AdminUserApi adminUserApi;
|
||||
@Mock
|
||||
private DeptApi deptApi;
|
||||
|
||||
@Test
|
||||
public void testCalculateUsersByTask() {
|
||||
// 准备参数
|
||||
String param = "2";
|
||||
// mock 方法(获得流程发起人)
|
||||
Long startUserId = 1L;
|
||||
ProcessInstance processInstance = mock(ProcessInstance.class);
|
||||
DelegateExecution execution = mock(DelegateExecution.class);
|
||||
when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance);
|
||||
when(processInstance.getStartUserId()).thenReturn(startUserId.toString());
|
||||
// mock 方法(获取发起人的部门负责人)
|
||||
mockGetStartUserDeptLeader(startUserId);
|
||||
|
||||
// 调用
|
||||
Set<Long> userIds = strategy.calculateUsersByTask(execution, param);
|
||||
// 断言
|
||||
assertEquals(Sets.newLinkedHashSet(1001L), userIds);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetStartUserDeptLeader() {
|
||||
// 准备参数
|
||||
String param = "2";
|
||||
// mock 方法
|
||||
Long startUserId = 1L;
|
||||
mockGetStartUserDeptLeader(startUserId);
|
||||
|
||||
// 调用
|
||||
Set<Long> userIds = strategy.calculateUsersByActivity(null, null, param,
|
||||
startUserId, null, null);
|
||||
// 断言
|
||||
assertEquals(Sets.newLinkedHashSet(1001L), userIds);
|
||||
}
|
||||
|
||||
private void mockGetStartUserDeptLeader(Long startUserId) {
|
||||
when(adminUserApi.getUser(eq(startUserId))).thenReturn(
|
||||
success(randomPojo(AdminUserRespDTO.class, o -> o.setId(startUserId).setDeptId(10L))));
|
||||
when(deptApi.getDept(any())).thenAnswer((Answer< CommonResult<DeptRespDTO>>) invocationOnMock -> {
|
||||
Long deptId = invocationOnMock.getArgument(0);
|
||||
return success(randomPojo(DeptRespDTO.class, o -> o.setId(deptId).setParentId(deptId * 100).setLeaderUserId(deptId + 1)));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;
|
||||
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
||||
import org.assertj.core.util.Sets;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class BpmTaskCandidateStartUserSelectStrategyTest extends BaseMockitoUnitTest {
|
||||
|
||||
@InjectMocks
|
||||
private BpmTaskCandidateStartUserSelectStrategy strategy;
|
||||
|
||||
@Mock
|
||||
private BpmProcessInstanceService processInstanceService;
|
||||
|
||||
@Test
|
||||
public void testCalculateUsersByTask() {
|
||||
// 准备参数
|
||||
String param = "2";
|
||||
// mock 方法(获得流程发起人)
|
||||
ProcessInstance processInstance = mock(ProcessInstance.class);
|
||||
DelegateExecution execution = mock(DelegateExecution.class);
|
||||
when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance);
|
||||
when(execution.getCurrentActivityId()).thenReturn("activity_001");
|
||||
// mock 方法(FlowableUtils)
|
||||
Map<String, Object> processVariables = new HashMap<>();
|
||||
processVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES,
|
||||
MapUtil.of("activity_001", List.of(1L, 2L)));
|
||||
when(processInstance.getProcessVariables()).thenReturn(processVariables);
|
||||
|
||||
// 调用
|
||||
Set<Long> userIds = strategy.calculateUsersByTask(execution, param);
|
||||
// 断言
|
||||
assertEquals(Sets.newLinkedHashSet(1L, 2L), userIds);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateUsersByActivity() {
|
||||
// 准备参数
|
||||
String activityId = "activity_001";
|
||||
Map<String, Object> processVariables = new HashMap<>();
|
||||
processVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES,
|
||||
MapUtil.of("activity_001", List.of(1L, 2L)));
|
||||
|
||||
// 调用
|
||||
Set<Long> userIds = strategy.calculateUsersByActivity(null, activityId, null,
|
||||
null, null, processVariables);
|
||||
// 断言
|
||||
assertEquals(Sets.newLinkedHashSet(1L, 2L), userIds);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.other;
|
||||
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignEmptyHandlerTypeEnum;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
|
||||
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
|
||||
import org.flowable.bpmn.model.BpmnModel;
|
||||
import org.flowable.bpmn.model.FlowElement;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockedStatic;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
public class BpmTaskCandidateAssignEmptyStrategyTest extends BaseMockitoUnitTest {
|
||||
|
||||
@InjectMocks
|
||||
private BpmTaskCandidateAssignEmptyStrategy strategy;
|
||||
|
||||
@Mock
|
||||
private BpmProcessDefinitionService processDefinitionService;
|
||||
|
||||
@Test
|
||||
public void testCalculateUsersByTask() {
|
||||
try (MockedStatic<FlowableUtils> flowableUtilMockedStatic = mockStatic(FlowableUtils.class);
|
||||
MockedStatic<BpmnModelUtils> bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) {
|
||||
// 准备参数
|
||||
DelegateExecution execution = mock(DelegateExecution.class);
|
||||
String param = randomString();
|
||||
// mock 方法(execution)
|
||||
String processDefinitionId = randomString();
|
||||
when(execution.getProcessDefinitionId()).thenReturn(processDefinitionId);
|
||||
FlowElement flowElement = mock(FlowElement.class);
|
||||
when(execution.getCurrentFlowElement()).thenReturn(flowElement);
|
||||
// mock 方法(parseAssignEmptyHandlerType)
|
||||
bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignEmptyHandlerType(same(flowElement)))
|
||||
.thenReturn(BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_USER.getType());
|
||||
bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignEmptyHandlerUserIds(same(flowElement)))
|
||||
.thenReturn(ListUtil.of(1L, 2L));
|
||||
|
||||
// 调用
|
||||
Set<Long> userIds = strategy.calculateUsersByTask(execution, param);
|
||||
// 断言
|
||||
assertEquals(SetUtils.asSet(1L, 2L), userIds);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateUsersByActivity() {
|
||||
try (MockedStatic<BpmnModelUtils> bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) {
|
||||
// 准备参数
|
||||
String processDefinitionId = randomString();
|
||||
String activityId = randomString();
|
||||
String param = randomString();
|
||||
// mock 方法(getFlowElementById)
|
||||
FlowElement flowElement = mock(FlowElement.class);
|
||||
BpmnModel bpmnModel = mock(BpmnModel.class);
|
||||
bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.getFlowElementById(same(bpmnModel), eq(activityId)))
|
||||
.thenReturn(flowElement);
|
||||
// mock 方法(parseAssignEmptyHandlerType)
|
||||
bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignEmptyHandlerType(same(flowElement)))
|
||||
.thenReturn(BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_ADMIN.getType());
|
||||
// mock 方法(getProcessDefinitionInfo)
|
||||
BpmProcessDefinitionInfoDO processDefinition = randomPojo(BpmProcessDefinitionInfoDO.class,
|
||||
o -> o.setManagerUserIds(ListUtil.of(1L, 2L)));
|
||||
when(processDefinitionService.getProcessDefinitionInfo(eq(processDefinitionId))).thenReturn(processDefinition);
|
||||
|
||||
// 调用
|
||||
Set<Long> userIds = strategy.calculateUsersByActivity(bpmnModel, activityId, param,
|
||||
null, processDefinitionId, null);
|
||||
// 断言
|
||||
assertEquals(SetUtils.asSet(1L, 2L), userIds);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.other;
|
||||
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.MockedStatic;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@Disabled // TODO 芋艿:临时注释
|
||||
public class BpmTaskCandidateExpressionStrategyTest extends BaseMockitoUnitTest {
|
||||
|
||||
@InjectMocks
|
||||
private BpmTaskCandidateExpressionStrategy strategy;
|
||||
|
||||
@Test
|
||||
public void testCalculateUsersByTask() {
|
||||
try (MockedStatic<FlowableUtils> flowableUtilMockedStatic = mockStatic(FlowableUtils.class)) {
|
||||
// 准备参数
|
||||
String param = "1,2";
|
||||
DelegateExecution execution = mock(DelegateExecution.class);
|
||||
// mock 方法
|
||||
flowableUtilMockedStatic.when(() -> FlowableUtils.getExpressionValue(same(execution), eq(param)))
|
||||
.thenReturn(asSet(1L, 2L));
|
||||
|
||||
// 调用
|
||||
Set<Long> results = strategy.calculateUsersByTask(execution, param);
|
||||
// 断言
|
||||
assertEquals(asSet(1L, 2L), results);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateUsersByActivity() {
|
||||
try (MockedStatic<FlowableUtils> flowableUtilMockedStatic = mockStatic(FlowableUtils.class)) {
|
||||
// 准备参数
|
||||
String param = "1,2";
|
||||
Map<String, Object> processVariables = new HashMap<>();
|
||||
// mock 方法
|
||||
flowableUtilMockedStatic.when(() -> FlowableUtils.getExpressionValue(same(processVariables), eq(param)))
|
||||
.thenReturn(asSet(1L, 2L));
|
||||
|
||||
// 调用
|
||||
Set<Long> results = strategy.calculateUsersByActivity(null, null, param,
|
||||
null, null, processVariables);
|
||||
// 断言
|
||||
assertEquals(asSet(1L, 2L), results);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;
|
||||
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
|
||||
import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@Disabled // TODO 芋艿:临时注释
|
||||
public class BpmTaskCandidateGroupStrategyTest extends BaseMockitoUnitTest {
|
||||
|
||||
@InjectMocks
|
||||
private BpmTaskCandidateGroupStrategy strategy;
|
||||
|
||||
@Mock
|
||||
private BpmUserGroupService userGroupService;
|
||||
|
||||
@Test
|
||||
public void testCalculateUsers() {
|
||||
// 准备参数
|
||||
String param = "1,2";
|
||||
// mock 方法
|
||||
BpmUserGroupDO userGroup1 = randomPojo(BpmUserGroupDO.class, o -> o.setUserIds(asSet(11L, 12L)));
|
||||
BpmUserGroupDO userGroup2 = randomPojo(BpmUserGroupDO.class, o -> o.setUserIds(asSet(21L, 22L)));
|
||||
when(userGroupService.getUserGroupList(eq(asSet(1L, 2L)))).thenReturn(Arrays.asList(userGroup1, userGroup2));
|
||||
|
||||
// 调用
|
||||
Set<Long> userIds = strategy.calculateUsersByTask(null, param);
|
||||
// 断言
|
||||
assertEquals(asSet(11L, 12L, 21L, 22L), userIds);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;
|
||||
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import cn.iocoder.yudao.module.system.api.dept.PostApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@Disabled // TODO 芋艿:临时注释
|
||||
public class BpmTaskCandidatePostStrategyTest extends BaseMockitoUnitTest {
|
||||
|
||||
@InjectMocks
|
||||
private BpmTaskCandidatePostStrategy strategy;
|
||||
|
||||
@Mock
|
||||
private PostApi postApi;
|
||||
@Mock
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@Test
|
||||
public void testCalculateUsers() {
|
||||
// 准备参数
|
||||
String param = "1,2";
|
||||
// mock 方法
|
||||
List<AdminUserRespDTO> users = convertList(asSet(11L, 22L),
|
||||
id -> new AdminUserRespDTO().setId(id));
|
||||
when(adminUserApi.getUserListByPostIds(eq(asSet(1L, 2L)))).thenReturn(success(users));
|
||||
|
||||
// 调用
|
||||
Set<Long> userIds = strategy.calculateUsersByTask(null, param);
|
||||
// 断言
|
||||
assertEquals(asSet(11L, 22L), userIds);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;
|
||||
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
|
||||
import cn.iocoder.yudao.module.system.api.permission.RoleApi;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@Disabled // TODO 芋艿:临时注释
|
||||
public class BpmTaskCandidateRoleStrategyTest extends BaseMockitoUnitTest {
|
||||
|
||||
@InjectMocks
|
||||
private BpmTaskCandidateRoleStrategy strategy;
|
||||
|
||||
@Mock
|
||||
private RoleApi roleApi;
|
||||
@Mock
|
||||
private PermissionApi permissionApi;
|
||||
|
||||
@Test
|
||||
public void testCalculateUsers() {
|
||||
// 准备参数
|
||||
String param = "1,2";
|
||||
// mock 方法
|
||||
when(permissionApi.getUserRoleIdListByRoleIds(eq(asSet(1L, 2L))))
|
||||
.thenReturn(success(asSet(11L, 22L)));
|
||||
|
||||
// 调用
|
||||
Set<Long> userIds = strategy.calculateUsersByTask(null, param);
|
||||
// 断言
|
||||
assertEquals(asSet(11L, 22L), userIds);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;
|
||||
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
|
||||
import org.assertj.core.util.Sets;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class BpmTaskCandidateStartUserStrategyTest extends BaseMockitoUnitTest {
|
||||
|
||||
@InjectMocks
|
||||
private BpmTaskCandidateStartUserStrategy strategy;
|
||||
|
||||
@Mock
|
||||
private BpmProcessInstanceService processInstanceService;
|
||||
|
||||
@Test
|
||||
public void testCalculateUsersByTask() {
|
||||
// 准备参数
|
||||
String param = "2";
|
||||
// mock 方法(获得流程发起人)
|
||||
Long startUserId = 1L;
|
||||
ProcessInstance processInstance = mock(ProcessInstance.class);
|
||||
DelegateExecution execution = mock(DelegateExecution.class);
|
||||
when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance);
|
||||
when(processInstance.getStartUserId()).thenReturn(startUserId.toString());
|
||||
|
||||
// 调用
|
||||
Set<Long> userIds = strategy.calculateUsersByTask(execution, param);
|
||||
// 断言
|
||||
assertEquals(Sets.newLinkedHashSet(startUserId), userIds);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateUsersByActivity() {
|
||||
// 准备参数
|
||||
Long startUserId = 1L;
|
||||
|
||||
// 调用
|
||||
Set<Long> userIds = strategy.calculateUsersByActivity(null, null, null,
|
||||
startUserId, null, null);
|
||||
// 断言
|
||||
assertEquals(Sets.newLinkedHashSet(startUserId), userIds);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user;
|
||||
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@Disabled // TODO 芋艿:临时注释
|
||||
public class BpmTaskCandidateUserStrategyTest extends BaseMockitoUnitTest {
|
||||
|
||||
@InjectMocks
|
||||
private BpmTaskCandidateUserStrategy strategy;
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
// 准备参数
|
||||
String param = "1,2";
|
||||
|
||||
// 调用
|
||||
Set<Long> userIds = strategy.calculateUsersByTask(null, param);
|
||||
// 断言
|
||||
assertEquals(asSet(1L, 2L), userIds);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package cn.iocoder.yudao.module.bpm.service.category;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategorySaveReqVO;
|
||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
|
||||
import cn.iocoder.yudao.module.bpm.dal.mysql.category.BpmCategoryMapper;
|
||||
import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryServiceImpl;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
|
||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
|
||||
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
|
||||
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.CATEGORY_NOT_EXISTS;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* {@link BpmCategoryServiceImpl} 的单元测试类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Import(BpmCategoryServiceImpl.class)
|
||||
public class BpmCategoryServiceImplTest extends BaseDbUnitTest {
|
||||
|
||||
@Resource
|
||||
private BpmCategoryServiceImpl categoryService;
|
||||
|
||||
@Resource
|
||||
private BpmCategoryMapper categoryMapper;
|
||||
|
||||
@Test
|
||||
public void testCreateCategory_success() {
|
||||
// 准备参数
|
||||
BpmCategorySaveReqVO createReqVO = randomPojo(BpmCategorySaveReqVO.class).setId(null)
|
||||
.setStatus(randomCommonStatus());
|
||||
|
||||
// 调用
|
||||
Long categoryId = categoryService.createCategory(createReqVO);
|
||||
// 断言
|
||||
assertNotNull(categoryId);
|
||||
// 校验记录的属性是否正确
|
||||
BpmCategoryDO category = categoryMapper.selectById(categoryId);
|
||||
assertPojoEquals(createReqVO, category, "id");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateCategory_success() {
|
||||
// mock 数据
|
||||
BpmCategoryDO dbCategory = randomPojo(BpmCategoryDO.class);
|
||||
categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
BpmCategorySaveReqVO updateReqVO = randomPojo(BpmCategorySaveReqVO.class, o -> {
|
||||
o.setId(dbCategory.getId()); // 设置更新的 ID
|
||||
o.setStatus(randomCommonStatus());
|
||||
});
|
||||
|
||||
// 调用
|
||||
categoryService.updateCategory(updateReqVO);
|
||||
// 校验是否更新正确
|
||||
BpmCategoryDO category = categoryMapper.selectById(updateReqVO.getId()); // 获取最新的
|
||||
assertPojoEquals(updateReqVO, category);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateCategory_notExists() {
|
||||
// 准备参数
|
||||
BpmCategorySaveReqVO updateReqVO = randomPojo(BpmCategorySaveReqVO.class);
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> categoryService.updateCategory(updateReqVO), CATEGORY_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteCategory_success() {
|
||||
// mock 数据
|
||||
BpmCategoryDO dbCategory = randomPojo(BpmCategoryDO.class);
|
||||
categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
Long id = dbCategory.getId();
|
||||
|
||||
// 调用
|
||||
categoryService.deleteCategory(id);
|
||||
// 校验数据不存在了
|
||||
assertNull(categoryMapper.selectById(id));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteCategory_notExists() {
|
||||
// 准备参数
|
||||
Long id = randomLongId();
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> categoryService.deleteCategory(id), CATEGORY_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCategoryPage() {
|
||||
// mock 数据
|
||||
BpmCategoryDO dbCategory = randomPojo(BpmCategoryDO.class, o -> { // 等会查询到
|
||||
o.setName("芋头");
|
||||
o.setCode("xiaodun");
|
||||
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
o.setCreateTime(buildTime(2023, 2, 2));
|
||||
});
|
||||
categoryMapper.insert(dbCategory);
|
||||
// 测试 name 不匹配
|
||||
categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName("小盾")));
|
||||
// 测试 code 不匹配
|
||||
categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCode("tudou")));
|
||||
// 测试 status 不匹配
|
||||
categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
|
||||
// 测试 createTime 不匹配
|
||||
categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCreateTime(buildTime(2024, 2, 2))));
|
||||
// 准备参数
|
||||
BpmCategoryPageReqVO reqVO = new BpmCategoryPageReqVO();
|
||||
reqVO.setName("芋");
|
||||
reqVO.setCode("xiao");
|
||||
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
|
||||
|
||||
// 调用
|
||||
PageResult<BpmCategoryDO> pageResult = categoryService.getCategoryPage(reqVO);
|
||||
// 断言
|
||||
assertEquals(1, pageResult.getTotal());
|
||||
assertEquals(1, pageResult.getList().size());
|
||||
assertPojoEquals(dbCategory, pageResult.getList().get(0));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package cn.iocoder.yudao.module.bpm.service.definition;
|
||||
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormSaveReqVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO;
|
||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
|
||||
import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmFormMapper;
|
||||
import cn.iocoder.yudao.module.bpm.service.definition.dto.BpmFormFieldRespDTO;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.FORM_NOT_EXISTS;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* {@link BpmFormServiceImpl} 的单元测试类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Import(BpmFormServiceImpl.class)
|
||||
public class BpmFormServiceTest extends BaseDbUnitTest {
|
||||
|
||||
@Resource
|
||||
private BpmFormServiceImpl formService;
|
||||
|
||||
@Resource
|
||||
private BpmFormMapper formMapper;
|
||||
|
||||
@Test
|
||||
public void testCreateForm_success() {
|
||||
// 准备参数
|
||||
BpmFormSaveReqVO reqVO = randomPojo(BpmFormSaveReqVO.class, o -> {
|
||||
o.setConf("{}");
|
||||
o.setFields(randomFields());
|
||||
});
|
||||
|
||||
// 调用
|
||||
Long formId = formService.createForm(reqVO);
|
||||
// 断言
|
||||
assertNotNull(formId);
|
||||
// 校验记录的属性是否正确
|
||||
BpmFormDO form = formMapper.selectById(formId);
|
||||
assertPojoEquals(reqVO, form);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateForm_success() {
|
||||
// mock 数据
|
||||
BpmFormDO dbForm = randomPojo(BpmFormDO.class, o -> {
|
||||
o.setConf("{}");
|
||||
o.setFields(randomFields());
|
||||
});
|
||||
formMapper.insert(dbForm);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
BpmFormSaveReqVO reqVO = randomPojo(BpmFormSaveReqVO.class, o -> {
|
||||
o.setId(dbForm.getId()); // 设置更新的 ID
|
||||
o.setConf("{'yudao': 'yuanma'}");
|
||||
o.setFields(randomFields());
|
||||
});
|
||||
|
||||
// 调用
|
||||
formService.updateForm(reqVO);
|
||||
// 校验是否更新正确
|
||||
BpmFormDO form = formMapper.selectById(reqVO.getId()); // 获取最新的
|
||||
assertPojoEquals(reqVO, form);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateForm_notExists() {
|
||||
// 准备参数
|
||||
BpmFormSaveReqVO reqVO = randomPojo(BpmFormSaveReqVO.class, o -> {
|
||||
o.setConf("{'yudao': 'yuanma'}");
|
||||
o.setFields(randomFields());
|
||||
});
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> formService.updateForm(reqVO), FORM_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteForm_success() {
|
||||
// mock 数据
|
||||
BpmFormDO dbForm = randomPojo(BpmFormDO.class);
|
||||
formMapper.insert(dbForm);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
Long id = dbForm.getId();
|
||||
|
||||
// 调用
|
||||
formService.deleteForm(id);
|
||||
// 校验数据不存在了
|
||||
assertNull(formMapper.selectById(id));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteForm_notExists() {
|
||||
// 准备参数
|
||||
Long id = randomLongId();
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> formService.deleteForm(id), FORM_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFormPage() {
|
||||
// mock 数据
|
||||
BpmFormDO dbForm = randomPojo(BpmFormDO.class, o -> { // 等会查询到
|
||||
o.setName("芋道源码");
|
||||
});
|
||||
formMapper.insert(dbForm);
|
||||
// 测试 name 不匹配
|
||||
formMapper.insert(cloneIgnoreId(dbForm, o -> o.setName("源码")));
|
||||
// 准备参数
|
||||
BpmFormPageReqVO reqVO = new BpmFormPageReqVO();
|
||||
reqVO.setName("芋道");
|
||||
|
||||
// 调用
|
||||
PageResult<BpmFormDO> pageResult = formService.getFormPage(reqVO);
|
||||
// 断言
|
||||
assertEquals(1, pageResult.getTotal());
|
||||
assertEquals(1, pageResult.getList().size());
|
||||
assertPojoEquals(dbForm, pageResult.getList().get(0));
|
||||
}
|
||||
|
||||
private List<String> randomFields() {
|
||||
int size = RandomUtil.randomInt(1, 3);
|
||||
return Stream.iterate(0, i -> i).limit(size)
|
||||
.map(i -> JsonUtils.toJsonString(randomPojo(BpmFormFieldRespDTO.class)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package cn.iocoder.yudao.module.bpm.service.definition;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
|
||||
import cn.iocoder.yudao.framework.test.core.util.AssertUtils;
|
||||
import cn.iocoder.yudao.framework.test.core.util.RandomUtils;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group.BpmUserGroupSaveReqVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group.BpmUserGroupPageReqVO;
|
||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
|
||||
import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmUserGroupMapper;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
|
||||
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
|
||||
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.USER_GROUP_NOT_EXISTS;
|
||||
|
||||
/**
|
||||
* {@link BpmUserGroupServiceImpl} 的单元测试类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Import(BpmUserGroupServiceImpl.class)
|
||||
public class BpmUserGroupServiceTest extends BaseDbUnitTest {
|
||||
|
||||
@Resource
|
||||
private BpmUserGroupServiceImpl userGroupService;
|
||||
|
||||
@Resource
|
||||
private BpmUserGroupMapper userGroupMapper;
|
||||
|
||||
@Test
|
||||
public void testCreateUserGroup_success() {
|
||||
// 准备参数
|
||||
BpmUserGroupSaveReqVO reqVO = RandomUtils.randomPojo(BpmUserGroupSaveReqVO.class);
|
||||
|
||||
// 调用
|
||||
Long userGroupId = userGroupService.createUserGroup(reqVO);
|
||||
// 断言
|
||||
Assertions.assertNotNull(userGroupId);
|
||||
// 校验记录的属性是否正确
|
||||
BpmUserGroupDO userGroup = userGroupMapper.selectById(userGroupId);
|
||||
AssertUtils.assertPojoEquals(reqVO, userGroup);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateUserGroup_success() {
|
||||
// mock 数据
|
||||
BpmUserGroupDO dbUserGroup = RandomUtils.randomPojo(BpmUserGroupDO.class);
|
||||
userGroupMapper.insert(dbUserGroup);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
BpmUserGroupSaveReqVO reqVO = RandomUtils.randomPojo(BpmUserGroupSaveReqVO.class, o -> {
|
||||
o.setId(dbUserGroup.getId()); // 设置更新的 ID
|
||||
});
|
||||
|
||||
// 调用
|
||||
userGroupService.updateUserGroup(reqVO);
|
||||
// 校验是否更新正确
|
||||
BpmUserGroupDO userGroup = userGroupMapper.selectById(reqVO.getId()); // 获取最新的
|
||||
AssertUtils.assertPojoEquals(reqVO, userGroup);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateUserGroup_notExists() {
|
||||
// 准备参数
|
||||
BpmUserGroupSaveReqVO reqVO = RandomUtils.randomPojo(BpmUserGroupSaveReqVO.class);
|
||||
|
||||
// 调用, 并断言异常
|
||||
AssertUtils.assertServiceException(() -> userGroupService.updateUserGroup(reqVO), USER_GROUP_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteUserGroup_success() {
|
||||
// mock 数据
|
||||
BpmUserGroupDO dbUserGroup = RandomUtils.randomPojo(BpmUserGroupDO.class);
|
||||
userGroupMapper.insert(dbUserGroup);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
Long id = dbUserGroup.getId();
|
||||
|
||||
// 调用
|
||||
userGroupService.deleteUserGroup(id);
|
||||
// 校验数据不存在了
|
||||
Assertions.assertNull(userGroupMapper.selectById(id));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteUserGroup_notExists() {
|
||||
// 准备参数
|
||||
Long id = RandomUtils.randomLongId();
|
||||
|
||||
// 调用, 并断言异常
|
||||
AssertUtils.assertServiceException(() -> userGroupService.deleteUserGroup(id), USER_GROUP_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUserGroupPage() {
|
||||
// mock 数据
|
||||
BpmUserGroupDO dbUserGroup = RandomUtils.randomPojo(BpmUserGroupDO.class, o -> { // 等会查询到
|
||||
o.setName("芋道源码");
|
||||
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
o.setCreateTime(buildTime(2021, 11, 11));
|
||||
});
|
||||
userGroupMapper.insert(dbUserGroup);
|
||||
// 测试 name 不匹配
|
||||
userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setName("芋道")));
|
||||
// 测试 status 不匹配
|
||||
userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
|
||||
// 测试 createTime 不匹配
|
||||
userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setCreateTime(buildTime(2021, 12, 12))));
|
||||
// 准备参数
|
||||
BpmUserGroupPageReqVO reqVO = new BpmUserGroupPageReqVO();
|
||||
reqVO.setName("源码");
|
||||
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
reqVO.setCreateTime((new LocalDateTime[]{buildTime(2021, 11, 10),buildTime(2021, 11, 12)}));
|
||||
|
||||
// 调用
|
||||
PageResult<BpmUserGroupDO> pageResult = userGroupService.getUserGroupPage(reqVO);
|
||||
// 断言
|
||||
Assertions.assertEquals(1, pageResult.getTotal());
|
||||
Assertions.assertEquals(1, pageResult.getList().size());
|
||||
AssertUtils.assertPojoEquals(dbUserGroup, pageResult.getList().get(0));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
spring:
|
||||
main:
|
||||
lazy-initialization: true # 开启懒加载,加快速度
|
||||
banner-mode: off # 单元测试,禁用 Banner
|
||||
|
||||
--- #################### 数据库相关配置 ####################
|
||||
|
||||
spring:
|
||||
# 数据源配置项
|
||||
datasource:
|
||||
name: ruoyi-vue-pro
|
||||
url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写
|
||||
driver-class-name: org.h2.Driver
|
||||
username: sa
|
||||
password:
|
||||
druid:
|
||||
async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度
|
||||
initial-size: 1 # 单元测试,配置为 1,提升启动速度
|
||||
sql:
|
||||
init:
|
||||
schema-locations: classpath:/sql/create_tables.sql
|
||||
|
||||
mybatis-plus:
|
||||
lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试
|
||||
type-aliases-package: ${yudao.info.base-package}.dal.dataobject
|
||||
global-config:
|
||||
db-config:
|
||||
id-type: AUTO # H2 主键递增
|
||||
|
||||
--- #################### 定时任务相关配置 ####################
|
||||
|
||||
--- #################### 配置中心相关配置 ####################
|
||||
|
||||
--- #################### 服务保障相关配置 ####################
|
||||
|
||||
# Lock4j 配置项(单元测试,禁用 Lock4j)
|
||||
|
||||
--- #################### 监控相关配置 ####################
|
||||
|
||||
--- #################### 芋道相关配置 ####################
|
||||
|
||||
# 芋道配置项,设置当前项目所有自定义的配置
|
||||
yudao:
|
||||
info:
|
||||
base-package: cn.iocoder.yudao.module.bpm
|
||||
@@ -0,0 +1,4 @@
|
||||
<configuration>
|
||||
<!-- 引用 Spring Boot 的 logback 基础配置 -->
|
||||
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
|
||||
</configuration>
|
||||
@@ -0,0 +1,3 @@
|
||||
DELETE FROM "bpm_form";
|
||||
DELETE FROM "bpm_user_group";
|
||||
DELETE FROM "bpm_category";
|
||||
@@ -0,0 +1,43 @@
|
||||
CREATE TABLE IF NOT EXISTS "bpm_user_group" (
|
||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"name" varchar(63) NOT NULL,
|
||||
"description" varchar(255) NOT NULL,
|
||||
"status" tinyint NOT NULL,
|
||||
"user_ids" varchar(255) NOT NULL,
|
||||
"creator" varchar(64) DEFAULT '',
|
||||
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updater" varchar(64) DEFAULT '',
|
||||
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deleted" bit NOT NULL DEFAULT FALSE,
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT '用户组';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "bpm_category" (
|
||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"name" varchar(63) NOT NULL,
|
||||
"code" varchar(63) NOT NULL,
|
||||
"description" varchar(255) NOT NULL,
|
||||
"status" tinyint NOT NULL,
|
||||
"sort" int NOT NULL,
|
||||
"creator" varchar(64) DEFAULT '',
|
||||
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updater" varchar(64) DEFAULT '',
|
||||
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deleted" bit NOT NULL DEFAULT FALSE,
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT '分类';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "bpm_form" (
|
||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"name" varchar(63) NOT NULL,
|
||||
"status" tinyint NOT NULL,
|
||||
"fields" varchar(255) NOT NULL,
|
||||
"conf" varchar(255) NOT NULL,
|
||||
"remark" varchar(255),
|
||||
"creator" varchar(64) DEFAULT '',
|
||||
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updater" varchar(64) DEFAULT '',
|
||||
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deleted" bit NOT NULL DEFAULT FALSE,
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT '动态表单';
|
||||
Reference in New Issue
Block a user