diff --git a/.idea/encodings.xml b/.idea/encodings.xml index aa00ffa..e13e8af 100644 --- a/.idea/encodings.xml +++ b/.idea/encodings.xml @@ -1,6 +1,10 @@ + + + + diff --git a/adapter/flowable-spring-boot-jpa-starter/pom.xml b/adapter/flowable-spring-boot-jpa-starter/pom.xml new file mode 100644 index 0000000..3ee5318 --- /dev/null +++ b/adapter/flowable-spring-boot-jpa-starter/pom.xml @@ -0,0 +1,60 @@ + + + + com.lanyuanxiaoyao + flowable + 1.0.0-SNAPSHOT + + + 4.0.0 + flowable-spring-boot-jpa-starter + + + + + + + com.lanyuanxiaoyao + flowable-core + + + org.springframework.boot + spring-boot-starter-logging + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.boot + spring-boot-starter-test + test + + + com.h2database + h2 + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + package + + repackage + + + + + + + + \ No newline at end of file diff --git a/adapter/flowable-spring-boot-jpa-starter/src/main/java/com/lanyuanxiaoyao/flowable/jpa/SpringFlowableManager.java b/adapter/flowable-spring-boot-jpa-starter/src/main/java/com/lanyuanxiaoyao/flowable/jpa/SpringFlowableManager.java new file mode 100644 index 0000000..6fc8453 --- /dev/null +++ b/adapter/flowable-spring-boot-jpa-starter/src/main/java/com/lanyuanxiaoyao/flowable/jpa/SpringFlowableManager.java @@ -0,0 +1,17 @@ +package com.lanyuanxiaoyao.flowable.jpa; + +import com.lanyuanxiaoyao.flowable.core.manager.FlowableManager; +import com.lanyuanxiaoyao.flowable.core.repository.FlowableRepository; +import java.util.UUID; +import org.springframework.stereotype.Service; + +/** + * @author lanyuanxiaoyao + * @version 20241231 + */ +@Service +public class SpringFlowableManager extends FlowableManager { + public SpringFlowableManager(FlowableRepository flowableRepository) { + super(flowableRepository, () -> UUID.randomUUID().toString()); + } +} diff --git a/adapter/flowable-spring-boot-jpa-starter/src/main/java/com/lanyuanxiaoyao/flowable/jpa/SpringJpaFlowableRepository.java b/adapter/flowable-spring-boot-jpa-starter/src/main/java/com/lanyuanxiaoyao/flowable/jpa/SpringJpaFlowableRepository.java new file mode 100644 index 0000000..31e3f39 --- /dev/null +++ b/adapter/flowable-spring-boot-jpa-starter/src/main/java/com/lanyuanxiaoyao/flowable/jpa/SpringJpaFlowableRepository.java @@ -0,0 +1,109 @@ +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 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 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 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 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); + } +} diff --git a/adapter/flowable-spring-boot-jpa-starter/src/main/java/com/lanyuanxiaoyao/flowable/jpa/entity/JpaFlowableHistory.java b/adapter/flowable-spring-boot-jpa-starter/src/main/java/com/lanyuanxiaoyao/flowable/jpa/entity/JpaFlowableHistory.java new file mode 100644 index 0000000..452dd0a --- /dev/null +++ b/adapter/flowable-spring-boot-jpa-starter/src/main/java/com/lanyuanxiaoyao/flowable/jpa/entity/JpaFlowableHistory.java @@ -0,0 +1,54 @@ +package com.lanyuanxiaoyao.flowable.jpa.entity; + +import com.lanyuanxiaoyao.flowable.core.model.FlowableAction; +import com.lanyuanxiaoyao.flowable.core.model.FlowableHistory; +import java.time.LocalDateTime; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EntityListeners; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Id; +import javax.persistence.Table; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import org.hibernate.annotations.DynamicUpdate; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +/** + * @author lanyuanxiaoyao + * @version 20250102 + */ +@NoArgsConstructor +@ToString +@Getter +@Setter +@Entity +@Table(name = "flowable_history") +@DynamicUpdate +@EntityListeners(AuditingEntityListener.class) +public class JpaFlowableHistory { + @Id + private String historyId; + @Column(nullable = false) + private String instanceId; + @Enumerated(EnumType.STRING) + private FlowableAction action; + private String comment; + @CreatedDate + private LocalDateTime createdTime; + + public JpaFlowableHistory(FlowableHistory history) { + this.historyId = history.getHistoryId(); + this.instanceId = history.getInstanceId(); + this.action = history.getAction(); + this.comment = history.getComment(); + } + + public FlowableHistory toFlowableHistory() { + return new FlowableHistory(historyId, instanceId, action, comment, createdTime); + } +} diff --git a/adapter/flowable-spring-boot-jpa-starter/src/main/java/com/lanyuanxiaoyao/flowable/jpa/entity/JpaFlowableInstance.java b/adapter/flowable-spring-boot-jpa-starter/src/main/java/com/lanyuanxiaoyao/flowable/jpa/entity/JpaFlowableInstance.java new file mode 100644 index 0000000..a56fe7e --- /dev/null +++ b/adapter/flowable-spring-boot-jpa-starter/src/main/java/com/lanyuanxiaoyao/flowable/jpa/entity/JpaFlowableInstance.java @@ -0,0 +1,94 @@ +package com.lanyuanxiaoyao.flowable.jpa.entity; + +import com.lanyuanxiaoyao.flowable.core.model.FlowableInstance; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.time.LocalDateTime; +import java.util.Map; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EntityListeners; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Id; +import javax.persistence.Lob; +import javax.persistence.Table; +import lombok.Cleanup; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.SneakyThrows; +import lombok.ToString; +import org.hibernate.annotations.DynamicUpdate; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +/** + * @author lanyuanxiaoyao + * @version 20250102 + */ +@NoArgsConstructor +@ToString +@Getter +@Setter +@Entity +@Table(name = "flowable_instance") +@DynamicUpdate +@EntityListeners(AuditingEntityListener.class) +public class JpaFlowableInstance { + @Id + private String instanceId; + @Lob + private byte[] metadata; + @Column(nullable = false) + private String currentNodeId; + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private FlowableInstance.Status status = FlowableInstance.Status.RUNNING; + + @CreatedDate + private LocalDateTime createdTime; + @LastModifiedDate + private LocalDateTime updatedTime; + + @SneakyThrows + public JpaFlowableInstance(FlowableInstance instance) { + this.instanceId = instance.getInstanceId(); + this.metadata = objectToBytes(instance.getMetadata()); + this.currentNodeId = instance.getCurrentNodeId(); + this.status = instance.getStatus(); + } + + private static byte[] objectToBytes(Object object) throws IOException { + @Cleanup + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + @Cleanup + ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); + objectOutputStream.writeObject(object); + return byteArrayOutputStream.toByteArray(); + } + + private static Object bytesToObject(byte[] bytes) throws IOException, ClassNotFoundException { + @Cleanup + java.io.ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); + @Cleanup + java.io.ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); + return objectInputStream.readObject(); + } + + @SneakyThrows + public FlowableInstance toFlowableInstance() { + return new FlowableInstance( + instanceId, + currentNodeId, + (Map) bytesToObject(this.metadata), + status, + createdTime, + updatedTime + ); + } +} diff --git a/adapter/flowable-spring-boot-jpa-starter/src/main/java/com/lanyuanxiaoyao/flowable/jpa/entity/JpaFlowableNode.java b/adapter/flowable-spring-boot-jpa-starter/src/main/java/com/lanyuanxiaoyao/flowable/jpa/entity/JpaFlowableNode.java new file mode 100644 index 0000000..4d56d1d --- /dev/null +++ b/adapter/flowable-spring-boot-jpa-starter/src/main/java/com/lanyuanxiaoyao/flowable/jpa/entity/JpaFlowableNode.java @@ -0,0 +1,72 @@ +package com.lanyuanxiaoyao.flowable.jpa.entity; + +import com.lanyuanxiaoyao.flowable.core.model.FlowableAction; +import com.lanyuanxiaoyao.flowable.core.model.FlowableNode; +import java.time.LocalDateTime; +import java.util.Map; +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.ConstraintMode; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.EntityListeners; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.ForeignKey; +import javax.persistence.Id; +import javax.persistence.MapKeyColumn; +import javax.persistence.Table; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import org.hibernate.annotations.DynamicUpdate; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +/** + * @author lanyuanxiaoyao + * @version 20241231 + */ +@NoArgsConstructor +@ToString +@Getter +@Setter +@Entity +@Table(name = "flowable_node") +@DynamicUpdate +@EntityListeners(AuditingEntityListener.class) +public class JpaFlowableNode { + @Id + private String nodeId; + @Column(nullable = false) + private String name; + private String description; + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private FlowableNode.RunType runType; + @ElementCollection(fetch = javax.persistence.FetchType.EAGER) + @MapKeyColumn(name = "action") + @Column(name = "nodeId") + @CollectionTable(foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT)) + private Map nextNodes; + @CreatedDate + private LocalDateTime createdTime; + @LastModifiedDate + private LocalDateTime updateTime; + + public JpaFlowableNode(FlowableNode node) { + this.nodeId = node.getNodeId(); + this.name = node.getName(); + this.description = node.getDescription(); + this.runType = node.getRunType(); + this.nextNodes = node.getNextNodes(); + } + + public FlowableNode toFlowableNode() { + FlowableNode node = new FlowableNode(nodeId, name, description, runType, nextNodes, null, createdTime); + node.setUpdatedTime(updateTime); + return node; + } +} diff --git a/adapter/flowable-spring-boot-jpa-starter/src/main/java/com/lanyuanxiaoyao/flowable/jpa/repository/JpaFlowableHistoryRepository.java b/adapter/flowable-spring-boot-jpa-starter/src/main/java/com/lanyuanxiaoyao/flowable/jpa/repository/JpaFlowableHistoryRepository.java new file mode 100644 index 0000000..408cac3 --- /dev/null +++ b/adapter/flowable-spring-boot-jpa-starter/src/main/java/com/lanyuanxiaoyao/flowable/jpa/repository/JpaFlowableHistoryRepository.java @@ -0,0 +1,15 @@ +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 { + List findAllByInstanceId(String instanceId); +} diff --git a/adapter/flowable-spring-boot-jpa-starter/src/main/java/com/lanyuanxiaoyao/flowable/jpa/repository/JpaFlowableInstanceRepository.java b/adapter/flowable-spring-boot-jpa-starter/src/main/java/com/lanyuanxiaoyao/flowable/jpa/repository/JpaFlowableInstanceRepository.java new file mode 100644 index 0000000..8c39442 --- /dev/null +++ b/adapter/flowable-spring-boot-jpa-starter/src/main/java/com/lanyuanxiaoyao/flowable/jpa/repository/JpaFlowableInstanceRepository.java @@ -0,0 +1,13 @@ +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 { +} diff --git a/adapter/flowable-spring-boot-jpa-starter/src/main/java/com/lanyuanxiaoyao/flowable/jpa/repository/JpaFlowableNodeRepository.java b/adapter/flowable-spring-boot-jpa-starter/src/main/java/com/lanyuanxiaoyao/flowable/jpa/repository/JpaFlowableNodeRepository.java new file mode 100644 index 0000000..606d759 --- /dev/null +++ b/adapter/flowable-spring-boot-jpa-starter/src/main/java/com/lanyuanxiaoyao/flowable/jpa/repository/JpaFlowableNodeRepository.java @@ -0,0 +1,13 @@ +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 { +} diff --git a/adapter/flowable-spring-boot-jpa-starter/src/test/java/com/lanyuanxiaoyao/flowable/jpa/SpringFlowableApplication.java b/adapter/flowable-spring-boot-jpa-starter/src/test/java/com/lanyuanxiaoyao/flowable/jpa/SpringFlowableApplication.java new file mode 100644 index 0000000..481ee24 --- /dev/null +++ b/adapter/flowable-spring-boot-jpa-starter/src/test/java/com/lanyuanxiaoyao/flowable/jpa/SpringFlowableApplication.java @@ -0,0 +1,17 @@ +package com.lanyuanxiaoyao.flowable.jpa; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; + +/** + * @author lanyuanxiaoyao + * @version 20250102 + */ +@SpringBootApplication +@EnableJpaAuditing +public class SpringFlowableApplication { + public static void main(String[] args) { + SpringApplication.run(SpringFlowableApplication.class, args); + } +} diff --git a/adapter/flowable-spring-boot-jpa-starter/src/test/java/com/lanyuanxiaoyao/flowable/jpa/TestSpringFlowableManager.java b/adapter/flowable-spring-boot-jpa-starter/src/test/java/com/lanyuanxiaoyao/flowable/jpa/TestSpringFlowableManager.java new file mode 100644 index 0000000..6528e91 --- /dev/null +++ b/adapter/flowable-spring-boot-jpa-starter/src/test/java/com/lanyuanxiaoyao/flowable/jpa/TestSpringFlowableManager.java @@ -0,0 +1,21 @@ +package com.lanyuanxiaoyao.flowable.jpa; + +import com.lanyuanxiaoyao.flowable.core.manager.FlowableManager; +import com.lanyuanxiaoyao.flowable.core.test.TestFlowableManager; +import javax.annotation.Resource; +import org.springframework.boot.test.context.SpringBootTest; + +/** + * @author lanyuanxiaoyao + * @version 20250102 + */ +@SpringBootTest +public class TestSpringFlowableManager extends TestFlowableManager { + @Resource + private SpringFlowableManager flowableManager; + + @Override + protected FlowableManager flowableManager() { + return flowableManager; + } +} diff --git a/adapter/flowable-spring-boot-jpa-starter/src/test/resources/application.yml b/adapter/flowable-spring-boot-jpa-starter/src/test/resources/application.yml new file mode 100644 index 0000000..644a03e --- /dev/null +++ b/adapter/flowable-spring-boot-jpa-starter/src/test/resources/application.yml @@ -0,0 +1,18 @@ +spring: + datasource: + url: jdbc:h2:mem:testdb + username: flowable + password: flowable + driver-class-name: org.h2.Driver + jpa: + show-sql: true + hibernate: + ddl-auto: create +logging: + level: + root: info + org: + hibernate: + orm: + jdbc: + bind: trace \ No newline at end of file diff --git a/flowable-core/pom.xml b/flowable-core/pom.xml new file mode 100644 index 0000000..58e22bd --- /dev/null +++ b/flowable-core/pom.xml @@ -0,0 +1,46 @@ + + + + com.lanyuanxiaoyao + flowable + 1.0.0-SNAPSHOT + + + 4.0.0 + flowable-core + + + + org.projectlombok + lombok + 1.18.34 + compile + + + org.slf4j + slf4j-api + 2.0.16 + + + org.junit.jupiter + junit-jupiter-api + 5.11.4 + + + + org.slf4j + slf4j-simple + 2.0.16 + test + + + org.junit.jupiter + junit-jupiter + 5.11.4 + test + + + + \ No newline at end of file diff --git a/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/helper/ListHelper.java b/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/helper/ListHelper.java new file mode 100644 index 0000000..34aa537 --- /dev/null +++ b/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/helper/ListHelper.java @@ -0,0 +1,24 @@ +package com.lanyuanxiaoyao.flowable.core.helper; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import lombok.NoArgsConstructor; + +/** + * List工具 + * + * @author lanyuanxiaoyao + * @version 20241231 + */ +@NoArgsConstructor(access = lombok.AccessLevel.PRIVATE) +public class ListHelper { + @SafeVarargs + public static List of(T... entries) { + return new ArrayList<>(Arrays.asList(entries)); + } + + public static List empty() { + return new ArrayList<>(); + } +} diff --git a/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/helper/MapHelper.java b/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/helper/MapHelper.java new file mode 100644 index 0000000..d4c942c --- /dev/null +++ b/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/helper/MapHelper.java @@ -0,0 +1,30 @@ +package com.lanyuanxiaoyao.flowable.core.helper; + +import java.util.HashMap; +import java.util.Map; +import lombok.NoArgsConstructor; + +/** + * Map工具 + * + * @author lanyuanxiaoyao + * @version 20241231 + */ +@NoArgsConstructor(access = lombok.AccessLevel.PRIVATE) +public class MapHelper { + @SuppressWarnings("unchecked") + public static Map of(Object... entries) { + if (entries.length % 2 != 0) { + throw new IllegalArgumentException("参数必须为偶数个"); + } + Map map = new HashMap<>(entries.length / 2 + 1); + for (int index = 0; index < entries.length; index += 2) { + map.put((K) entries[index], (V) entries[index + 1]); + } + return map; + } + + public static Map empty() { + return new HashMap<>(0); + } +} diff --git a/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/helper/StringHelper.java b/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/helper/StringHelper.java new file mode 100644 index 0000000..3bc94d1 --- /dev/null +++ b/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/helper/StringHelper.java @@ -0,0 +1,20 @@ +package com.lanyuanxiaoyao.flowable.core.helper; + +import lombok.NoArgsConstructor; + +/** + * String工具 + * + * @author lanyuanxiaoyao + * @version 20241231 + */ +@NoArgsConstructor(access = lombok.AccessLevel.PRIVATE) +public class StringHelper { + public static Boolean isBlank(String text) { + return text == null || text.trim().isEmpty(); + } + + public static Boolean equals(String text1, String text2) { + return text1 != null && text1.equals(text2); + } +} diff --git a/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/manager/FlowableManager.java b/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/manager/FlowableManager.java new file mode 100644 index 0000000..3b3fb5d --- /dev/null +++ b/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/manager/FlowableManager.java @@ -0,0 +1,156 @@ +package com.lanyuanxiaoyao.flowable.core.manager; + +import com.lanyuanxiaoyao.flowable.core.helper.ListHelper; +import com.lanyuanxiaoyao.flowable.core.helper.MapHelper; +import com.lanyuanxiaoyao.flowable.core.helper.StringHelper; +import com.lanyuanxiaoyao.flowable.core.model.FlowableHistory; +import com.lanyuanxiaoyao.flowable.core.model.FlowableInstance; +import com.lanyuanxiaoyao.flowable.core.model.FlowableAction; +import com.lanyuanxiaoyao.flowable.core.model.FlowableListener; +import com.lanyuanxiaoyao.flowable.core.model.FlowableNode; +import com.lanyuanxiaoyao.flowable.core.repository.FlowableRepository; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Consumer; +import lombok.extern.slf4j.Slf4j; + +/** + * 流程服务 + * + * @author lanyuanxiaoyao + * @version 20241231 + */ +@Slf4j +public abstract class FlowableManager { + private final FlowableRepository flowableRepository; + private final IdGenerator idGenerator; + + public FlowableManager(FlowableRepository flowableRepository, IdGenerator idGenerator) { + this.flowableRepository = flowableRepository; + this.idGenerator = idGenerator; + } + + public List listNodes() { + return flowableRepository.listNodes(); + } + + public FlowableNode getNode(String nodeId) { + return flowableRepository.getNode(nodeId); + } + + public List listInstances() { + return flowableRepository.listInstances(); + } + + public FlowableInstance getInstance(String instantId) { + return flowableRepository.getInstance(instantId); + } + + public List listHistories(String instanceId) { + return flowableRepository.listHistories(instanceId); + } + + public FlowableHistory getHistory(String historyId) { + return flowableRepository.getHistory(historyId); + } + + public void create(FlowableNode... node) { + flowableRepository.saveNode(ListHelper.of(node)); + } + + public String start(String nodeId) { + return start(nodeId, MapHelper.empty()); + } + + public String start(String nodeId, Map metadata) { + FlowableNode node = flowableRepository.getNode(nodeId); + FlowableInstance instance = new FlowableInstance(idGenerator.createId(), node.getNodeId(), metadata); + flowableRepository.saveInstance(instance); + + if (FlowableNode.RunType.AUTOMATIC.equals(node.getRunType())) { + action(instance, node, FlowableAction.APPROVE, "系统审批通过", MapHelper.empty()); + } + return instance.getInstanceId(); + } + + public void approve(String instanceId) { + approve(instanceId, "审批通过"); + } + + public void approve(String instanceId, String comment) { + action(instanceId, FlowableAction.APPROVE, comment); + } + + public void reject(String instanceId) { + reject(instanceId, "审批不通过"); + } + + public void reject(String instanceId, String comment) { + action(instanceId, FlowableAction.REJECT, comment); + } + + public void terminal(String instanceId) { + terminal(instanceId, "流程被终止"); + } + + public void terminal(String instanceId, String comment) { + action(instanceId, FlowableAction.TERMINAL, comment); + } + + public void action(String instanceId, FlowableAction action, String comment) { + action(instanceId, action, comment, MapHelper.empty()); + } + + private void action(String instanceId, FlowableAction action, String comment, Map metadata) { + FlowableInstance instance = flowableRepository.getInstance(instanceId); + FlowableNode node = flowableRepository.getNode(instance.getCurrentNodeId()); + action(instance, node, action, comment, metadata); + } + + private void action(FlowableInstance instance, FlowableNode node, FlowableAction action, String comment, Map metadata) { + if (FlowableInstance.Status.COMPLETED.equals(instance.getStatus()) || FlowableInstance.Status.ERROR.equals(instance.getStatus())) { + throw new IllegalArgumentException("ID为" + instance.getInstanceId() + "的流程已结束,无法操作"); + } + if (FlowableAction.TERMINAL.equals(action)) { + saveInstance(instance, FlowableInstance.Status.ERROR, metadata, action, comment); + return; + } + if (Objects.isNull(node.getNextNodes()) + || !node.getNextNodes().containsKey(action) + || StringHelper.isBlank(node.getNextNodes().get(action))) { + saveInstance(instance, FlowableInstance.Status.COMPLETED, metadata, action, comment); + return; + } + String nextNodeId = node.getNextNodes().get(action); + FlowableNode nextNode = flowableRepository.getNode(nextNodeId); + instance.setCurrentNodeId(nextNode.getNodeId()); + saveInstance(instance, FlowableInstance.Status.RUNNING, metadata, action, comment); + + if (FlowableNode.RunType.AUTOMATIC.equals(nextNode.getRunType())) { + action(instance, node, FlowableAction.APPROVE, "系统审批通过", metadata); + } + } + + private void saveInstance(FlowableInstance instance, FlowableInstance.Status status, Map metadata, FlowableAction action, String comment) { + instance.setStatus(status); + if (Objects.nonNull(metadata)) { + instance.addMetadata(metadata); + } + instance.setUpdatedTime(LocalDateTime.now()); + + FlowableHistory history = new FlowableHistory(idGenerator.createId(), instance.getInstanceId(), action, comment); + flowableRepository.saveInstanceAndHistory(instance, history); + } + + private void callListeners(List listeners, Consumer handler) { + for (FlowableListener listener : listeners) { + handler.accept(listener); + } + } + + public interface IdGenerator { + String createId(); + } +} diff --git a/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/model/FlowableAccessor.java b/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/model/FlowableAccessor.java new file mode 100644 index 0000000..ebb9304 --- /dev/null +++ b/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/model/FlowableAccessor.java @@ -0,0 +1,11 @@ +package com.lanyuanxiaoyao.flowable.core.model; + +/** + * 权限校验 + * + * @author lanyuanxiaoyao + * @version 20241231 + */ +public interface FlowableAccessor { + void access(String accessor); +} diff --git a/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/model/FlowableAction.java b/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/model/FlowableAction.java new file mode 100644 index 0000000..b669826 --- /dev/null +++ b/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/model/FlowableAction.java @@ -0,0 +1,13 @@ +package com.lanyuanxiaoyao.flowable.core.model; + +/** + * 节点操作 + * + * @author lanyuanxiaoyao + * @version 20241231 + */ +public enum FlowableAction { + APPROVE, + REJECT, + TERMINAL, +} diff --git a/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/model/FlowableHistory.java b/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/model/FlowableHistory.java new file mode 100644 index 0000000..16d6014 --- /dev/null +++ b/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/model/FlowableHistory.java @@ -0,0 +1,29 @@ +package com.lanyuanxiaoyao.flowable.core.model; + +import java.time.LocalDateTime; +import lombok.Data; + +/** + * @author lanyuanxiaoyao + * @version 20241231 + */ +@Data +public class FlowableHistory { + private final String historyId; + private final String instanceId; + private final FlowableAction action; + private final String comment; + private final LocalDateTime createdTime; + + public FlowableHistory(String historyId, String instanceId, FlowableAction action, String comment) { + 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; + } +} diff --git a/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/model/FlowableInstance.java b/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/model/FlowableInstance.java new file mode 100644 index 0000000..1945bfe --- /dev/null +++ b/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/model/FlowableInstance.java @@ -0,0 +1,48 @@ +package com.lanyuanxiaoyao.flowable.core.model; + +import com.lanyuanxiaoyao.flowable.core.helper.MapHelper; +import java.time.LocalDateTime; +import java.util.Map; +import lombok.Data; + +/** + * @author lanyuanxiaoyao + * @version 20241231 + */ +@Data +public class FlowableInstance { + private final String instanceId; + private final Map metadata; + private final LocalDateTime createdTime; + + private String currentNodeId; + private Status status; + private LocalDateTime updatedTime = LocalDateTime.now(); + + public FlowableInstance(String instanceId, String currentNodeId) { + this(instanceId, currentNodeId, MapHelper.empty(), Status.RUNNING, LocalDateTime.now(), LocalDateTime.now()); + } + + public FlowableInstance(String instanceId, String currentNodeId, Map metadata) { + this(instanceId, currentNodeId, metadata, Status.RUNNING, LocalDateTime.now(), LocalDateTime.now()); + } + + public FlowableInstance(String instanceId, String currentNodeId, Map 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 metadata) { + this.metadata.putAll(metadata); + } + + public enum Status { + RUNNING, + COMPLETED, + ERROR, + } +} diff --git a/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/model/FlowableListener.java b/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/model/FlowableListener.java new file mode 100644 index 0000000..53a01b5 --- /dev/null +++ b/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/model/FlowableListener.java @@ -0,0 +1,19 @@ +package com.lanyuanxiaoyao.flowable.core.model; + +/** + * 节点监听 + * + * @author lanyuanxiaoyao + * @version 20241231 + */ +public interface FlowableListener { + void onStart(FlowableInstance instance); + + void onError(FlowableInstance instance, Throwable throwable); + + void onComplete(FlowableInstance instance); + + void onApprove(FlowableInstance instance); + + void onReject(FlowableInstance instance); +} diff --git a/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/model/FlowableNode.java b/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/model/FlowableNode.java new file mode 100644 index 0000000..bba1667 --- /dev/null +++ b/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/model/FlowableNode.java @@ -0,0 +1,45 @@ +package com.lanyuanxiaoyao.flowable.core.model; + +import com.lanyuanxiaoyao.flowable.core.helper.ListHelper; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import lombok.Data; + +/** + * 流程节点 + * + * @author lanyuanxiaoyao + * @version 20241231 + */ +@Data +public class FlowableNode { + private final String nodeId; + private final String name; + private final String description; + private final RunType runType; + private final Map nextNodes; + private final List> listeners; + private final LocalDateTime createdTime; + + private LocalDateTime updatedTime = LocalDateTime.now(); + + public FlowableNode(String nodeId, String name, String description, RunType runType, Map nextNodes) { + this(nodeId, name, description, runType, nextNodes, ListHelper.empty(), LocalDateTime.now()); + } + + public FlowableNode(String nodeId, String name, String description, RunType runType, Map nextNodes, List> listeners, LocalDateTime createdTime) { + this.nodeId = nodeId; + this.name = name; + this.description = description; + this.runType = runType; + this.nextNodes = nextNodes; + this.listeners = listeners; + this.createdTime = createdTime; + } + + public enum RunType { + AUTOMATIC, + MANUAL, + } +} diff --git a/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/repository/FlowableRepository.java b/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/repository/FlowableRepository.java new file mode 100644 index 0000000..be394f6 --- /dev/null +++ b/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/repository/FlowableRepository.java @@ -0,0 +1,39 @@ +package com.lanyuanxiaoyao.flowable.core.repository; + +import com.lanyuanxiaoyao.flowable.core.model.FlowableHistory; +import com.lanyuanxiaoyao.flowable.core.model.FlowableInstance; +import com.lanyuanxiaoyao.flowable.core.model.FlowableNode; +import java.util.List; + +/** + * 存储统一管理 + * + * @author lanyuanxiaoyao + * @version 20241231 + */ +public interface FlowableRepository { + void saveNode(FlowableNode node); + + void saveNode(List nodes); + + FlowableNode getNode(String nodeId); + + List listNodes(); + + void saveInstance(FlowableInstance instance); + + FlowableInstance getInstance(String instantId); + + List listInstances(); + + void saveHistory(FlowableHistory history); + + FlowableHistory getHistory(String historyId); + + List listHistories(String instanceId); + + /** + * 同时保存实例和历史记录,单独设立这里一个方法是方便数据库使用事务重写 + */ + void saveInstanceAndHistory(FlowableInstance instance, FlowableHistory history); +} diff --git a/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/test/TestFlowableManager.java b/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/test/TestFlowableManager.java new file mode 100644 index 0000000..957a694 --- /dev/null +++ b/flowable-core/src/main/java/com/lanyuanxiaoyao/flowable/core/test/TestFlowableManager.java @@ -0,0 +1,123 @@ +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 nextNodes) { + return new FlowableNode( + nodeId, + UUID.randomUUID().toString(), + UUID.randomUUID().toString(), + FlowableNode.RunType.MANUAL, + 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)); + } +} diff --git a/flowable-core/src/test/java/com/lanyuanxiaoyao/flowable/core/InMemoryFlowableRepository.java b/flowable-core/src/test/java/com/lanyuanxiaoyao/flowable/core/InMemoryFlowableRepository.java new file mode 100644 index 0000000..d6ecfb0 --- /dev/null +++ b/flowable-core/src/test/java/com/lanyuanxiaoyao/flowable/core/InMemoryFlowableRepository.java @@ -0,0 +1,90 @@ +package com.lanyuanxiaoyao.flowable.core; + +import com.lanyuanxiaoyao.flowable.core.helper.ListHelper; +import com.lanyuanxiaoyao.flowable.core.helper.StringHelper; +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 java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 内存存储 + * + * @author lanyuanxiaoyao + * @version 20241231 + */ +public class InMemoryFlowableRepository implements FlowableRepository { + private static final Map nodes = new HashMap<>(); + private static final Map instances = new HashMap<>(); + private static final Map> histories = new HashMap<>(); + + @Override + public void saveNode(FlowableNode node) { + nodes.put(node.getNodeId(), node); + } + + @Override + public void saveNode(List nodes) { + for (FlowableNode node : nodes) { + saveNode(node); + } + } + + @Override + public FlowableNode getNode(String nodeId) { + return nodes.get(nodeId); + } + + @Override + public List listNodes() { + return new ArrayList<>(nodes.values()); + } + + @Override + public void saveInstance(FlowableInstance instance) { + instances.put(instance.getInstanceId(), instance); + } + + @Override + public FlowableInstance getInstance(String instantId) { + return instances.getOrDefault(instantId, null); + } + + @Override + public List listInstances() { + return new ArrayList<>(instances.values()); + } + + @Override + public void saveHistory(FlowableHistory history) { + String instanceId = history.getInstanceId(); + if (!histories.containsKey(instanceId)) { + histories.put(instanceId, new ArrayList<>()); + } + histories.get(instanceId).add(history); + } + + @Override + public FlowableHistory getHistory(String historyId) { + return histories.values() + .stream() + .flatMap(List::stream) + .filter(history -> StringHelper.equals(history.getHistoryId(), historyId)) + .findFirst() + .orElse(null); + } + @Override + public List listHistories(String instanceId) { + return histories.getOrDefault(instanceId, ListHelper.empty()); + } + + @Override + public void saveInstanceAndHistory(FlowableInstance instance, FlowableHistory history) { + saveInstance(instance); + saveHistory(history); + } +} diff --git a/flowable-core/src/test/java/com/lanyuanxiaoyao/flowable/core/SimpleFlowableManager.java b/flowable-core/src/test/java/com/lanyuanxiaoyao/flowable/core/SimpleFlowableManager.java new file mode 100644 index 0000000..590c430 --- /dev/null +++ b/flowable-core/src/test/java/com/lanyuanxiaoyao/flowable/core/SimpleFlowableManager.java @@ -0,0 +1,14 @@ +package com.lanyuanxiaoyao.flowable.core; + +import com.lanyuanxiaoyao.flowable.core.manager.FlowableManager; +import java.util.UUID; + +/** + * @author lanyuanxiaoyao + * @version 20241231 + */ +public class SimpleFlowableManager extends FlowableManager { + public SimpleFlowableManager() { + super(new InMemoryFlowableRepository(), () -> UUID.randomUUID().toString()); + } +} diff --git a/flowable-core/src/test/java/com/lanyuanxiaoyao/flowable/core/TestSimpleFlowableManager.java b/flowable-core/src/test/java/com/lanyuanxiaoyao/flowable/core/TestSimpleFlowableManager.java new file mode 100644 index 0000000..130319b --- /dev/null +++ b/flowable-core/src/test/java/com/lanyuanxiaoyao/flowable/core/TestSimpleFlowableManager.java @@ -0,0 +1,15 @@ +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(); + } +} diff --git a/pom.xml b/pom.xml index 1254a5a..5c8624f 100644 --- a/pom.xml +++ b/pom.xml @@ -6,26 +6,54 @@ com.lanyuanxiaoyao flowable - 1.0-SNAPSHOT + 1.0.0-SNAPSHOT + pom + + + flowable-core + adapter/flowable-spring-boot-jpa-starter + 8 8 UTF-8 + + 2.6.15 - - - org.projectlombok - lombok - 1.18.34 - - - org.junit.jupiter - junit-jupiter - 5.11.4 - test - - + + + + com.lanyuanxiaoyao + flowable-core + 1.0.0-SNAPSHOT + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + cn.hutool + hutool-core + 5.8.32 + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + \ No newline at end of file