引擎核心(flow-engine-framework)与 Spring 无关,所有存储/服务访问统一经过 IRepositoryHolder 抽象。持久化层由 flow-engine-starter-infra 提供 JPA 实现;业务方也可以自行实现仓储接口替换存储。
文件:flow-engine-framework/src/main/java/com/codingapi/flow/session/IRepositoryHolder.java
引擎各服务(FlowXxxService)不直接注入仓储,而是通过 IRepositoryHolder 获取依赖:
public interface IRepositoryHolder {
SubProcessRepository getSubProcessRepository();
WorkflowService getWorkflowService();
FlowRecordService getFlowRecordService();
FlowOperatorGateway getFlowOperatorGateway();
FlowDelayTriggerService createDelayTriggerService(DelayTask task);
FlowActionService createFlowActionService(FlowSession flowSession);
FlowService createFlowService();
FlowRecord getRecordById(long recordId);
List<IFlowOperator> findOperatorByIds(List<Long> ids);
IFlowOperator getOperatorById(long id);
void saveDelayTask(DelayTask delayTask);
void deleteDelayTask(DelayTask delayTask);
void saveRecords(List<FlowRecord> flowRecords);
void saveRecord(FlowRecord flowRecord);
List<FlowRecord> findCurrentNodeRecords(long fromId, String nodeId);
List<FlowRecord> findProcessRecords(String processId);
List<FlowRecord> findAfterRecords(String processId, long currentId);
int getParallelBranchTriggerCount(String parallelId);
void addParallelTriggerCount(String parallelId);
void clearParallelTriggerCount(String parallelId);
void saveUrgeInterval(UrgeInterval interval);
UrgeInterval getLatestUrgeInterval(String processId, long recordId);
List<DelayTask> findDelayTasks();
void saveOperatorAssignment(String processId, String nodeId, List<Long> operatorIds);
List<Long> findAssignedOperatorIds(String processId, String nodeId);
}| 实现 | 场景 | 说明 |
|---|---|---|
RepositoryHolderContext(context/ 包,单例) |
生产 | 由 starter 的 RepositoryHolderContextRegister 注册 7 个依赖;操作人读取委托 GatewayContext;记录保存委托 FlowRecordService |
MockRepositoryHolder(mock/ 包) |
Mock 沙箱 | 构造时 new 出 9 个内存仓储 + FlowRecordService/WorkflowService,每个 Mock 实例独立一份(见 Mock 模式) |
文件:flow-engine-framework/src/main/java/com/codingapi/flow/repository/
public interface WorkflowRepository {
void save(Workflow workflow);
Workflow getById(String id);
Workflow getByCode(String code);
default void lockById(String id) {} // 持久化仓储应实现数据库行锁,保证多实例下流程维度创建串行
void delete(String id);
}
public interface WorkflowVersionRepository {
WorkflowVersion get(long id);
void delete(String workId);
List<WorkflowVersion> findVersion(String workId);
void saveAll(List<WorkflowVersion> versionList);
void save(WorkflowVersion workflowVersion);
void delete(long id);
}
public interface WorkflowRuntimeRepository {
void save(WorkflowRuntime workflowRuntime);
WorkflowRuntime get(long id);
WorkflowRuntime getByWorkId(String workId, long workVersion);
void delete(WorkflowRuntime backup);
}
WorkflowRepository.lockById:接口 javadoc 明确要求持久化实现使用数据库行锁(多实例部署需要)。JPA 实现WorkflowRepositoryImpl已实现;WorkflowService.getOrCreateWorkflowRuntime通过@Transactional(REQUIRES_NEW)+lockById保证流程维度创建串行。
public interface FlowRecordRepository {
FlowRecord get(long id);
List<FlowRecord> findByIds(List<Long> ids);
void save(FlowRecord flowRecord); // 注意:为保证待办合并一致性,保存应经由 IRepositoryHolder#saveRecord
void saveAll(List<FlowRecord> flowRecords);
void delete(FlowRecord flowRecord);
List<FlowRecord> findCurrentNodeRecords(long fromId, String nodeId);
List<FlowRecord> findProcessRecords(String processId);
List<FlowRecord> findTodoRecords(String processId);
List<FlowRecord> findAfterRecords(String processId, long fromId);
List<FlowRecord> findBeforeRecords(String processId, long id);
}
public interface FlowTodoRecordRepository {
void saveAll(List<FlowTodoRecord> margeRecords);
FlowTodoRecord getByTodoKey(String key);
void delete(FlowTodoRecord margeRecord);
void save(FlowTodoRecord margeRecord);
}
public interface FlowTodoMergeRepository {
void saveAll(List<FlowTodoMerge> list);
void delete(FlowTodoMerge todoMerge);
List<FlowTodoMerge> findByTodoId(long todoId);
}/** 操作人手动分配(INITIATOR_SELECT / APPROVER_SELECT) */
public interface FlowOperatorAssignmentRepository {
void save(String processId, String nodeId, List<Long> operatorIds); // 幂等,已存在则覆盖
List<Long> findOperatorIds(String processId, String nodeId); // 不存在时返回空列表
}
/** 节点视图 JS 持久化(⚠️ infra 不提供实现,必须由业务方提供) */
public interface NodeViewJavaScriptRepository {
void save(NodeViewJavaScript javaScript);
void delete(String code);
NodeViewJavaScript get(String code);
}
/** 并行分支触发计数 */
public interface ParallelBranchRepository {
int getTriggerCount(String parallelId);
void addTriggerCount(String parallelId);
void clearTriggerCount(String parallelId);
}
/** 子流程记录 */
public interface SubProcessRepository {
void save(SubProcessRecord record);
List<SubProcessRecord> findByParentRecordId(long parentRecordId);
List<SubProcessRecord> findByParentProcessId(String parentProcessId);
List<SubProcessRecord> findByParentProcessIdAndNodeId(String parentProcessId, String nodeId);
}
/** 延迟任务 */
public interface DelayTaskRepository {
void save(DelayTask task);
void delete(DelayTask delayTask);
List<DelayTask> findAll();
}
/** 催办间隔 */
public interface UrgeIntervalRepository {
UrgeInterval getLatest(String processId, long recordId);
void save(UrgeInterval urgeInterval);
}AutoConfiguration:@ConditionalOnClass(name = "org.springframework.data.jpa.repository.JpaRepository")+@Import(FlowJpaPackageRegistrar.class)。FlowJpaPackageRegistrar(ImportBeanDefinitionRegistrar)调用AutoConfigurationPackages.register(registry, "com.codingapi.flow.infra"),让宿主应用的 JPA 扫描覆盖引擎实体与仓储包 —— 业务方无需配置@EntityScan。
| Bean | 实现类 |
|---|---|
delayTaskRepository |
DelayTaskRepositoryImpl |
urgeIntervalRepository |
UrgeIntervalRepositoryImpl |
parallelBranchRepository |
ParallelBranchRepositoryImpl |
workflowRepository |
WorkflowRepositoryImpl |
workflowRuntimeRepository |
WorkflowRuntimeRepositoryImpl |
workflowVersionRepository |
WorkflowVersionRepositoryImpl |
flowRecordRepository |
FlowRecordRepositoryImpl |
flowTodoRecordRepository |
FlowTodoRecordRepositoryImpl |
flowTodoMargeRepository(bean 名 marge) |
FlowTodoMergeRepositoryImpl |
flowOperatorAssignmentRepository |
FlowOperatorAssignmentRepositoryImpl |
subProcessRepository |
SubProcessRepositoryImpl |
⚠️ infra 不提供NodeViewJavaScriptRepository与FlowOperatorGateway的实现,必须由业务方提供。
| 实体类 | 表名 |
|---|---|
WorkflowEntity |
t_flow_workflow |
WorkflowVersionEntity |
t_flow_workflow_version |
WorkflowRuntimeEntity |
t_flow_workflow_runtime(唯一约束 uk_workflow_runtime_work_version(workId, workVersion)) |
FlowRecordEntity |
t_flow_record |
FlowTodoRecordEntity |
t_flow_todo_record |
FlowTodoMargeEntity |
t_flow_todo_marge |
ParallelControlEntity |
t_flow_parallel_control |
DelayTaskEntity |
t_flow_delay_task |
UrgeIntervalEntity |
t_flow_urge_interval |
FlowOperatorAssignmentEntity |
t_flow_operator_assignment |
SubProcessRecordEntity |
t_flow_sub_process_record |
Spring Data 仓储接口位于 jpa/ 包,全部继承外部 FastRepository(来自 springboot-starter-data-fast)。
三类记录紧密绑定,必须保持一致:
FlowRecord— 流程执行记录(TODO / DONE 状态)FlowTodoRecord— 待办记录FlowTodoMerge— 待办合并关系(开启记录合并isMergeable()时产生)
保存统一走 FlowRecordSaveService.saveAll(),单次调用按 saveRecords → saveTodoMargeRecords → removeTodoMergeRecords 顺序执行。
来自外部 springboot-starter-script 的 SPI:
| SPI | 方法 | 注册方式 |
|---|---|---|
GroovyScriptRepository |
save / delete / get(String key) |
GroovyScriptRepositoryContext.getInstance().setGroovyScriptRepository(...) |
TempGroovyScriptRepository |
get / save / delete / find(PageRequest) |
TempGroovyScriptRepositoryContext.getInstance().setTempGroovyScriptRepository(...) |
参考实现(flow-engine-example/.../repository/impl/):
@Repository
public class GroovyScriptRepositoryImpl implements GroovyScriptRepository, InitializingBean {
private final GroovyScriptEntityRepository repository;
// ...
@Override
public void afterPropertiesSet() {
GroovyScriptRepositoryContext.getInstance().setGroovyScriptRepository(this);
}
}- 引入
flow-engine-starter-infra:开箱即用(推荐)。 - 不引入 infra:自行实现 11 个仓储接口 +
NodeViewJavaScriptRepository为 Spring Bean 即可。starter 的RepositoryHolderContextRegister会自动注入。framework 测试包(src/test/java/com/codingapi/flow/repository/)提供了一套内存实现可作参考。
framework 测试中的 MyFlowServiceFactory 展示了最小装配路径 —— 仅两次静态单例注入:
// 1. 注册仓储/服务
RepositoryHolderContext.getInstance().register(
workflowService, flowRecordService,
parallelBranchRepository, delayTaskRepository,
urgeIntervalRepository, flowOperatorAssignmentRepository,
subProcessRepository);
// 2. 注册操作人网关
GatewayContext.getInstance().setFlowOperatorGateway(userGateway);