Compare commits
33 Commits
c3ca027f72
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| e1d50571d1 | |||
| 6ef29b05c5 | |||
| 96dfcea7b8 | |||
| b48903c8af | |||
| dcf91a7e1e | |||
| 77567d997d | |||
| 1cc8bb5618 | |||
| d8f58ea2b5 | |||
| bf0e7e47be | |||
| b56555923a | |||
| 53c4be6e3e | |||
| 935793d5b6 | |||
| 2054f5802b | |||
| 28ef67d630 | |||
| 2c75cad680 | |||
| dddc9d7171 | |||
| 465cc8cd3f | |||
| 82af04a4c6 | |||
| f4e8896763 | |||
| 6d869ef915 | |||
| bca3933790 | |||
| 743e2db17d | |||
| 45ce5a2615 | |||
| 57bc3fb2a8 | |||
| b82c8fe4ea | |||
| 86f702db3e | |||
| b69da9a4e4 | |||
| 040b980a8b | |||
| eff6d6be02 | |||
| 53ab0d731c | |||
| 7885f8e87a | |||
| 07ee530b79 | |||
| 071beee794 |
2
.idea/encodings.xml
generated
2
.idea/encodings.xml
generated
@@ -5,6 +5,8 @@
|
|||||||
<file url="file://$PROJECT_DIR$/adapter/flowable-spring-boot-jpa-starter/src/main/resources" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/adapter/flowable-spring-boot-jpa-starter/src/main/resources" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/flowable-core/src/main/java" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/flowable-core/src/main/java" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/flowable-core/src/main/resources" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/flowable-core/src/main/resources" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/flowable-example/src/main/java" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/flowable-example/src/main/resources" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
7
.idea/jpa.xml
generated
Normal file
7
.idea/jpa.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="JpaBuddyIdeaProjectConfig">
|
||||||
|
<option name="defaultUnitInitialized" value="true" />
|
||||||
|
<option name="renamerInitialized" value="true" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
3
.idea/misc.xml
generated
3
.idea/misc.xml
generated
@@ -11,4 +11,7 @@
|
|||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="temurin-1.8" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="temurin-1.8" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
|
<component name="ProjectType">
|
||||||
|
<option name="id" value="jpab" />
|
||||||
|
</component>
|
||||||
</project>
|
</project>
|
||||||
@@ -23,11 +23,21 @@
|
|||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-logging</artifactId>
|
<artifactId>spring-boot-starter-logging</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.lanyuanxiaoyao</groupId>
|
||||||
|
<artifactId>flowable-example</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
@@ -39,22 +49,4 @@
|
|||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<phase>package</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>repackage</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package com.lanyuanxiaoyao.flowable.jpa;
|
||||||
|
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.manager.FlowableManager;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.repository.FlowableRepository;
|
||||||
|
import com.lanyuanxiaoyao.flowable.jpa.repository.FlowableHistoryRepository;
|
||||||
|
import com.lanyuanxiaoyao.flowable.jpa.repository.FlowableInstanceRepository;
|
||||||
|
import com.lanyuanxiaoyao.flowable.jpa.repository.FlowableNodeRepository;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动配置
|
||||||
|
*
|
||||||
|
* @author lanyuanxiaoyao
|
||||||
|
* @version 20250103
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
|
||||||
|
@Configuration
|
||||||
|
@EnableJpaRepositories("com.lanyuanxiaoyao.flowable.jpa.repository")
|
||||||
|
@EnableConfigurationProperties(SpringFlowableConfiguration.class)
|
||||||
|
public class SpringFlowableAutoConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean(FlowableRepository.class)
|
||||||
|
public SpringFlowableRepository flowableRepository(
|
||||||
|
FlowableNodeRepository flowableNodeRepository,
|
||||||
|
FlowableInstanceRepository flowableInstanceRepository,
|
||||||
|
FlowableHistoryRepository flowableHistoryRepository
|
||||||
|
) {
|
||||||
|
return new SpringFlowableRepository(flowableNodeRepository, flowableInstanceRepository, flowableHistoryRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnBean(FlowableRepository.class)
|
||||||
|
@ConditionalOnMissingBean(FlowableManager.class)
|
||||||
|
public SpringFlowableManager flowableManager(
|
||||||
|
SpringFlowableConfiguration configuration,
|
||||||
|
SpringFlowableRepository repository,
|
||||||
|
ApplicationContext applicationContext
|
||||||
|
) {
|
||||||
|
SpringFlowableManager manager = new SpringFlowableManager(configuration, repository, applicationContext);
|
||||||
|
|
||||||
|
Map<String, SpringFlowableManagerInitializer> initializerMap = applicationContext.getBeansOfType(SpringFlowableManagerInitializer.class);
|
||||||
|
for (SpringFlowableManagerInitializer initializer : initializerMap.values()) {
|
||||||
|
manager = initializer.initial(manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
return manager;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.lanyuanxiaoyao.flowable.jpa;
|
||||||
|
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.manager.FlowableConfiguration;
|
||||||
|
import lombok.ToString;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置类
|
||||||
|
*
|
||||||
|
* @author lanyuanxiaoyao
|
||||||
|
* @version 20250103
|
||||||
|
*/
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
@ConfigurationProperties("flowable")
|
||||||
|
public class SpringFlowableConfiguration extends FlowableConfiguration {
|
||||||
|
}
|
||||||
@@ -1,42 +1,90 @@
|
|||||||
package com.lanyuanxiaoyao.flowable.jpa;
|
package com.lanyuanxiaoyao.flowable.jpa;
|
||||||
|
|
||||||
import com.lanyuanxiaoyao.flowable.core.manager.FlowableManager;
|
import com.lanyuanxiaoyao.flowable.core.manager.FlowableManager;
|
||||||
import com.lanyuanxiaoyao.flowable.core.repository.FlowableRepository;
|
import com.lanyuanxiaoyao.flowable.jpa.entity.FlowableHistory;
|
||||||
|
import com.lanyuanxiaoyao.flowable.jpa.entity.FlowableInstance;
|
||||||
|
import com.lanyuanxiaoyao.flowable.jpa.entity.FlowableNode;
|
||||||
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
|
import org.springframework.data.jpa.domain.Specification;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author lanyuanxiaoyao
|
* @author lanyuanxiaoyao
|
||||||
* @version 20241231
|
* @version 20241231
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
|
||||||
public class SpringFlowableManager extends FlowableManager {
|
public class SpringFlowableManager extends FlowableManager {
|
||||||
|
private final SpringFlowableRepository repository;
|
||||||
private final ApplicationContext applicationContext;
|
private final ApplicationContext applicationContext;
|
||||||
|
|
||||||
public SpringFlowableManager(FlowableRepository flowableRepository, ApplicationContext applicationContext) {
|
public SpringFlowableManager(SpringFlowableConfiguration configuration, SpringFlowableRepository repository, ApplicationContext applicationContext) {
|
||||||
super(flowableRepository, () -> UUID.randomUUID().toString());
|
super(configuration, repository);
|
||||||
|
log.info("Configuration: {}", configuration);
|
||||||
|
this.repository = repository;
|
||||||
this.applicationContext = applicationContext;
|
this.applicationContext = applicationContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<com.lanyuanxiaoyao.flowable.core.model.FlowableNode> listNodes(Specification<FlowableNode> specification) {
|
||||||
|
return repository.listNodes(specification);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<com.lanyuanxiaoyao.flowable.core.model.FlowableNode> listNodes(Specification<FlowableNode> specification, Sort sort) {
|
||||||
|
return repository.listNodes(specification, sort);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page<com.lanyuanxiaoyao.flowable.core.model.FlowableNode> listNodes(Specification<FlowableNode> specification, Pageable pageable) {
|
||||||
|
return repository.listNodes(specification, pageable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<com.lanyuanxiaoyao.flowable.core.model.FlowableInstance> listInstances(Specification<FlowableInstance> specification) {
|
||||||
|
return repository.listInstances(specification);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<com.lanyuanxiaoyao.flowable.core.model.FlowableInstance> listInstances(Specification<FlowableInstance> specification, Sort sort) {
|
||||||
|
return repository.listInstances(specification, sort);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page<com.lanyuanxiaoyao.flowable.core.model.FlowableInstance> listInstances(Specification<FlowableInstance> specification, Pageable pageable) {
|
||||||
|
return repository.listInstances(specification, pageable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<com.lanyuanxiaoyao.flowable.core.model.FlowableHistory> listHistories(String instanceId, Specification<FlowableHistory> specification) {
|
||||||
|
return repository.listHistories(instanceId, specification);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<com.lanyuanxiaoyao.flowable.core.model.FlowableHistory> listHistories(String instanceId, Specification<FlowableHistory> specification, Sort sort) {
|
||||||
|
return repository.listHistories(instanceId, specification, sort);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page<com.lanyuanxiaoyao.flowable.core.model.FlowableHistory> listHistories(String instanceId, Specification<FlowableHistory> specification, Pageable pageable) {
|
||||||
|
return repository.listHistories(instanceId, specification, pageable);
|
||||||
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@Override
|
@Override
|
||||||
protected <T> T createBean(String classpath) {
|
protected <T> T createBean(String classpath) {
|
||||||
Class<?> clazz = Class.forName(classpath);
|
Class<?> clazz = Class.forName(classpath);
|
||||||
T targetObject = null;
|
T targetObject;
|
||||||
try {
|
try {
|
||||||
targetObject = (T) applicationContext.getBean(clazz);
|
targetObject = (T) applicationContext.getBean(clazz);
|
||||||
} catch (Exception springException) {
|
} catch (Exception springException) {
|
||||||
log.warn("{} not found in spring context", springException);
|
|
||||||
try {
|
try {
|
||||||
targetObject = (T) clazz.newInstance();
|
targetObject = (T) clazz.newInstance();
|
||||||
} catch (Exception javaException) {
|
} catch (Exception javaException) {
|
||||||
throw new IllegalArgumentException(javaException);
|
throw new IllegalArgumentException(javaException.initCause(springException));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return targetObject;
|
return targetObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String createId() {
|
||||||
|
return UUID.randomUUID().toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.lanyuanxiaoyao.flowable.jpa;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lanyuanxiaoyao
|
||||||
|
* @version 20250106
|
||||||
|
*/
|
||||||
|
public interface SpringFlowableManagerInitializer {
|
||||||
|
SpringFlowableManager initial(SpringFlowableManager manager);
|
||||||
|
}
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
package com.lanyuanxiaoyao.flowable.jpa;
|
||||||
|
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.repository.FlowableRepository;
|
||||||
|
import com.lanyuanxiaoyao.flowable.jpa.entity.FlowableHistory;
|
||||||
|
import com.lanyuanxiaoyao.flowable.jpa.entity.FlowableInstance;
|
||||||
|
import com.lanyuanxiaoyao.flowable.jpa.entity.FlowableNode;
|
||||||
|
import com.lanyuanxiaoyao.flowable.jpa.repository.FlowableHistoryRepository;
|
||||||
|
import com.lanyuanxiaoyao.flowable.jpa.repository.FlowableInstanceRepository;
|
||||||
|
import com.lanyuanxiaoyao.flowable.jpa.repository.FlowableNodeRepository;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
import javax.transaction.Transactional;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.PageImpl;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
|
import org.springframework.data.jpa.domain.Specification;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lanyuanxiaoyao
|
||||||
|
* @version 20241231
|
||||||
|
*/
|
||||||
|
public class SpringFlowableRepository implements FlowableRepository {
|
||||||
|
private final FlowableNodeRepository flowableNodeRepository;
|
||||||
|
private final FlowableInstanceRepository flowableInstanceRepository;
|
||||||
|
private final FlowableHistoryRepository flowableHistoryRepository;
|
||||||
|
|
||||||
|
public SpringFlowableRepository(FlowableNodeRepository flowableNodeRepository, FlowableInstanceRepository flowableInstanceRepository, FlowableHistoryRepository flowableHistoryRepository) {
|
||||||
|
this.flowableNodeRepository = flowableNodeRepository;
|
||||||
|
this.flowableInstanceRepository = flowableInstanceRepository;
|
||||||
|
this.flowableHistoryRepository = flowableHistoryRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean existsNode(String nodeId) {
|
||||||
|
return flowableNodeRepository.existsById(nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(rollbackOn = Exception.class)
|
||||||
|
@Override
|
||||||
|
public void saveNode(com.lanyuanxiaoyao.flowable.core.model.FlowableNode node) {
|
||||||
|
flowableNodeRepository.save(new FlowableNode(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(rollbackOn = Exception.class)
|
||||||
|
@Override
|
||||||
|
public void saveNode(List<com.lanyuanxiaoyao.flowable.core.model.FlowableNode> nodes) {
|
||||||
|
flowableNodeRepository.saveAll(nodes.stream().map(FlowableNode::new).collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public com.lanyuanxiaoyao.flowable.core.model.FlowableNode getNode(String nodeId) {
|
||||||
|
return flowableNodeRepository.findById(nodeId)
|
||||||
|
.map(FlowableNode::toFlowableNode)
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<com.lanyuanxiaoyao.flowable.core.model.FlowableNode> toNodes(Iterable<FlowableNode> nodes) {
|
||||||
|
return StreamSupport.stream(nodes.spliterator(), false)
|
||||||
|
.map(FlowableNode::toFlowableNode)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<com.lanyuanxiaoyao.flowable.core.model.FlowableNode> listNodes() {
|
||||||
|
return toNodes(flowableNodeRepository.findAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<com.lanyuanxiaoyao.flowable.core.model.FlowableNode> listNodes(Specification<FlowableNode> specification) {
|
||||||
|
return toNodes(flowableNodeRepository.findAll(specification));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<com.lanyuanxiaoyao.flowable.core.model.FlowableNode> listNodes(Specification<FlowableNode> specification, Sort sort) {
|
||||||
|
return toNodes(flowableNodeRepository.findAll(specification, sort));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page<com.lanyuanxiaoyao.flowable.core.model.FlowableNode> listNodes(Specification<FlowableNode> specification, Pageable pageable) {
|
||||||
|
Page<FlowableNode> page = flowableNodeRepository.findAll(specification, pageable);
|
||||||
|
return new PageImpl<>(toNodes(page.getContent()), pageable, page.getTotalElements());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean existsInstance(String instanceId) {
|
||||||
|
return flowableInstanceRepository.existsById(instanceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(rollbackOn = Exception.class)
|
||||||
|
@Override
|
||||||
|
public void saveInstance(com.lanyuanxiaoyao.flowable.core.model.FlowableInstance instance) {
|
||||||
|
flowableInstanceRepository.save(new FlowableInstance(instance));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public com.lanyuanxiaoyao.flowable.core.model.FlowableInstance getInstance(String instantId) {
|
||||||
|
return flowableInstanceRepository.findById(instantId)
|
||||||
|
.map(FlowableInstance::toFlowableInstance)
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<com.lanyuanxiaoyao.flowable.core.model.FlowableInstance> toInstances(Iterable<FlowableInstance> instances) {
|
||||||
|
return StreamSupport.stream(instances.spliterator(), false)
|
||||||
|
.map(FlowableInstance::toFlowableInstance)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<com.lanyuanxiaoyao.flowable.core.model.FlowableInstance> listInstances() {
|
||||||
|
return toInstances(flowableInstanceRepository.findAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<com.lanyuanxiaoyao.flowable.core.model.FlowableInstance> listInstances(Specification<FlowableInstance> specification) {
|
||||||
|
return toInstances(flowableInstanceRepository.findAll(specification));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<com.lanyuanxiaoyao.flowable.core.model.FlowableInstance> listInstances(Specification<FlowableInstance> specification, Sort sort) {
|
||||||
|
return toInstances(flowableInstanceRepository.findAll(specification, sort));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page<com.lanyuanxiaoyao.flowable.core.model.FlowableInstance> listInstances(Specification<FlowableInstance> specification, Pageable pageable) {
|
||||||
|
Page<FlowableInstance> page = flowableInstanceRepository.findAll(specification, pageable);
|
||||||
|
return new PageImpl<>(toInstances(page.getContent()), pageable, page.getTotalElements());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean existsHistory(String historyId) {
|
||||||
|
return flowableHistoryRepository.existsById(historyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(rollbackOn = Exception.class)
|
||||||
|
@Override
|
||||||
|
public void saveHistory(com.lanyuanxiaoyao.flowable.core.model.FlowableHistory history) {
|
||||||
|
flowableHistoryRepository.save(new FlowableHistory(history));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public com.lanyuanxiaoyao.flowable.core.model.FlowableHistory getHistory(String historyId) {
|
||||||
|
return flowableHistoryRepository.findById(historyId)
|
||||||
|
.map(FlowableHistory::toFlowableHistory)
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<com.lanyuanxiaoyao.flowable.core.model.FlowableHistory> toHistories(Iterable<FlowableHistory> histories) {
|
||||||
|
return StreamSupport.stream(histories.spliterator(), false)
|
||||||
|
.map(FlowableHistory::toFlowableHistory)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<com.lanyuanxiaoyao.flowable.core.model.FlowableHistory> listHistories(String instanceId) {
|
||||||
|
return toHistories(flowableHistoryRepository.findAllByInstanceId(instanceId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<com.lanyuanxiaoyao.flowable.core.model.FlowableHistory> listHistories(String instanceId, Specification<FlowableHistory> specification) {
|
||||||
|
return toHistories(
|
||||||
|
flowableHistoryRepository.findAll(
|
||||||
|
(root, query, builder) -> builder.and(
|
||||||
|
builder.equal(root.get("instanceId"), instanceId),
|
||||||
|
specification.toPredicate(root, query, builder)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<com.lanyuanxiaoyao.flowable.core.model.FlowableHistory> listHistories(String instanceId, Specification<FlowableHistory> specification, Sort sort) {
|
||||||
|
return toHistories(
|
||||||
|
flowableHistoryRepository.findAll(
|
||||||
|
(root, query, builder) -> builder.and(
|
||||||
|
builder.equal(root.get("instanceId"), instanceId),
|
||||||
|
specification.toPredicate(root, query, builder)
|
||||||
|
),
|
||||||
|
sort
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Page<com.lanyuanxiaoyao.flowable.core.model.FlowableHistory> listHistories(String instanceId, Specification<FlowableHistory> specification, Pageable pageable) {
|
||||||
|
Page<FlowableHistory> page = flowableHistoryRepository.findAll(
|
||||||
|
(root, query, builder) -> builder.and(
|
||||||
|
builder.equal(root.get("instanceId"), instanceId),
|
||||||
|
specification.toPredicate(root, query, builder)
|
||||||
|
),
|
||||||
|
pageable
|
||||||
|
);
|
||||||
|
return new PageImpl<>(toHistories(page.getContent()), pageable, page.getTotalElements());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(rollbackOn = Exception.class)
|
||||||
|
@Override
|
||||||
|
public void saveInstanceAndHistory(com.lanyuanxiaoyao.flowable.core.model.FlowableInstance instance, com.lanyuanxiaoyao.flowable.core.model.FlowableHistory history) {
|
||||||
|
saveInstance(instance);
|
||||||
|
saveHistory(history);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
package com.lanyuanxiaoyao.flowable.jpa;
|
|
||||||
|
|
||||||
import com.lanyuanxiaoyao.flowable.core.model.FlowableHistory;
|
|
||||||
import com.lanyuanxiaoyao.flowable.core.model.FlowableInstance;
|
|
||||||
import com.lanyuanxiaoyao.flowable.core.model.FlowableNode;
|
|
||||||
import com.lanyuanxiaoyao.flowable.core.repository.FlowableRepository;
|
|
||||||
import com.lanyuanxiaoyao.flowable.jpa.entity.JpaFlowableHistory;
|
|
||||||
import com.lanyuanxiaoyao.flowable.jpa.entity.JpaFlowableInstance;
|
|
||||||
import com.lanyuanxiaoyao.flowable.jpa.entity.JpaFlowableNode;
|
|
||||||
import com.lanyuanxiaoyao.flowable.jpa.repository.JpaFlowableHistoryRepository;
|
|
||||||
import com.lanyuanxiaoyao.flowable.jpa.repository.JpaFlowableInstanceRepository;
|
|
||||||
import com.lanyuanxiaoyao.flowable.jpa.repository.JpaFlowableNodeRepository;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import javax.transaction.Transactional;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lanyuanxiaoyao
|
|
||||||
* @version 20241231
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
public class SpringJpaFlowableRepository implements FlowableRepository {
|
|
||||||
private final JpaFlowableNodeRepository jpaFlowableNodeRepository;
|
|
||||||
private final JpaFlowableInstanceRepository jpaFlowableInstanceRepository;
|
|
||||||
private final JpaFlowableHistoryRepository jpaFlowableHistoryRepository;
|
|
||||||
|
|
||||||
public SpringJpaFlowableRepository(JpaFlowableNodeRepository jpaFlowableNodeRepository, JpaFlowableInstanceRepository jpaFlowableInstanceRepository, JpaFlowableHistoryRepository jpaFlowableHistoryRepository) {
|
|
||||||
this.jpaFlowableNodeRepository = jpaFlowableNodeRepository;
|
|
||||||
this.jpaFlowableInstanceRepository = jpaFlowableInstanceRepository;
|
|
||||||
this.jpaFlowableHistoryRepository = jpaFlowableHistoryRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional(rollbackOn = Exception.class)
|
|
||||||
@Override
|
|
||||||
public void saveNode(FlowableNode node) {
|
|
||||||
jpaFlowableNodeRepository.save(new JpaFlowableNode(node));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional(rollbackOn = Exception.class)
|
|
||||||
@Override
|
|
||||||
public void saveNode(List<FlowableNode> nodes) {
|
|
||||||
jpaFlowableNodeRepository.saveAll(nodes.stream().map(JpaFlowableNode::new).collect(Collectors.toList()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FlowableNode getNode(String nodeId) {
|
|
||||||
return jpaFlowableNodeRepository.findById(nodeId)
|
|
||||||
.map(JpaFlowableNode::toFlowableNode)
|
|
||||||
.orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<FlowableNode> listNodes() {
|
|
||||||
return jpaFlowableNodeRepository.findAll()
|
|
||||||
.stream()
|
|
||||||
.map(JpaFlowableNode::toFlowableNode)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional(rollbackOn = Exception.class)
|
|
||||||
@Override
|
|
||||||
public void saveInstance(FlowableInstance instance) {
|
|
||||||
jpaFlowableInstanceRepository.save(new JpaFlowableInstance(instance));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FlowableInstance getInstance(String instantId) {
|
|
||||||
return jpaFlowableInstanceRepository.findById(instantId)
|
|
||||||
.map(JpaFlowableInstance::toFlowableInstance)
|
|
||||||
.orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<FlowableInstance> listInstances() {
|
|
||||||
return jpaFlowableInstanceRepository.findAll()
|
|
||||||
.stream()
|
|
||||||
.map(JpaFlowableInstance::toFlowableInstance)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional(rollbackOn = Exception.class)
|
|
||||||
@Override
|
|
||||||
public void saveHistory(FlowableHistory history) {
|
|
||||||
jpaFlowableHistoryRepository.save(new JpaFlowableHistory(history));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FlowableHistory getHistory(String historyId) {
|
|
||||||
return jpaFlowableHistoryRepository.findById(historyId)
|
|
||||||
.map(JpaFlowableHistory::toFlowableHistory)
|
|
||||||
.orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<FlowableHistory> listHistories(String instanceId) {
|
|
||||||
return jpaFlowableHistoryRepository.findAllByInstanceId(instanceId)
|
|
||||||
.stream()
|
|
||||||
.map(JpaFlowableHistory::toFlowableHistory)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional(rollbackOn = Exception.class)
|
|
||||||
@Override
|
|
||||||
public void saveInstanceAndHistory(FlowableInstance instance, FlowableHistory history) {
|
|
||||||
saveInstance(instance);
|
|
||||||
saveHistory(history);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.lanyuanxiaoyao.flowable.jpa.entity;
|
package com.lanyuanxiaoyao.flowable.jpa.entity;
|
||||||
|
|
||||||
import com.lanyuanxiaoyao.flowable.core.model.FlowableAction;
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableAction;
|
||||||
import com.lanyuanxiaoyao.flowable.core.model.FlowableHistory;
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
@@ -9,7 +8,6 @@ import javax.persistence.EntityListeners;
|
|||||||
import javax.persistence.EnumType;
|
import javax.persistence.EnumType;
|
||||||
import javax.persistence.Enumerated;
|
import javax.persistence.Enumerated;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
import javax.persistence.Table;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
@@ -26,11 +24,10 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
|||||||
@ToString
|
@ToString
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@Entity
|
@Entity(name = "flowable_history")
|
||||||
@Table(name = "flowable_history")
|
|
||||||
@DynamicUpdate
|
@DynamicUpdate
|
||||||
@EntityListeners(AuditingEntityListener.class)
|
@EntityListeners(AuditingEntityListener.class)
|
||||||
public class JpaFlowableHistory {
|
public class FlowableHistory {
|
||||||
@Id
|
@Id
|
||||||
private String historyId;
|
private String historyId;
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
@@ -41,14 +38,15 @@ public class JpaFlowableHistory {
|
|||||||
@CreatedDate
|
@CreatedDate
|
||||||
private LocalDateTime createdTime;
|
private LocalDateTime createdTime;
|
||||||
|
|
||||||
public JpaFlowableHistory(FlowableHistory history) {
|
public FlowableHistory(com.lanyuanxiaoyao.flowable.core.model.FlowableHistory history) {
|
||||||
this.historyId = history.getHistoryId();
|
this.historyId = history.getHistoryId();
|
||||||
this.instanceId = history.getInstanceId();
|
this.instanceId = history.getInstanceId();
|
||||||
this.action = history.getAction();
|
this.action = history.getAction();
|
||||||
this.comment = history.getComment();
|
this.comment = history.getComment();
|
||||||
|
this.createdTime = history.getCreatedTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
public FlowableHistory toFlowableHistory() {
|
public com.lanyuanxiaoyao.flowable.core.model.FlowableHistory toFlowableHistory() {
|
||||||
return new FlowableHistory(historyId, instanceId, action, comment, createdTime);
|
return new com.lanyuanxiaoyao.flowable.core.model.FlowableHistory(historyId, instanceId, action, comment, createdTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
package com.lanyuanxiaoyao.flowable.jpa.entity;
|
package com.lanyuanxiaoyao.flowable.jpa.entity;
|
||||||
|
|
||||||
import com.lanyuanxiaoyao.flowable.core.model.FlowableInstance;
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableMetadata;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectInputStream;
|
import java.io.ObjectInputStream;
|
||||||
import java.io.ObjectOutputStream;
|
import java.io.ObjectOutputStream;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.Map;
|
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.EntityListeners;
|
import javax.persistence.EntityListeners;
|
||||||
@@ -15,7 +14,6 @@ import javax.persistence.EnumType;
|
|||||||
import javax.persistence.Enumerated;
|
import javax.persistence.Enumerated;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
import javax.persistence.Lob;
|
import javax.persistence.Lob;
|
||||||
import javax.persistence.Table;
|
|
||||||
import lombok.Cleanup;
|
import lombok.Cleanup;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
@@ -35,11 +33,10 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
|||||||
@ToString
|
@ToString
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@Entity
|
@Entity(name = "flowable_instance")
|
||||||
@Table(name = "flowable_instance")
|
|
||||||
@DynamicUpdate
|
@DynamicUpdate
|
||||||
@EntityListeners(AuditingEntityListener.class)
|
@EntityListeners(AuditingEntityListener.class)
|
||||||
public class JpaFlowableInstance {
|
public class FlowableInstance {
|
||||||
@Id
|
@Id
|
||||||
private String instanceId;
|
private String instanceId;
|
||||||
@Lob
|
@Lob
|
||||||
@@ -48,19 +45,24 @@ public class JpaFlowableInstance {
|
|||||||
private String currentNodeId;
|
private String currentNodeId;
|
||||||
@Enumerated(EnumType.STRING)
|
@Enumerated(EnumType.STRING)
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private FlowableInstance.Status status = FlowableInstance.Status.RUNNING;
|
private com.lanyuanxiaoyao.flowable.core.model.FlowableInstance.Status status = com.lanyuanxiaoyao.flowable.core.model.FlowableInstance.Status.RUNNING;
|
||||||
|
|
||||||
@CreatedDate
|
@CreatedDate
|
||||||
private LocalDateTime createdTime;
|
private LocalDateTime createdTime;
|
||||||
@LastModifiedDate
|
@LastModifiedDate
|
||||||
private LocalDateTime updatedTime;
|
private LocalDateTime updatedTime;
|
||||||
|
|
||||||
|
private String extra;
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public JpaFlowableInstance(FlowableInstance instance) {
|
public FlowableInstance(com.lanyuanxiaoyao.flowable.core.model.FlowableInstance instance) {
|
||||||
this.instanceId = instance.getInstanceId();
|
this.instanceId = instance.getInstanceId();
|
||||||
this.metadata = objectToBytes(instance.getMetadata());
|
this.metadata = objectToBytes(instance.getMetadata());
|
||||||
this.currentNodeId = instance.getCurrentNodeId();
|
this.currentNodeId = instance.getCurrentNodeId();
|
||||||
this.status = instance.getStatus();
|
this.status = instance.getStatus();
|
||||||
|
this.extra = instance.getExtra();
|
||||||
|
this.createdTime = instance.getCreatedTime();
|
||||||
|
this.updatedTime = instance.getUpdatedTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] objectToBytes(Object object) throws IOException {
|
private static byte[] objectToBytes(Object object) throws IOException {
|
||||||
@@ -81,14 +83,15 @@ public class JpaFlowableInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public FlowableInstance toFlowableInstance() {
|
public com.lanyuanxiaoyao.flowable.core.model.FlowableInstance toFlowableInstance() {
|
||||||
return new FlowableInstance(
|
return com.lanyuanxiaoyao.flowable.core.model.FlowableInstance.builder()
|
||||||
instanceId,
|
.instanceId(instanceId)
|
||||||
currentNodeId,
|
.currentNodeId(currentNodeId)
|
||||||
(Map<String, Object>) bytesToObject(this.metadata),
|
.metadata((FlowableMetadata) bytesToObject(this.metadata))
|
||||||
status,
|
.status(status)
|
||||||
createdTime,
|
.createdTime(createdTime)
|
||||||
updatedTime
|
.updatedTime(updatedTime)
|
||||||
);
|
.extra(extra)
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package com.lanyuanxiaoyao.flowable.jpa.entity;
|
package com.lanyuanxiaoyao.flowable.jpa.entity;
|
||||||
|
|
||||||
import com.lanyuanxiaoyao.flowable.core.model.FlowableAction;
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableAction;
|
||||||
import com.lanyuanxiaoyao.flowable.core.model.FlowableNode;
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.persistence.CollectionTable;
|
import javax.persistence.CollectionTable;
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
@@ -14,8 +14,6 @@ import javax.persistence.EnumType;
|
|||||||
import javax.persistence.Enumerated;
|
import javax.persistence.Enumerated;
|
||||||
import javax.persistence.ForeignKey;
|
import javax.persistence.ForeignKey;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
import javax.persistence.MapKeyColumn;
|
|
||||||
import javax.persistence.Table;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
@@ -33,11 +31,10 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
|||||||
@ToString
|
@ToString
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@Entity
|
@Entity(name = "flowable_node")
|
||||||
@Table(name = "flowable_node")
|
|
||||||
@DynamicUpdate
|
@DynamicUpdate
|
||||||
@EntityListeners(AuditingEntityListener.class)
|
@EntityListeners(AuditingEntityListener.class)
|
||||||
public class JpaFlowableNode {
|
public class FlowableNode {
|
||||||
@Id
|
@Id
|
||||||
private String nodeId;
|
private String nodeId;
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
@@ -46,31 +43,49 @@ public class JpaFlowableNode {
|
|||||||
|
|
||||||
@Enumerated(EnumType.STRING)
|
@Enumerated(EnumType.STRING)
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private FlowableNode.Type type;
|
private com.lanyuanxiaoyao.flowable.core.model.FlowableNode.Type type;
|
||||||
private String automaticAction;
|
@Column(nullable = false)
|
||||||
|
private String handler;
|
||||||
@ElementCollection(fetch = javax.persistence.FetchType.EAGER)
|
@ElementCollection(fetch = javax.persistence.FetchType.EAGER)
|
||||||
@MapKeyColumn(name = "action")
|
@CollectionTable(name = "flowable_node_targets", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
|
||||||
@Column(name = "nodeId")
|
private Map<FlowableAction, String> targets;
|
||||||
@CollectionTable(name = "flowable_node_manual_actions", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
|
|
||||||
private Map<FlowableAction, String> manualActions;
|
@Column(nullable = false)
|
||||||
|
private String accessor;
|
||||||
|
@ElementCollection(fetch = javax.persistence.FetchType.EAGER)
|
||||||
|
@CollectionTable(name = "flowable_node_listeners", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
|
||||||
|
private List<String> listeners;
|
||||||
|
|
||||||
@CreatedDate
|
@CreatedDate
|
||||||
private LocalDateTime createdTime;
|
private LocalDateTime createdTime;
|
||||||
@LastModifiedDate
|
@LastModifiedDate
|
||||||
private LocalDateTime updateTime;
|
private LocalDateTime updatedTime;
|
||||||
|
|
||||||
public JpaFlowableNode(FlowableNode node) {
|
public FlowableNode(com.lanyuanxiaoyao.flowable.core.model.FlowableNode node) {
|
||||||
this.nodeId = node.getNodeId();
|
this.nodeId = node.getNodeId();
|
||||||
this.name = node.getName();
|
this.name = node.getName();
|
||||||
this.description = node.getDescription();
|
this.description = node.getDescription();
|
||||||
this.type = node.getType();
|
this.type = node.getType();
|
||||||
this.automaticAction = node.getAutomaticAction();
|
this.handler = node.getHandler();
|
||||||
this.manualActions = node.getManualActions();
|
this.targets = node.getTargets();
|
||||||
|
this.accessor = node.getAccessor();
|
||||||
|
this.listeners = node.getListeners();
|
||||||
|
this.createdTime = node.getCreatedTime();
|
||||||
|
this.updatedTime = node.getUpdatedTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
public FlowableNode toFlowableNode() {
|
public com.lanyuanxiaoyao.flowable.core.model.FlowableNode toFlowableNode() {
|
||||||
FlowableNode node = new FlowableNode(nodeId, name, description, type, automaticAction, manualActions, null, createdTime);
|
return com.lanyuanxiaoyao.flowable.core.model.FlowableNode.builder()
|
||||||
node.setUpdatedTime(updateTime);
|
.nodeId(nodeId)
|
||||||
return node;
|
.name(name)
|
||||||
|
.description(description)
|
||||||
|
.type(type)
|
||||||
|
.handler(handler)
|
||||||
|
.targets(targets)
|
||||||
|
.accessor(accessor)
|
||||||
|
.listeners(listeners)
|
||||||
|
.createdTime(createdTime)
|
||||||
|
.updatedTime(updatedTime)
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.lanyuanxiaoyao.flowable.jpa.repository;
|
||||||
|
|
||||||
|
import com.lanyuanxiaoyao.flowable.jpa.entity.FlowableHistory;
|
||||||
|
import java.util.List;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
import org.springframework.data.repository.query.QueryByExampleExecutor;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lanyuanxiaoyao
|
||||||
|
* @version 20250102
|
||||||
|
*/
|
||||||
|
@Repository
|
||||||
|
public interface FlowableHistoryRepository extends JpaRepository<FlowableHistory, String>, JpaSpecificationExecutor<FlowableHistory>, QueryByExampleExecutor<FlowableHistory> {
|
||||||
|
List<FlowableHistory> findAllByInstanceId(String instanceId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.lanyuanxiaoyao.flowable.jpa.repository;
|
||||||
|
|
||||||
|
import com.lanyuanxiaoyao.flowable.jpa.entity.FlowableInstance;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
import org.springframework.data.repository.query.QueryByExampleExecutor;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lanyuanxiaoyao
|
||||||
|
* @version 20250102
|
||||||
|
*/
|
||||||
|
@Repository
|
||||||
|
public interface FlowableInstanceRepository extends JpaRepository<FlowableInstance, String>, JpaSpecificationExecutor<FlowableInstance>, QueryByExampleExecutor<FlowableInstance> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.lanyuanxiaoyao.flowable.jpa.repository;
|
||||||
|
|
||||||
|
import com.lanyuanxiaoyao.flowable.jpa.entity.FlowableNode;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
|
import org.springframework.data.repository.query.QueryByExampleExecutor;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lanyuanxiaoyao
|
||||||
|
* @version 20250102
|
||||||
|
*/
|
||||||
|
@Repository
|
||||||
|
public interface FlowableNodeRepository extends JpaRepository<FlowableNode, String>, JpaSpecificationExecutor<FlowableNode>, QueryByExampleExecutor<FlowableNode> {
|
||||||
|
}
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package com.lanyuanxiaoyao.flowable.jpa.repository;
|
|
||||||
|
|
||||||
import com.lanyuanxiaoyao.flowable.jpa.entity.JpaFlowableHistory;
|
|
||||||
import java.util.List;
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
|
||||||
import org.springframework.stereotype.Repository;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lanyuanxiaoyao
|
|
||||||
* @version 20250102
|
|
||||||
*/
|
|
||||||
@Repository
|
|
||||||
public interface JpaFlowableHistoryRepository extends JpaRepository<JpaFlowableHistory, String> {
|
|
||||||
List<JpaFlowableHistory> findAllByInstanceId(String instanceId);
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package com.lanyuanxiaoyao.flowable.jpa.repository;
|
|
||||||
|
|
||||||
import com.lanyuanxiaoyao.flowable.jpa.entity.JpaFlowableInstance;
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
|
||||||
import org.springframework.stereotype.Repository;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lanyuanxiaoyao
|
|
||||||
* @version 20250102
|
|
||||||
*/
|
|
||||||
@Repository
|
|
||||||
public interface JpaFlowableInstanceRepository extends JpaRepository<JpaFlowableInstance, String> {
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package com.lanyuanxiaoyao.flowable.jpa.repository;
|
|
||||||
|
|
||||||
import com.lanyuanxiaoyao.flowable.jpa.entity.JpaFlowableNode;
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
|
||||||
import org.springframework.stereotype.Repository;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lanyuanxiaoyao
|
|
||||||
* @version 20250102
|
|
||||||
*/
|
|
||||||
@Repository
|
|
||||||
public interface JpaFlowableNodeRepository extends JpaRepository<JpaFlowableNode, String> {
|
|
||||||
}
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.lanyuanxiaoyao.flowable.jpa.SpringFlowableAutoConfiguration
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
package com.lanyuanxiaoyao.flowable.core;
|
package com.lanyuanxiaoyao.flowable.jpa;
|
||||||
|
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.manager.FlowableConfiguration;
|
||||||
import com.lanyuanxiaoyao.flowable.core.manager.FlowableManager;
|
import com.lanyuanxiaoyao.flowable.core.manager.FlowableManager;
|
||||||
import com.lanyuanxiaoyao.flowable.core.model.FlowableAction;
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableAction;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableHandler;
|
||||||
import com.lanyuanxiaoyao.flowable.core.model.FlowableInstance;
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableInstance;
|
||||||
import com.lanyuanxiaoyao.flowable.core.model.FlowableNode;
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableNode;
|
||||||
import java.util.Map;
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
@@ -13,12 +14,12 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
* @version 20250102
|
* @version 20250102
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class SimpleAutoAction implements FlowableNode.AutoAction {
|
public class SimpleAutoAction implements FlowableHandler {
|
||||||
@Resource
|
@Resource
|
||||||
private FlowableManager flowableManager;
|
private FlowableManager flowableManager;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FlowableAction action(FlowableInstance instance, FlowableNode node, Map<String, Object> metadata) {
|
public FlowableAction handle(FlowableConfiguration configuration, FlowableInstance instance, FlowableNode node, FlowableAction action) {
|
||||||
log.info("Initial with spring: {}", flowableManager.listNodes());
|
log.info("Initial with spring: {}", flowableManager.listNodes());
|
||||||
return FlowableAction.APPROVE;
|
return FlowableAction.APPROVE;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.lanyuanxiaoyao.flowable.jpa;
|
||||||
|
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.manager.FlowableManager;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableAction;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableInstance;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableListener;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableNode;
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lanyuanxiaoyao
|
||||||
|
* @version 20250106
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class SimpleListener implements FlowableListener {
|
||||||
|
@Resource
|
||||||
|
private FlowableManager flowableManager;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActionStart(FlowableInstance instance, FlowableNode node, FlowableAction action) {
|
||||||
|
log.info("onActionStart with spring: {}", flowableManager.listNodes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAction(FlowableInstance instance, FlowableNode node, FlowableAction action) {
|
||||||
|
log.info("onAction with spring: {}", flowableManager.listNodes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActionComplete(FlowableInstance instance, FlowableNode node, FlowableAction action) {
|
||||||
|
log.info("onActionComplete with spring: {}", flowableManager.listNodes());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.lanyuanxiaoyao.flowable.jpa;
|
package com.lanyuanxiaoyao.flowable.jpa;
|
||||||
|
|
||||||
import com.lanyuanxiaoyao.flowable.core.SimpleAutoAction;
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package com.lanyuanxiaoyao.flowable.jpa;
|
package com.lanyuanxiaoyao.flowable.jpa;
|
||||||
|
|
||||||
import com.lanyuanxiaoyao.flowable.core.manager.FlowableManager;
|
import com.lanyuanxiaoyao.flowable.core.manager.FlowableManager;
|
||||||
import com.lanyuanxiaoyao.flowable.core.test.TestFlowableManager;
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableHandler;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableListener;
|
||||||
|
import com.lanyuanxiaoyao.flowable.test.TestFlowableManager;
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
@@ -18,4 +20,14 @@ public class TestSpringFlowableManager extends TestFlowableManager {
|
|||||||
protected FlowableManager flowableManager() {
|
protected FlowableManager flowableManager() {
|
||||||
return flowableManager;
|
return flowableManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class<? extends FlowableHandler> getHandler() {
|
||||||
|
return SimpleAutoAction.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class<? extends FlowableListener> getListenerClass() {
|
||||||
|
return SimpleListener.class;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,34 +12,9 @@
|
|||||||
<artifactId>flowable-core</artifactId>
|
<artifactId>flowable-core</artifactId>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
|
||||||
<groupId>org.projectlombok</groupId>
|
|
||||||
<artifactId>lombok</artifactId>
|
|
||||||
<version>1.18.34</version>
|
|
||||||
<scope>compile</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
<artifactId>slf4j-api</artifactId>
|
<artifactId>slf4j-api</artifactId>
|
||||||
<version>2.0.16</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.junit.jupiter</groupId>
|
|
||||||
<artifactId>junit-jupiter-api</artifactId>
|
|
||||||
<version>5.11.4</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.slf4j</groupId>
|
|
||||||
<artifactId>slf4j-simple</artifactId>
|
|
||||||
<version>2.0.16</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.junit.jupiter</groupId>
|
|
||||||
<artifactId>junit-jupiter</artifactId>
|
|
||||||
<version>5.11.4</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.lanyuanxiaoyao.flowable.core.helper;
|
|||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,7 +25,26 @@ public class MapHelper {
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Map<String, Object> empty() {
|
public static <K, V> Map<K, V> empty() {
|
||||||
return new HashMap<>(0);
|
return new HashMap<>(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isEmpty(Map<?, ?> map) {
|
||||||
|
return Objects.isNull(map) || map.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isNotEmpty(Map<?, ?> map) {
|
||||||
|
return !isEmpty(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public static <K, V> Map<K, V> concat(Map<K, V>... maps) {
|
||||||
|
Map<K, V> result = new HashMap<>();
|
||||||
|
for (Map<K, V> map : maps) {
|
||||||
|
if (Objects.nonNull(map)) {
|
||||||
|
result.putAll(map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.lanyuanxiaoyao.flowable.core.manager;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置项
|
||||||
|
*
|
||||||
|
* @author lanyuanxiaoyao
|
||||||
|
* @version 20250103
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
public class FlowableConfiguration {
|
||||||
|
/**
|
||||||
|
* 在上下文中查询权限的key
|
||||||
|
*/
|
||||||
|
@Builder.Default
|
||||||
|
private String accessorKey = Constants.DEFAULT_ACCESSOR_KEY;
|
||||||
|
|
||||||
|
public interface Constants {
|
||||||
|
String DEFAULT_ACCESSOR_KEY = "accessor";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,10 +3,13 @@ package com.lanyuanxiaoyao.flowable.core.manager;
|
|||||||
import com.lanyuanxiaoyao.flowable.core.helper.ListHelper;
|
import com.lanyuanxiaoyao.flowable.core.helper.ListHelper;
|
||||||
import com.lanyuanxiaoyao.flowable.core.helper.MapHelper;
|
import com.lanyuanxiaoyao.flowable.core.helper.MapHelper;
|
||||||
import com.lanyuanxiaoyao.flowable.core.helper.StringHelper;
|
import com.lanyuanxiaoyao.flowable.core.helper.StringHelper;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableAccessor;
|
||||||
import com.lanyuanxiaoyao.flowable.core.model.FlowableAction;
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableAction;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableHandler;
|
||||||
import com.lanyuanxiaoyao.flowable.core.model.FlowableHistory;
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableHistory;
|
||||||
import com.lanyuanxiaoyao.flowable.core.model.FlowableInstance;
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableInstance;
|
||||||
import com.lanyuanxiaoyao.flowable.core.model.FlowableListener;
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableListener;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableMetadata;
|
||||||
import com.lanyuanxiaoyao.flowable.core.model.FlowableNode;
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableNode;
|
||||||
import com.lanyuanxiaoyao.flowable.core.repository.FlowableRepository;
|
import com.lanyuanxiaoyao.flowable.core.repository.FlowableRepository;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@@ -24,40 +27,53 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public abstract class FlowableManager {
|
public abstract class FlowableManager {
|
||||||
private final FlowableRepository flowableRepository;
|
private static final String DEFAULT_APPROVE_COMMENT = "审批通过";
|
||||||
private final IdGenerator idGenerator;
|
private final FlowableConfiguration configuration;
|
||||||
|
private final FlowableRepository repository;
|
||||||
|
|
||||||
public FlowableManager(FlowableRepository flowableRepository, IdGenerator idGenerator) {
|
public FlowableManager(FlowableConfiguration configuration, FlowableRepository repository) {
|
||||||
this.flowableRepository = flowableRepository;
|
this.configuration = configuration;
|
||||||
this.idGenerator = idGenerator;
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean existsNode(String nodeId) {
|
||||||
|
return repository.existsNode(nodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FlowableNode> listNodes() {
|
public List<FlowableNode> listNodes() {
|
||||||
return flowableRepository.listNodes();
|
return repository.listNodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
public FlowableNode getNode(String nodeId) {
|
public FlowableNode getNode(String nodeId) {
|
||||||
return flowableRepository.getNode(nodeId);
|
return repository.getNode(nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean existsInstance(String instanceId) {
|
||||||
|
return repository.existsInstance(instanceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FlowableInstance> listInstances() {
|
public List<FlowableInstance> listInstances() {
|
||||||
return flowableRepository.listInstances();
|
return repository.listInstances();
|
||||||
}
|
}
|
||||||
|
|
||||||
public FlowableInstance getInstance(String instantId) {
|
public FlowableInstance getInstance(String instantId) {
|
||||||
return flowableRepository.getInstance(instantId);
|
return repository.getInstance(instantId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean existsHistory(String historyId) {
|
||||||
|
return repository.existsHistory(historyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FlowableHistory> listHistories(String instanceId) {
|
public List<FlowableHistory> listHistories(String instanceId) {
|
||||||
return flowableRepository.listHistories(instanceId);
|
return repository.listHistories(instanceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FlowableHistory getHistory(String historyId) {
|
public FlowableHistory getHistory(String historyId) {
|
||||||
return flowableRepository.getHistory(historyId);
|
return repository.getHistory(historyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void create(FlowableNode... node) {
|
public void create(FlowableNode... node) {
|
||||||
flowableRepository.saveNode(ListHelper.of(node));
|
repository.saveNode(ListHelper.of(node));
|
||||||
}
|
}
|
||||||
|
|
||||||
public String start(String nodeId) {
|
public String start(String nodeId) {
|
||||||
@@ -65,22 +81,38 @@ public abstract class FlowableManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String start(String nodeId, Map<String, Object> metadata) {
|
public String start(String nodeId, Map<String, Object> metadata) {
|
||||||
FlowableNode node = flowableRepository.getNode(nodeId);
|
return start(nodeId, metadata, null);
|
||||||
FlowableInstance instance = new FlowableInstance(idGenerator.createId(), node.getNodeId(), metadata);
|
}
|
||||||
flowableRepository.saveInstance(instance);
|
|
||||||
|
|
||||||
|
public String start(String nodeId, Map<String, Object> metadata, String extra) {
|
||||||
|
FlowableNode node = repository.getNode(nodeId);
|
||||||
|
FlowableInstance instance = FlowableInstance.builder()
|
||||||
|
.instanceId(createId())
|
||||||
|
.currentNodeId(node.getNodeId())
|
||||||
|
.metadata(new FlowableMetadata(metadata))
|
||||||
|
.extra(extra)
|
||||||
|
.build();
|
||||||
|
repository.saveInstance(instance);
|
||||||
if (FlowableNode.Type.AUTOMATIC.equals(node.getType())) {
|
if (FlowableNode.Type.AUTOMATIC.equals(node.getType())) {
|
||||||
autoAction(instance, node, MapHelper.empty());
|
automaticAction(instance);
|
||||||
}
|
}
|
||||||
return instance.getInstanceId();
|
return instance.getInstanceId();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void approve(String instanceId) {
|
public void approve(String instanceId) {
|
||||||
approve(instanceId, "审批通过");
|
approve(instanceId, DEFAULT_APPROVE_COMMENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void approve(String instanceId, String comment) {
|
public void approve(String instanceId, String comment) {
|
||||||
action(instanceId, FlowableAction.APPROVE, comment);
|
manualAction(instanceId, FlowableAction.APPROVE, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void approve(String instanceId, Map<String, Object> metadata) {
|
||||||
|
manualAction(instanceId, FlowableAction.APPROVE, DEFAULT_APPROVE_COMMENT, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void approve(String instanceId, String comment, Map<String, Object> metadata) {
|
||||||
|
manualAction(instanceId, FlowableAction.APPROVE, comment, metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reject(String instanceId) {
|
public void reject(String instanceId) {
|
||||||
@@ -88,7 +120,7 @@ public abstract class FlowableManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void reject(String instanceId, String comment) {
|
public void reject(String instanceId, String comment) {
|
||||||
action(instanceId, FlowableAction.REJECT, comment);
|
manualAction(instanceId, FlowableAction.REJECT, comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void terminal(String instanceId) {
|
public void terminal(String instanceId) {
|
||||||
@@ -96,72 +128,99 @@ public abstract class FlowableManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void terminal(String instanceId, String comment) {
|
public void terminal(String instanceId, String comment) {
|
||||||
action(instanceId, FlowableAction.TERMINAL, comment);
|
manualAction(instanceId, FlowableAction.TERMINAL, comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void action(String instanceId, FlowableAction action, String comment) {
|
public void manualAction(String instanceId, FlowableAction action, String comment) {
|
||||||
action(instanceId, action, comment, MapHelper.empty());
|
manualAction(instanceId, action, comment, MapHelper.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void autoAction(FlowableInstance instance, FlowableNode node, Map<String, Object> metadata) {
|
private void manualAction(String instanceId, FlowableAction action, String comment, Map<String, Object> metadata) {
|
||||||
String actionClass = node.getAutomaticAction();
|
FlowableInstance instance = repository.getInstance(instanceId);
|
||||||
if (StringHelper.isBlank(actionClass)) {
|
if (MapHelper.isNotEmpty(metadata)) {
|
||||||
throw new IllegalArgumentException("自动节点执行器为空");
|
instance.getMetadata().putAll(metadata);
|
||||||
}
|
}
|
||||||
FlowableNode.AutoAction autoAction = createBean(actionClass);
|
action(instance, action, comment);
|
||||||
action(instance, node, autoAction.action(instance, node, metadata), "系统自动执行", metadata);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void action(String instanceId, FlowableAction action, String comment, Map<String, Object> metadata) {
|
private void automaticAction(FlowableInstance instance) {
|
||||||
FlowableInstance instance = flowableRepository.getInstance(instanceId);
|
action(instance, null, "系统自动执行");
|
||||||
FlowableNode node = flowableRepository.getNode(instance.getCurrentNodeId());
|
|
||||||
action(instance, node, action, comment, metadata);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void action(FlowableInstance instance, FlowableNode node, FlowableAction action, String comment, Map<String, Object> metadata) {
|
private void action(FlowableInstance instance, final FlowableAction inputAction, String comment) {
|
||||||
if (FlowableInstance.Status.COMPLETED.equals(instance.getStatus()) || FlowableInstance.Status.ERROR.equals(instance.getStatus())) {
|
FlowableNode node = repository.getNode(instance.getCurrentNodeId());
|
||||||
|
String handlerClass = node.getHandler();
|
||||||
|
if (StringHelper.isBlank(handlerClass)) {
|
||||||
|
throw new IllegalArgumentException("节点执行器为空");
|
||||||
|
}
|
||||||
|
FlowableHandler handler = createBean(handlerClass);
|
||||||
|
final FlowableAction action = handler.handle(configuration, instance, node, inputAction);
|
||||||
|
if (Objects.isNull(action)) {
|
||||||
|
throw new IllegalArgumentException("节点执行结果不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
callListeners(node.getListeners(), listener -> listener.onActionStart(instance, node, action));
|
||||||
|
|
||||||
|
if (FlowableInstance.Status.COMPLETED.equals(instance.getStatus()) || FlowableInstance.Status.TERMINAL.equals(instance.getStatus())) {
|
||||||
throw new IllegalArgumentException("ID为" + instance.getInstanceId() + "的流程已结束,无法操作");
|
throw new IllegalArgumentException("ID为" + instance.getInstanceId() + "的流程已结束,无法操作");
|
||||||
}
|
}
|
||||||
if (FlowableAction.TERMINAL.equals(action)) {
|
|
||||||
saveInstance(instance, FlowableInstance.Status.ERROR, metadata, action, comment);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Objects.isNull(node.getManualActions())
|
|
||||||
|| !node.getManualActions().containsKey(action)
|
|
||||||
|| StringHelper.isBlank(node.getManualActions().get(action))) {
|
|
||||||
saveInstance(instance, FlowableInstance.Status.COMPLETED, metadata, action, comment);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String nextNodeId = node.getManualActions().get(action);
|
|
||||||
FlowableNode nextNode = flowableRepository.getNode(nextNodeId);
|
|
||||||
instance.setCurrentNodeId(nextNode.getNodeId());
|
|
||||||
saveInstance(instance, FlowableInstance.Status.RUNNING, metadata, action, comment);
|
|
||||||
|
|
||||||
|
String accessorClass = node.getAccessor();
|
||||||
|
if (StringHelper.isBlank(accessorClass)) {
|
||||||
|
throw new IllegalArgumentException("权限检查器为空");
|
||||||
|
}
|
||||||
|
FlowableAccessor accessor = createBean(accessorClass);
|
||||||
|
if (!accessor.access(instance, node, action)) {
|
||||||
|
throw new IllegalArgumentException("权限校验不通过");
|
||||||
|
}
|
||||||
|
|
||||||
|
callListeners(node.getListeners(), listener -> listener.onAction(instance, node, action));
|
||||||
|
|
||||||
|
// 如果是挂起操作,就直接返回,不做操作
|
||||||
|
if (FlowableAction.SUSPEND.equals(action)) {
|
||||||
|
saveInstance(instance, instance.getStatus(), action, comment);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FlowableAction.TERMINAL.equals(action)) {
|
||||||
|
saveInstance(instance, FlowableInstance.Status.TERMINAL, action, comment);
|
||||||
|
callListeners(node.getListeners(), listener -> listener.onActionComplete(instance, node, action));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Objects.isNull(node.getTargets())
|
||||||
|
|| !node.getTargets().containsKey(action)
|
||||||
|
|| StringHelper.isBlank(node.getTargets().get(action))) {
|
||||||
|
saveInstance(instance, FlowableInstance.Status.COMPLETED, action, comment);
|
||||||
|
callListeners(node.getListeners(), listener -> listener.onActionComplete(instance, node, action));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String nextNodeId = node.getTargets().get(action);
|
||||||
|
FlowableNode nextNode = repository.getNode(nextNodeId);
|
||||||
|
instance.setCurrentNodeId(nextNode.getNodeId());
|
||||||
|
saveInstance(instance, FlowableInstance.Status.RUNNING, action, comment);
|
||||||
|
|
||||||
|
callListeners(node.getListeners(), listener -> listener.onActionComplete(instance, node, action));
|
||||||
if (FlowableNode.Type.AUTOMATIC.equals(nextNode.getType())) {
|
if (FlowableNode.Type.AUTOMATIC.equals(nextNode.getType())) {
|
||||||
autoAction(instance, node, metadata);
|
automaticAction(instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveInstance(FlowableInstance instance, FlowableInstance.Status status, Map<String, Object> metadata, FlowableAction action, String comment) {
|
private void saveInstance(FlowableInstance instance, FlowableInstance.Status status, FlowableAction action, String comment) {
|
||||||
instance.setStatus(status);
|
instance.setStatus(status);
|
||||||
if (Objects.nonNull(metadata)) {
|
|
||||||
instance.addMetadata(metadata);
|
|
||||||
}
|
|
||||||
instance.setUpdatedTime(LocalDateTime.now());
|
instance.setUpdatedTime(LocalDateTime.now());
|
||||||
|
|
||||||
FlowableHistory history = new FlowableHistory(idGenerator.createId(), instance.getInstanceId(), action, comment);
|
FlowableHistory history = new FlowableHistory(createId(), instance.getInstanceId(), action, comment);
|
||||||
flowableRepository.saveInstanceAndHistory(instance, history);
|
repository.saveInstanceAndHistory(instance, history);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void callListeners(List<FlowableListener> listeners, Consumer<FlowableListener> handler) {
|
private void callListeners(List<String> listeners, Consumer<FlowableListener> handler) {
|
||||||
for (FlowableListener listener : listeners) {
|
for (String listenerClass : listeners) {
|
||||||
|
FlowableListener listener = createBean(listenerClass);
|
||||||
handler.accept(listener);
|
handler.accept(listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract <T> T createBean(String classpath);
|
protected abstract <T> T createBean(String classpath);
|
||||||
|
|
||||||
public interface IdGenerator {
|
protected abstract String createId();
|
||||||
String createId();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,5 +7,12 @@ package com.lanyuanxiaoyao.flowable.core.model;
|
|||||||
* @version 20241231
|
* @version 20241231
|
||||||
*/
|
*/
|
||||||
public interface FlowableAccessor {
|
public interface FlowableAccessor {
|
||||||
void access(String accessor);
|
boolean access(FlowableInstance instance, FlowableNode node, FlowableAction action);
|
||||||
|
|
||||||
|
class DefaultFlowableAccessor implements FlowableAccessor {
|
||||||
|
@Override
|
||||||
|
public boolean access(FlowableInstance instance, FlowableNode node, FlowableAction action) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,4 +10,5 @@ public enum FlowableAction {
|
|||||||
APPROVE,
|
APPROVE,
|
||||||
REJECT,
|
REJECT,
|
||||||
TERMINAL,
|
TERMINAL,
|
||||||
|
SUSPEND,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.lanyuanxiaoyao.flowable.core.model;
|
||||||
|
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.manager.FlowableConfiguration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理器
|
||||||
|
*
|
||||||
|
* @author lanyuanxiaoyao
|
||||||
|
* @version 20250103
|
||||||
|
*/
|
||||||
|
public interface FlowableHandler {
|
||||||
|
FlowableAction handle(FlowableConfiguration configuration, FlowableInstance instance, FlowableNode node, FlowableAction action);
|
||||||
|
|
||||||
|
class DefaultFlowableHandler implements FlowableHandler {
|
||||||
|
@Override
|
||||||
|
public FlowableAction handle(FlowableConfiguration configuration, FlowableInstance instance, FlowableNode node, FlowableAction action) {
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package com.lanyuanxiaoyao.flowable.core.model;
|
package com.lanyuanxiaoyao.flowable.core.model;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -8,6 +10,8 @@ import lombok.Data;
|
|||||||
* @version 20241231
|
* @version 20241231
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
public class FlowableHistory {
|
public class FlowableHistory {
|
||||||
private final String historyId;
|
private final String historyId;
|
||||||
private final String instanceId;
|
private final String instanceId;
|
||||||
@@ -18,12 +22,4 @@ public class FlowableHistory {
|
|||||||
public FlowableHistory(String historyId, String instanceId, FlowableAction action, String comment) {
|
public FlowableHistory(String historyId, String instanceId, FlowableAction action, String comment) {
|
||||||
this(historyId, instanceId, action, comment, LocalDateTime.now());
|
this(historyId, instanceId, action, comment, LocalDateTime.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
public FlowableHistory(String historyId, String instanceId, FlowableAction action, String comment, LocalDateTime createdTime) {
|
|
||||||
this.historyId = historyId;
|
|
||||||
this.instanceId = instanceId;
|
|
||||||
this.action = action;
|
|
||||||
this.comment = comment;
|
|
||||||
this.createdTime = createdTime;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package com.lanyuanxiaoyao.flowable.core.model;
|
package com.lanyuanxiaoyao.flowable.core.model;
|
||||||
|
|
||||||
import com.lanyuanxiaoyao.flowable.core.helper.MapHelper;
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -10,31 +11,22 @@ import lombok.Data;
|
|||||||
* @version 20241231
|
* @version 20241231
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
public class FlowableInstance {
|
public class FlowableInstance {
|
||||||
private final String instanceId;
|
private final String instanceId;
|
||||||
private final Map<String, Object> metadata;
|
@Builder.Default
|
||||||
private final LocalDateTime createdTime;
|
private final FlowableMetadata metadata = new FlowableMetadata();
|
||||||
|
@Builder.Default
|
||||||
|
private final LocalDateTime createdTime = LocalDateTime.now();
|
||||||
|
|
||||||
private String currentNodeId;
|
private String currentNodeId;
|
||||||
private Status status;
|
@Builder.Default
|
||||||
|
private Status status = Status.RUNNING;
|
||||||
|
@Builder.Default
|
||||||
private LocalDateTime updatedTime = LocalDateTime.now();
|
private LocalDateTime updatedTime = LocalDateTime.now();
|
||||||
|
|
||||||
public FlowableInstance(String instanceId, String currentNodeId) {
|
private String extra;
|
||||||
this(instanceId, currentNodeId, MapHelper.empty(), Status.RUNNING, LocalDateTime.now(), LocalDateTime.now());
|
|
||||||
}
|
|
||||||
|
|
||||||
public FlowableInstance(String instanceId, String currentNodeId, Map<String, Object> metadata) {
|
|
||||||
this(instanceId, currentNodeId, metadata, Status.RUNNING, LocalDateTime.now(), LocalDateTime.now());
|
|
||||||
}
|
|
||||||
|
|
||||||
public FlowableInstance(String instanceId, String currentNodeId, Map<String, Object> metadata, Status status, LocalDateTime createdTime, LocalDateTime updatedTime) {
|
|
||||||
this.instanceId = instanceId;
|
|
||||||
this.metadata = metadata;
|
|
||||||
this.createdTime = createdTime;
|
|
||||||
this.currentNodeId = currentNodeId;
|
|
||||||
this.status = status;
|
|
||||||
this.updatedTime = updatedTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addMetadata(Map<String, Object> metadata) {
|
public void addMetadata(Map<String, Object> metadata) {
|
||||||
this.metadata.putAll(metadata);
|
this.metadata.putAll(metadata);
|
||||||
@@ -43,6 +35,6 @@ public class FlowableInstance {
|
|||||||
public enum Status {
|
public enum Status {
|
||||||
RUNNING,
|
RUNNING,
|
||||||
COMPLETED,
|
COMPLETED,
|
||||||
ERROR,
|
TERMINAL,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,13 +7,23 @@ package com.lanyuanxiaoyao.flowable.core.model;
|
|||||||
* @version 20241231
|
* @version 20241231
|
||||||
*/
|
*/
|
||||||
public interface FlowableListener {
|
public interface FlowableListener {
|
||||||
void onStart(FlowableInstance instance);
|
void onActionStart(FlowableInstance instance, FlowableNode node, FlowableAction action);
|
||||||
|
|
||||||
void onError(FlowableInstance instance, Throwable throwable);
|
void onAction(FlowableInstance instance, FlowableNode node, FlowableAction action);
|
||||||
|
|
||||||
void onComplete(FlowableInstance instance);
|
void onActionComplete(FlowableInstance instance, FlowableNode node, FlowableAction action);
|
||||||
|
|
||||||
void onApprove(FlowableInstance instance);
|
abstract class AbstractFlowableListener implements FlowableListener {
|
||||||
|
@Override
|
||||||
|
public void onActionStart(FlowableInstance instance, FlowableNode node, FlowableAction action) {
|
||||||
|
}
|
||||||
|
|
||||||
void onReject(FlowableInstance instance);
|
@Override
|
||||||
|
public void onAction(FlowableInstance instance, FlowableNode node, FlowableAction action) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActionComplete(FlowableInstance instance, FlowableNode node, FlowableAction action) {
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
package com.lanyuanxiaoyao.flowable.core.model;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 节点流转上下文
|
||||||
|
*
|
||||||
|
* @author lanyuanxiaoyao
|
||||||
|
* @version 20250103
|
||||||
|
*/
|
||||||
|
@ToString
|
||||||
|
@Getter
|
||||||
|
public class FlowableMetadata implements Serializable {
|
||||||
|
private final Map<String, Object> metadata;
|
||||||
|
|
||||||
|
public FlowableMetadata() {
|
||||||
|
this(new HashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public FlowableMetadata(Map<String, Object> metadata) {
|
||||||
|
if (metadata == null) {
|
||||||
|
metadata = new HashMap<>();
|
||||||
|
}
|
||||||
|
this.metadata = metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object put(String key, Object value) {
|
||||||
|
return metadata.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object get(String key) {
|
||||||
|
return metadata.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T get(String key, Class<T> clazz) {
|
||||||
|
return clazz.cast(metadata.get(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getString(String key) {
|
||||||
|
return get(key, String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getInt(String key) {
|
||||||
|
return get(key, Integer.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getLong(String key) {
|
||||||
|
return get(key, Long.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getBoolean(String key) {
|
||||||
|
return get(key, Boolean.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return metadata.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return metadata.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsKey(String key) {
|
||||||
|
return metadata.containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsValue(String value) {
|
||||||
|
return metadata.containsValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object remove(String key) {
|
||||||
|
return metadata.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putAll(Map<String, Object> m) {
|
||||||
|
metadata.putAll(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getOrDefault(String key, Object defaultValue) {
|
||||||
|
return metadata.getOrDefault(key, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T getOrDefault(String key, Class<T> clazz, T defaultValue) {
|
||||||
|
return clazz.cast(metadata.getOrDefault(key, defaultValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStringOrDefault(String key, String defaultValue) {
|
||||||
|
return getOrDefault(key, String.class, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getIntOrDefault(String key, Integer defaultValue) {
|
||||||
|
return getOrDefault(key, Integer.class, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getLongOrDefault(String key, Long defaultValue) {
|
||||||
|
return getOrDefault(key, Long.class, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getBooleanOrDefault(String key, Boolean defaultValue) {
|
||||||
|
return getOrDefault(key, Boolean.class, defaultValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
package com.lanyuanxiaoyao.flowable.core.model;
|
package com.lanyuanxiaoyao.flowable.core.model;
|
||||||
|
|
||||||
import com.lanyuanxiaoyao.flowable.core.helper.ListHelper;
|
import com.lanyuanxiaoyao.flowable.core.helper.ListHelper;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.helper.MapHelper;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -13,41 +16,32 @@ import lombok.Data;
|
|||||||
* @version 20241231
|
* @version 20241231
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
public class FlowableNode {
|
public class FlowableNode {
|
||||||
private final String nodeId;
|
private final String nodeId;
|
||||||
private final String name;
|
private final String name;
|
||||||
private final String description;
|
private final String description;
|
||||||
|
|
||||||
private final Type type;
|
@Builder.Default
|
||||||
private final String automaticAction;
|
private final Type type = Type.MANUAL;
|
||||||
private final Map<FlowableAction, String> manualActions;
|
@Builder.Default
|
||||||
|
private final String handler = FlowableHandler.DefaultFlowableHandler.class.getName();
|
||||||
|
@Builder.Default
|
||||||
|
private final Map<FlowableAction, String> targets = MapHelper.empty();
|
||||||
|
|
||||||
private final List<String> listeners;
|
@Builder.Default
|
||||||
private final LocalDateTime createdTime;
|
private final String accessor = FlowableAccessor.DefaultFlowableAccessor.class.getName();
|
||||||
|
@Builder.Default
|
||||||
|
private final List<String> listeners = ListHelper.empty();
|
||||||
|
|
||||||
|
@Builder.Default
|
||||||
|
private final LocalDateTime createdTime = LocalDateTime.now();
|
||||||
|
@Builder.Default
|
||||||
private LocalDateTime updatedTime = LocalDateTime.now();
|
private LocalDateTime updatedTime = LocalDateTime.now();
|
||||||
|
|
||||||
public FlowableNode(String nodeId, String name, String description, Type type, String automaticAction, Map<FlowableAction, String> manualActions) {
|
|
||||||
this(nodeId, name, description, type, automaticAction, manualActions, ListHelper.empty(), LocalDateTime.now());
|
|
||||||
}
|
|
||||||
|
|
||||||
public FlowableNode(String nodeId, String name, String description, Type type, String automaticAction, Map<FlowableAction, String> manualActions, List<String> listeners, LocalDateTime createdTime) {
|
|
||||||
this.nodeId = nodeId;
|
|
||||||
this.name = name;
|
|
||||||
this.description = description;
|
|
||||||
this.type = type;
|
|
||||||
this.automaticAction = automaticAction;
|
|
||||||
this.manualActions = manualActions;
|
|
||||||
this.listeners = listeners;
|
|
||||||
this.createdTime = createdTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Type {
|
public enum Type {
|
||||||
AUTOMATIC,
|
AUTOMATIC,
|
||||||
MANUAL,
|
MANUAL,
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface AutoAction {
|
|
||||||
FlowableAction action(FlowableInstance instance, FlowableNode node, Map<String, Object> metadata);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import java.util.List;
|
|||||||
* @version 20241231
|
* @version 20241231
|
||||||
*/
|
*/
|
||||||
public interface FlowableRepository {
|
public interface FlowableRepository {
|
||||||
|
boolean existsNode(String nodeId);
|
||||||
|
|
||||||
void saveNode(FlowableNode node);
|
void saveNode(FlowableNode node);
|
||||||
|
|
||||||
void saveNode(List<FlowableNode> nodes);
|
void saveNode(List<FlowableNode> nodes);
|
||||||
@@ -20,12 +22,16 @@ public interface FlowableRepository {
|
|||||||
|
|
||||||
List<FlowableNode> listNodes();
|
List<FlowableNode> listNodes();
|
||||||
|
|
||||||
|
boolean existsInstance(String instanceId);
|
||||||
|
|
||||||
void saveInstance(FlowableInstance instance);
|
void saveInstance(FlowableInstance instance);
|
||||||
|
|
||||||
FlowableInstance getInstance(String instantId);
|
FlowableInstance getInstance(String instantId);
|
||||||
|
|
||||||
List<FlowableInstance> listInstances();
|
List<FlowableInstance> listInstances();
|
||||||
|
|
||||||
|
boolean existsHistory(String historyId);
|
||||||
|
|
||||||
void saveHistory(FlowableHistory history);
|
void saveHistory(FlowableHistory history);
|
||||||
|
|
||||||
FlowableHistory getHistory(String historyId);
|
FlowableHistory getHistory(String historyId);
|
||||||
|
|||||||
@@ -1,140 +0,0 @@
|
|||||||
package com.lanyuanxiaoyao.flowable.core.test;
|
|
||||||
|
|
||||||
import com.lanyuanxiaoyao.flowable.core.helper.MapHelper;
|
|
||||||
import com.lanyuanxiaoyao.flowable.core.manager.FlowableManager;
|
|
||||||
import com.lanyuanxiaoyao.flowable.core.model.FlowableAction;
|
|
||||||
import com.lanyuanxiaoyao.flowable.core.model.FlowableInstance;
|
|
||||||
import com.lanyuanxiaoyao.flowable.core.model.FlowableNode;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 集成测试
|
|
||||||
*
|
|
||||||
* @author lanyuanxiaoyao
|
|
||||||
* @version 20241231
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
public abstract class TestFlowableManager {
|
|
||||||
protected abstract FlowableManager flowableManager();
|
|
||||||
|
|
||||||
private FlowableNode createManualNode() {
|
|
||||||
return createManualNode(UUID.randomUUID().toString(), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private FlowableNode createManualNode(String nodeId) {
|
|
||||||
return createManualNode(nodeId, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private FlowableNode createManualNode(String nodeId, Map<FlowableAction, String> nextNodes) {
|
|
||||||
return new FlowableNode(
|
|
||||||
nodeId,
|
|
||||||
UUID.randomUUID().toString(),
|
|
||||||
UUID.randomUUID().toString(),
|
|
||||||
FlowableNode.Type.MANUAL,
|
|
||||||
null,
|
|
||||||
nextNodes
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 单节点审批
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testSingleNode() {
|
|
||||||
FlowableManager manager = flowableManager();
|
|
||||||
FlowableNode node1 = createManualNode();
|
|
||||||
manager.create(node1);
|
|
||||||
Assertions.assertNotNull(manager.getNode(node1.getNodeId()));
|
|
||||||
|
|
||||||
String instanceId = manager.start(node1.getNodeId());
|
|
||||||
Assertions.assertEquals(FlowableInstance.Status.RUNNING, manager.getInstance(instanceId).getStatus());
|
|
||||||
Assertions.assertTrue(manager.listHistories(instanceId).isEmpty());
|
|
||||||
|
|
||||||
manager.approve(instanceId, "3ca20a11-dfb6-435b-8dcc-d00c6c0abd7f");
|
|
||||||
Assertions.assertEquals(FlowableInstance.Status.COMPLETED, manager.getInstance(instanceId).getStatus());
|
|
||||||
Assertions.assertEquals(1, manager.listHistories(instanceId).size());
|
|
||||||
Assertions.assertEquals(FlowableAction.APPROVE, manager.listHistories(instanceId).get(0).getAction());
|
|
||||||
Assertions.assertEquals("3ca20a11-dfb6-435b-8dcc-d00c6c0abd7f", manager.listHistories(instanceId).get(0).getComment());
|
|
||||||
|
|
||||||
instanceId = manager.start(node1.getNodeId());
|
|
||||||
manager.reject(instanceId, "3ca20a11-dfb6-435b-8dcc-d00c6c0abd7f");
|
|
||||||
Assertions.assertEquals(FlowableInstance.Status.COMPLETED, manager.getInstance(instanceId).getStatus());
|
|
||||||
Assertions.assertEquals(FlowableAction.REJECT, manager.listHistories(instanceId).get(0).getAction());
|
|
||||||
Assertions.assertEquals("3ca20a11-dfb6-435b-8dcc-d00c6c0abd7f", manager.listHistories(instanceId).get(0).getComment());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMultiNode() {
|
|
||||||
FlowableManager manager = flowableManager();
|
|
||||||
FlowableNode node1 = createManualNode(
|
|
||||||
"02779cbe-0d82-4e09-9bf8-60885400d100",
|
|
||||||
MapHelper.of(
|
|
||||||
FlowableAction.APPROVE, "1e126640-34ae-40f9-b55f-9cb8099d638f",
|
|
||||||
FlowableAction.REJECT, "02779cbe-0d82-4e09-9bf8-60885400d100"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
FlowableNode node2 = createManualNode(
|
|
||||||
"1e126640-34ae-40f9-b55f-9cb8099d638f",
|
|
||||||
MapHelper.of(FlowableAction.REJECT, "02779cbe-0d82-4e09-9bf8-60885400d100")
|
|
||||||
);
|
|
||||||
manager.create(node1, node2);
|
|
||||||
|
|
||||||
String instanceId = manager.start(node1.getNodeId());
|
|
||||||
|
|
||||||
manager.reject(instanceId);
|
|
||||||
Assertions.assertEquals(FlowableInstance.Status.RUNNING, manager.getInstance(instanceId).getStatus());
|
|
||||||
Assertions.assertEquals(node1.getNodeId(), manager.getNode(manager.getInstance(instanceId).getCurrentNodeId()).getNodeId());
|
|
||||||
|
|
||||||
manager.approve(instanceId);
|
|
||||||
manager.reject(instanceId, "我觉得不行");
|
|
||||||
Assertions.assertEquals(FlowableInstance.Status.RUNNING, manager.getInstance(instanceId).getStatus());
|
|
||||||
Assertions.assertEquals(node1.getNodeId(), manager.getNode(manager.getInstance(instanceId).getCurrentNodeId()).getNodeId());
|
|
||||||
|
|
||||||
manager.approve(instanceId);
|
|
||||||
manager.approve(instanceId);
|
|
||||||
Assertions.assertEquals(FlowableInstance.Status.COMPLETED, manager.getInstance(instanceId).getStatus());
|
|
||||||
Assertions.assertEquals(node2.getNodeId(), manager.getNode(manager.getInstance(instanceId).getCurrentNodeId()).getNodeId());
|
|
||||||
|
|
||||||
Assertions.assertEquals(FlowableAction.APPROVE, manager.listHistories(instanceId).get(4).getAction());
|
|
||||||
Assertions.assertEquals(5, manager.listHistories(instanceId).size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testTerminal() {
|
|
||||||
FlowableManager manager = flowableManager();
|
|
||||||
FlowableNode node1 = createManualNode();
|
|
||||||
manager.create(node1);
|
|
||||||
|
|
||||||
String instanceId = manager.start(node1.getNodeId());
|
|
||||||
|
|
||||||
Assertions.assertEquals(FlowableInstance.Status.RUNNING, manager.getInstance(instanceId).getStatus());
|
|
||||||
|
|
||||||
manager.terminal(instanceId, "d896b642-a1d8-499c-92e7-bed63581f2f8");
|
|
||||||
Assertions.assertEquals(FlowableInstance.Status.ERROR, manager.getInstance(instanceId).getStatus());
|
|
||||||
Assertions.assertEquals(1, manager.listHistories(instanceId).size());
|
|
||||||
Assertions.assertEquals(FlowableAction.TERMINAL, manager.listHistories(instanceId).get(0).getAction());
|
|
||||||
Assertions.assertEquals("d896b642-a1d8-499c-92e7-bed63581f2f8", manager.listHistories(instanceId).get(0).getComment());
|
|
||||||
|
|
||||||
Assertions.assertThrows(IllegalArgumentException.class, () -> manager.approve(instanceId));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAutomaticNode() {
|
|
||||||
FlowableManager manager = flowableManager();
|
|
||||||
FlowableNode node = new FlowableNode(
|
|
||||||
"2733d930-7a4b-491e-b1ca-4d5811435e9f",
|
|
||||||
"自动节点",
|
|
||||||
"自动节点",
|
|
||||||
FlowableNode.Type.AUTOMATIC,
|
|
||||||
"com.lanyuanxiaoyao.flowable.core.SimpleAutoAction",
|
|
||||||
null
|
|
||||||
);
|
|
||||||
manager.create(node);
|
|
||||||
String instanceId = manager.start(node.getNodeId());
|
|
||||||
Assertions.assertEquals(FlowableInstance.Status.COMPLETED, manager.getInstance(instanceId).getStatus());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package com.lanyuanxiaoyao.flowable.core;
|
|
||||||
|
|
||||||
import com.lanyuanxiaoyao.flowable.core.model.FlowableAction;
|
|
||||||
import com.lanyuanxiaoyao.flowable.core.model.FlowableInstance;
|
|
||||||
import com.lanyuanxiaoyao.flowable.core.model.FlowableNode;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lanyuanxiaoyao
|
|
||||||
* @version 20250102
|
|
||||||
*/
|
|
||||||
public class SimpleAutoAction implements FlowableNode.AutoAction {
|
|
||||||
@Override
|
|
||||||
public FlowableAction action(FlowableInstance instance, FlowableNode node, Map<String, Object> metadata) {
|
|
||||||
return FlowableAction.APPROVE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package com.lanyuanxiaoyao.flowable.core;
|
|
||||||
|
|
||||||
import com.lanyuanxiaoyao.flowable.core.test.TestFlowableManager;
|
|
||||||
import com.lanyuanxiaoyao.flowable.core.manager.FlowableManager;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lanyuanxiaoyao
|
|
||||||
* @version 20250102
|
|
||||||
*/
|
|
||||||
public class TestSimpleFlowableManager extends TestFlowableManager {
|
|
||||||
@Override
|
|
||||||
protected FlowableManager flowableManager() {
|
|
||||||
return new SimpleFlowableManager();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
36
flowable-example/pom.xml
Normal file
36
flowable-example/pom.xml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<?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>com.lanyuanxiaoyao</groupId>
|
||||||
|
<artifactId>flowable</artifactId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>flowable-example</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.lanyuanxiaoyao</groupId>
|
||||||
|
<artifactId>flowable-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-simple</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,248 @@
|
|||||||
|
package com.lanyuanxiaoyao.flowable.test;
|
||||||
|
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.helper.ListHelper;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.helper.MapHelper;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.manager.FlowableManager;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableAction;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableHandler;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableInstance;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableListener;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableNode;
|
||||||
|
import com.lanyuanxiaoyao.flowable.test.accessor.RoleCheckAccessor;
|
||||||
|
import com.lanyuanxiaoyao.flowable.test.handler.TwoApproveHandler;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.MethodOrderer;
|
||||||
|
import org.junit.jupiter.api.Order;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.TestMethodOrder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 集成测试
|
||||||
|
*
|
||||||
|
* @author lanyuanxiaoyao
|
||||||
|
* @version 20241231
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||||
|
public abstract class TestFlowableManager {
|
||||||
|
protected abstract FlowableManager flowableManager();
|
||||||
|
|
||||||
|
private FlowableNode createManualNode() {
|
||||||
|
return createManualNode(UUID.randomUUID().toString(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private FlowableNode createManualNode(String nodeId) {
|
||||||
|
return createManualNode(nodeId, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private FlowableNode createManualNode(String nodeId, Map<FlowableAction, String> nextNodes) {
|
||||||
|
return FlowableNode.builder()
|
||||||
|
.nodeId(nodeId)
|
||||||
|
.name(UUID.randomUUID().toString())
|
||||||
|
.description(UUID.randomUUID().toString())
|
||||||
|
.targets(nextNodes)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(1)
|
||||||
|
public void testRepository() {
|
||||||
|
FlowableManager manager = flowableManager();
|
||||||
|
FlowableNode node1 = createManualNode();
|
||||||
|
Assertions.assertFalse(manager.existsNode(node1.getNodeId()));
|
||||||
|
manager.create(node1);
|
||||||
|
Assertions.assertTrue(manager.existsNode(node1.getNodeId()));
|
||||||
|
Assertions.assertNotNull(manager.getNode(node1.getNodeId()));
|
||||||
|
Assertions.assertEquals(1, manager.listNodes().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单节点审批
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@Order(2)
|
||||||
|
public void testSingleNode() {
|
||||||
|
FlowableManager manager = flowableManager();
|
||||||
|
FlowableNode node1 = createManualNode();
|
||||||
|
manager.create(node1);
|
||||||
|
Assertions.assertNotNull(manager.getNode(node1.getNodeId()));
|
||||||
|
|
||||||
|
String instanceId = manager.start(node1.getNodeId());
|
||||||
|
Assertions.assertEquals(FlowableInstance.Status.RUNNING, manager.getInstance(instanceId).getStatus());
|
||||||
|
Assertions.assertTrue(manager.listHistories(instanceId).isEmpty());
|
||||||
|
|
||||||
|
manager.approve(instanceId, "3ca20a11-dfb6-435b-8dcc-d00c6c0abd7f");
|
||||||
|
Assertions.assertEquals(FlowableInstance.Status.COMPLETED, manager.getInstance(instanceId).getStatus());
|
||||||
|
Assertions.assertEquals(1, manager.listHistories(instanceId).size());
|
||||||
|
Assertions.assertEquals(FlowableAction.APPROVE, manager.listHistories(instanceId).get(0).getAction());
|
||||||
|
Assertions.assertEquals("3ca20a11-dfb6-435b-8dcc-d00c6c0abd7f", manager.listHistories(instanceId).get(0).getComment());
|
||||||
|
|
||||||
|
instanceId = manager.start(node1.getNodeId());
|
||||||
|
manager.reject(instanceId, "3ca20a11-dfb6-435b-8dcc-d00c6c0abd7f");
|
||||||
|
Assertions.assertEquals(FlowableInstance.Status.COMPLETED, manager.getInstance(instanceId).getStatus());
|
||||||
|
Assertions.assertEquals(FlowableAction.REJECT, manager.listHistories(instanceId).get(0).getAction());
|
||||||
|
Assertions.assertEquals("3ca20a11-dfb6-435b-8dcc-d00c6c0abd7f", manager.listHistories(instanceId).get(0).getComment());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(3)
|
||||||
|
public void testMultiNode() {
|
||||||
|
FlowableManager manager = flowableManager();
|
||||||
|
FlowableNode node1 = createManualNode(
|
||||||
|
"02779cbe-0d82-4e09-9bf8-60885400d100",
|
||||||
|
MapHelper.of(
|
||||||
|
FlowableAction.APPROVE, "1e126640-34ae-40f9-b55f-9cb8099d638f",
|
||||||
|
FlowableAction.REJECT, "02779cbe-0d82-4e09-9bf8-60885400d100"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
FlowableNode node2 = createManualNode(
|
||||||
|
"1e126640-34ae-40f9-b55f-9cb8099d638f",
|
||||||
|
MapHelper.of(FlowableAction.REJECT, "02779cbe-0d82-4e09-9bf8-60885400d100")
|
||||||
|
);
|
||||||
|
manager.create(node1, node2);
|
||||||
|
|
||||||
|
String instanceId = manager.start(node1.getNodeId());
|
||||||
|
|
||||||
|
manager.reject(instanceId);
|
||||||
|
Assertions.assertEquals(FlowableInstance.Status.RUNNING, manager.getInstance(instanceId).getStatus());
|
||||||
|
Assertions.assertEquals(node1.getNodeId(), manager.getNode(manager.getInstance(instanceId).getCurrentNodeId()).getNodeId());
|
||||||
|
|
||||||
|
manager.approve(instanceId);
|
||||||
|
manager.reject(instanceId, "我觉得不行");
|
||||||
|
Assertions.assertEquals(FlowableInstance.Status.RUNNING, manager.getInstance(instanceId).getStatus());
|
||||||
|
Assertions.assertEquals(node1.getNodeId(), manager.getNode(manager.getInstance(instanceId).getCurrentNodeId()).getNodeId());
|
||||||
|
|
||||||
|
manager.approve(instanceId);
|
||||||
|
manager.approve(instanceId);
|
||||||
|
Assertions.assertEquals(FlowableInstance.Status.COMPLETED, manager.getInstance(instanceId).getStatus());
|
||||||
|
Assertions.assertEquals(node2.getNodeId(), manager.getNode(manager.getInstance(instanceId).getCurrentNodeId()).getNodeId());
|
||||||
|
|
||||||
|
Assertions.assertEquals(FlowableAction.APPROVE, manager.listHistories(instanceId).get(4).getAction());
|
||||||
|
Assertions.assertEquals(5, manager.listHistories(instanceId).size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(4)
|
||||||
|
public void testTerminal() {
|
||||||
|
FlowableManager manager = flowableManager();
|
||||||
|
FlowableNode node1 = createManualNode();
|
||||||
|
manager.create(node1);
|
||||||
|
|
||||||
|
String instanceId = manager.start(node1.getNodeId());
|
||||||
|
|
||||||
|
Assertions.assertEquals(FlowableInstance.Status.RUNNING, manager.getInstance(instanceId).getStatus());
|
||||||
|
|
||||||
|
manager.terminal(instanceId, "d896b642-a1d8-499c-92e7-bed63581f2f8");
|
||||||
|
Assertions.assertEquals(FlowableInstance.Status.TERMINAL, manager.getInstance(instanceId).getStatus());
|
||||||
|
Assertions.assertEquals(1, manager.listHistories(instanceId).size());
|
||||||
|
Assertions.assertEquals(FlowableAction.TERMINAL, manager.listHistories(instanceId).get(0).getAction());
|
||||||
|
Assertions.assertEquals("d896b642-a1d8-499c-92e7-bed63581f2f8", manager.listHistories(instanceId).get(0).getComment());
|
||||||
|
|
||||||
|
Assertions.assertThrows(IllegalArgumentException.class, () -> manager.approve(instanceId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(5)
|
||||||
|
public void testSuspend() {
|
||||||
|
FlowableManager manager = flowableManager();
|
||||||
|
FlowableNode node = FlowableNode.builder()
|
||||||
|
.nodeId("50342cb7-2029-4159-bd4c-cbfa4aebe474")
|
||||||
|
.name("50342cb7-2029-4159-bd4c-cbfa4aebe474")
|
||||||
|
.description("50342cb7-2029-4159-bd4c-cbfa4aebe474")
|
||||||
|
.handler(TwoApproveHandler.class.getName())
|
||||||
|
.build();
|
||||||
|
manager.create(node);
|
||||||
|
|
||||||
|
String instanceId = manager.start(node.getNodeId());
|
||||||
|
Assertions.assertEquals(FlowableInstance.Status.RUNNING, manager.getInstance(instanceId).getStatus());
|
||||||
|
manager.approve(instanceId);
|
||||||
|
Assertions.assertEquals(FlowableInstance.Status.RUNNING, manager.getInstance(instanceId).getStatus());
|
||||||
|
manager.approve(instanceId);
|
||||||
|
Assertions.assertEquals(FlowableInstance.Status.COMPLETED, manager.getInstance(instanceId).getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract Class<? extends FlowableHandler> getHandler();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(6)
|
||||||
|
public void testAutomaticNode() {
|
||||||
|
FlowableManager manager = flowableManager();
|
||||||
|
FlowableNode node = FlowableNode.builder()
|
||||||
|
.nodeId("5d60dab0-7691-4543-b753-af7ac02cb7ec")
|
||||||
|
.name("5d60dab0-7691-4543-b753-af7ac02cb7ec")
|
||||||
|
.description("5d60dab0-7691-4543-b753-af7ac02cb7ec")
|
||||||
|
.type(FlowableNode.Type.AUTOMATIC)
|
||||||
|
.handler(getHandler().getName())
|
||||||
|
.build();
|
||||||
|
manager.create(node);
|
||||||
|
String instanceId = manager.start(node.getNodeId());
|
||||||
|
Assertions.assertEquals(FlowableInstance.Status.COMPLETED, manager.getInstance(instanceId).getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(7)
|
||||||
|
public void testNodeContext() {
|
||||||
|
FlowableManager manager = flowableManager();
|
||||||
|
FlowableNode node1 = createManualNode(
|
||||||
|
"a3f3f055-aa0e-49ed-9bc5-d0c04f11017e",
|
||||||
|
MapHelper.of(
|
||||||
|
FlowableAction.APPROVE, "88a4ef5b-9cca-4e89-8232-24b6e9e94f4a"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
FlowableNode node2 = createManualNode("88a4ef5b-9cca-4e89-8232-24b6e9e94f4a");
|
||||||
|
manager.create(node1, node2);
|
||||||
|
|
||||||
|
String instanceId = manager.start(node1.getNodeId(), MapHelper.of("name", "lanyuanxiaoyao"));
|
||||||
|
Assertions.assertEquals(FlowableInstance.Status.RUNNING, manager.getInstance(instanceId).getStatus());
|
||||||
|
Assertions.assertEquals("lanyuanxiaoyao", manager.getInstance(instanceId).getMetadata().get("name"));
|
||||||
|
|
||||||
|
manager.approve(instanceId);
|
||||||
|
Assertions.assertEquals(FlowableInstance.Status.RUNNING, manager.getInstance(instanceId).getStatus());
|
||||||
|
Assertions.assertEquals("88a4ef5b-9cca-4e89-8232-24b6e9e94f4a", manager.getInstance(instanceId).getCurrentNodeId());
|
||||||
|
Assertions.assertEquals("lanyuanxiaoyao", manager.getInstance(instanceId).getMetadata().get("name"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(8)
|
||||||
|
public void testAccessor() {
|
||||||
|
FlowableManager manager = flowableManager();
|
||||||
|
FlowableNode node = FlowableNode.builder()
|
||||||
|
.nodeId("a3f3f055-aa0e-49ed-9bc5-d0c04f11017e")
|
||||||
|
.name("a3f3f055-aa0e-49ed-9bc5-d0c04f11017e")
|
||||||
|
.description("a3f3f055-aa0e-49ed-9bc5-d0c04f11017e")
|
||||||
|
.type(FlowableNode.Type.MANUAL)
|
||||||
|
.accessor(RoleCheckAccessor.class.getName())
|
||||||
|
.build();
|
||||||
|
manager.create(node);
|
||||||
|
String instanceId = manager.start(node.getNodeId());
|
||||||
|
Assertions.assertEquals(FlowableInstance.Status.RUNNING, manager.getInstance(instanceId).getStatus());
|
||||||
|
Assertions.assertThrows(IllegalArgumentException.class, () -> manager.approve(instanceId));
|
||||||
|
|
||||||
|
manager.approve(instanceId, MapHelper.of("role", "admin"));
|
||||||
|
Assertions.assertEquals(FlowableInstance.Status.COMPLETED, manager.getInstance(instanceId).getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract Class<? extends FlowableListener> getListenerClass();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(9)
|
||||||
|
public void testListener() {
|
||||||
|
FlowableManager manager = flowableManager();
|
||||||
|
FlowableNode node = FlowableNode.builder()
|
||||||
|
.nodeId("1c81366f-4102-4a9e-abb2-1dbcb09062e9")
|
||||||
|
.name("1c81366f-4102-4a9e-abb2-1dbcb09062e9")
|
||||||
|
.description("1c81366f-4102-4a9e-abb2-1dbcb09062e9")
|
||||||
|
.type(FlowableNode.Type.MANUAL)
|
||||||
|
.listeners(ListHelper.of(
|
||||||
|
getListenerClass().getName()
|
||||||
|
))
|
||||||
|
.build();
|
||||||
|
manager.create(node);
|
||||||
|
String instanceId = manager.start(node.getNodeId());
|
||||||
|
manager.approve(instanceId);
|
||||||
|
Assertions.assertEquals(FlowableInstance.Status.COMPLETED, manager.getInstance(instanceId).getStatus());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.lanyuanxiaoyao.flowable.test.accessor;
|
||||||
|
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableAccessor;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableAction;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableInstance;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableMetadata;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableNode;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lanyuanxiaoyao
|
||||||
|
* @version 20250103
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class RoleCheckAccessor implements FlowableAccessor {
|
||||||
|
@Override
|
||||||
|
public boolean access(FlowableInstance instance, FlowableNode node, FlowableAction action) {
|
||||||
|
FlowableMetadata metadata = instance.getMetadata();
|
||||||
|
return metadata.containsKey("role");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.lanyuanxiaoyao.flowable.test.handler;
|
||||||
|
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.manager.FlowableConfiguration;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableAction;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableHandler;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableInstance;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableMetadata;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableNode;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同意两次才能通过的自动节点
|
||||||
|
*
|
||||||
|
* @author lanyuanxiaoyao
|
||||||
|
* @version 20250103
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class TwoApproveHandler implements FlowableHandler {
|
||||||
|
private static final String KEY = "approve-times";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FlowableAction handle(FlowableConfiguration configuration, FlowableInstance instance, FlowableNode node, FlowableAction action) {
|
||||||
|
log.info("{}", instance.getMetadata());
|
||||||
|
FlowableMetadata metadata = instance.getMetadata();
|
||||||
|
int approveCount = metadata.getIntOrDefault(KEY, 0);
|
||||||
|
if (approveCount + 1 > 1) {
|
||||||
|
return FlowableAction.APPROVE;
|
||||||
|
}
|
||||||
|
metadata.put(KEY, approveCount + 1);
|
||||||
|
return FlowableAction.SUSPEND;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.lanyuanxiaoyao.flowable.core;
|
package com.lanyuanxiaoyao.flowable.test;
|
||||||
|
|
||||||
import com.lanyuanxiaoyao.flowable.core.helper.ListHelper;
|
import com.lanyuanxiaoyao.flowable.core.helper.ListHelper;
|
||||||
import com.lanyuanxiaoyao.flowable.core.helper.StringHelper;
|
import com.lanyuanxiaoyao.flowable.core.helper.StringHelper;
|
||||||
@@ -22,6 +22,11 @@ public class InMemoryFlowableRepository implements FlowableRepository {
|
|||||||
private static final Map<String, FlowableInstance> instances = new HashMap<>();
|
private static final Map<String, FlowableInstance> instances = new HashMap<>();
|
||||||
private static final Map<String, List<FlowableHistory>> histories = new HashMap<>();
|
private static final Map<String, List<FlowableHistory>> histories = new HashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean existsNode(String nodeId) {
|
||||||
|
return nodes.containsKey(nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void saveNode(FlowableNode node) {
|
public void saveNode(FlowableNode node) {
|
||||||
nodes.put(node.getNodeId(), node);
|
nodes.put(node.getNodeId(), node);
|
||||||
@@ -44,6 +49,11 @@ public class InMemoryFlowableRepository implements FlowableRepository {
|
|||||||
return new ArrayList<>(nodes.values());
|
return new ArrayList<>(nodes.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean existsInstance(String instanceId) {
|
||||||
|
return instances.containsKey(instanceId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void saveInstance(FlowableInstance instance) {
|
public void saveInstance(FlowableInstance instance) {
|
||||||
instances.put(instance.getInstanceId(), instance);
|
instances.put(instance.getInstanceId(), instance);
|
||||||
@@ -59,6 +69,11 @@ public class InMemoryFlowableRepository implements FlowableRepository {
|
|||||||
return new ArrayList<>(instances.values());
|
return new ArrayList<>(instances.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean existsHistory(String historyId) {
|
||||||
|
return histories.containsKey(historyId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void saveHistory(FlowableHistory history) {
|
public void saveHistory(FlowableHistory history) {
|
||||||
String instanceId = history.getInstanceId();
|
String instanceId = history.getInstanceId();
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.lanyuanxiaoyao.flowable.test;
|
||||||
|
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.manager.FlowableConfiguration;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableAction;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableHandler;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableInstance;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableNode;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lanyuanxiaoyao
|
||||||
|
* @version 20250102
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class SimpleAutoHandler implements FlowableHandler {
|
||||||
|
@Override
|
||||||
|
public FlowableAction handle(FlowableConfiguration configuration, FlowableInstance instance, FlowableNode node, FlowableAction action) {
|
||||||
|
log.info("Simple handler initial");
|
||||||
|
return FlowableAction.APPROVE;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.lanyuanxiaoyao.flowable.core;
|
package com.lanyuanxiaoyao.flowable.test;
|
||||||
|
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.manager.FlowableConfiguration;
|
||||||
import com.lanyuanxiaoyao.flowable.core.manager.FlowableManager;
|
import com.lanyuanxiaoyao.flowable.core.manager.FlowableManager;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
@@ -10,7 +11,7 @@ import lombok.SneakyThrows;
|
|||||||
*/
|
*/
|
||||||
public class SimpleFlowableManager extends FlowableManager {
|
public class SimpleFlowableManager extends FlowableManager {
|
||||||
public SimpleFlowableManager() {
|
public SimpleFlowableManager() {
|
||||||
super(new InMemoryFlowableRepository(), () -> UUID.randomUUID().toString());
|
super(FlowableConfiguration.builder().build(), new InMemoryFlowableRepository());
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@@ -18,4 +19,9 @@ public class SimpleFlowableManager extends FlowableManager {
|
|||||||
protected <T> T createBean(String classpath) {
|
protected <T> T createBean(String classpath) {
|
||||||
return (T) Class.forName(classpath).newInstance();
|
return (T) Class.forName(classpath).newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String createId() {
|
||||||
|
return UUID.randomUUID().toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.lanyuanxiaoyao.flowable.test;
|
||||||
|
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableAction;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableInstance;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableListener;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableNode;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lanyuanxiaoyao
|
||||||
|
* @version 20250106
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class SimpleListener implements FlowableListener {
|
||||||
|
@Override
|
||||||
|
public void onActionStart(FlowableInstance instance, FlowableNode node, FlowableAction action) {
|
||||||
|
log.info("onActionStart");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAction(FlowableInstance instance, FlowableNode node, FlowableAction action) {
|
||||||
|
log.info("onAction");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActionComplete(FlowableInstance instance, FlowableNode node, FlowableAction action) {
|
||||||
|
log.info("onActionComplete");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.lanyuanxiaoyao.flowable.test;
|
||||||
|
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.manager.FlowableManager;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableHandler;
|
||||||
|
import com.lanyuanxiaoyao.flowable.core.model.FlowableListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lanyuanxiaoyao
|
||||||
|
* @version 20250102
|
||||||
|
*/
|
||||||
|
public class TestSimpleFlowableManager extends TestFlowableManager {
|
||||||
|
@Override
|
||||||
|
protected FlowableManager flowableManager() {
|
||||||
|
return new SimpleFlowableManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class<? extends FlowableHandler> getHandler() {
|
||||||
|
return SimpleAutoHandler.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class<? extends FlowableListener> getListenerClass() {
|
||||||
|
return SimpleListener.class;
|
||||||
|
}
|
||||||
|
}
|
||||||
42
pom.xml
42
pom.xml
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
<module>flowable-core</module>
|
<module>flowable-core</module>
|
||||||
|
<module>flowable-example</module>
|
||||||
<module>adapter/flowable-spring-boot-jpa-starter</module>
|
<module>adapter/flowable-spring-boot-jpa-starter</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
@@ -20,14 +21,31 @@
|
|||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
|
||||||
<spring-boot.version>2.6.15</spring-boot.version>
|
<spring-boot.version>2.6.15</spring-boot.version>
|
||||||
|
<hutool.version>5.8.32</hutool.version>
|
||||||
|
<junit.version>5.8.2</junit.version>
|
||||||
|
<slf4j.version>1.7.36</slf4j.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>1.18.34</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.lanyuanxiaoyao</groupId>
|
<groupId>com.lanyuanxiaoyao</groupId>
|
||||||
<artifactId>flowable-core</artifactId>
|
<artifactId>flowable-core</artifactId>
|
||||||
<version>1.0.0-SNAPSHOT</version>
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.lanyuanxiaoyao</groupId>
|
||||||
|
<artifactId>flowable-example</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
@@ -39,7 +57,27 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
<artifactId>hutool-core</artifactId>
|
<artifactId>hutool-core</artifactId>
|
||||||
<version>5.8.32</version>
|
<version>${hutool.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-api</artifactId>
|
||||||
|
<version>${junit.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter</artifactId>
|
||||||
|
<version>${junit.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
<version>${slf4j.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-simple</artifactId>
|
||||||
|
<version>${slf4j.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|||||||
Reference in New Issue
Block a user