feat: add explicit conditional routing for approval steps

Require ProcessDefinition transitions so the engine can branch or end by conditionKey, persist them in JDBC, and pin repository text files to LF.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
0264408
2026-09-10 18:56:59 +08:00
co-authored by Cursor
parent 581823290a
commit 58ec867eab
23 changed files with 594 additions and 52 deletions
@@ -2,6 +2,7 @@ package com.jetlumen.ordo.spring;
import com.jetlumen.ordo.api.AssigneeResolver;
import com.jetlumen.ordo.api.OrdoEngine;
import com.jetlumen.ordo.api.RoutingCondition;
import com.jetlumen.ordo.api.TransactionExecutor;
import com.jetlumen.ordo.api.repository.ApprovalTaskRepository;
import com.jetlumen.ordo.api.repository.ProcessDefinitionRepository;
@@ -58,6 +59,12 @@ public class OrdoJdbcAutoConfiguration {
return AssigneeResolver.direct();
}
@Bean
@ConditionalOnMissingBean
public RoutingCondition ordoRoutingCondition() {
return RoutingCondition.always();
}
@Bean
@ConditionalOnMissingBean
@DependsOnDatabaseInitialization
@@ -93,11 +100,12 @@ public class OrdoJdbcAutoConfiguration {
@ConditionalOnMissingBean
public OrdoEngine ordoEngine(Clock ordoClock,
AssigneeResolver ordoAssigneeResolver,
RoutingCondition ordoRoutingCondition,
TransactionExecutor ordoTransactionExecutor,
ProcessDefinitionRepository ordoProcessDefinitionRepository,
ProcessInstanceRepository ordoProcessInstanceRepository,
ApprovalTaskRepository ordoApprovalTaskRepository) {
return new DefaultOrdoEngine(ordoClock, ordoAssigneeResolver, ordoTransactionExecutor,
return new DefaultOrdoEngine(ordoClock, ordoAssigneeResolver, ordoRoutingCondition, ordoTransactionExecutor,
ordoProcessDefinitionRepository, ordoProcessInstanceRepository, ordoApprovalTaskRepository);
}
}
@@ -6,6 +6,7 @@ import com.jetlumen.ordo.api.AssigneeResolver;
import com.jetlumen.ordo.api.OrdoEngine;
import com.jetlumen.ordo.api.ProcessDefinition;
import com.jetlumen.ordo.api.ProcessInstance;
import com.jetlumen.ordo.api.RoutingCondition;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
@@ -21,7 +22,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class OrdoJdbcAutoConfigurationTest {
private static final ProcessDefinition LEAVE_REQUEST = new ProcessDefinition("leave-request", "Leave request",
private static final ProcessDefinition LEAVE_REQUEST = ProcessDefinition.linear("leave-request", "Leave request",
List.of(ApprovalStep.single("manager", "Manager approval", "maria")));
private final ApplicationContextRunner withDataSourceRunner = new ApplicationContextRunner()
@@ -76,6 +77,15 @@ class OrdoJdbcAutoConfigurationTest {
});
}
@Test
void honoursUserDefinedRoutingCondition() {
withDataSourceRunner.withUserConfiguration(CustomRoutingConditionConfig.class)
.run(context -> {
assertThat(context).hasSingleBean(OrdoEngine.class);
assertThat(context.getBean(RoutingCondition.class).matches("any", null)).isTrue();
});
}
@Configuration
static class CustomAssigneeResolverConfig {
@Bean
@@ -83,4 +93,12 @@ class OrdoJdbcAutoConfigurationTest {
return (candidate, step, ctx) -> "custom-assignee";
}
}
@Configuration
static class CustomRoutingConditionConfig {
@Bean
RoutingCondition ordoRoutingCondition() {
return (key, context) -> true;
}
}
}