Lightweight linear approval engine (v0.1): - ordo-api: domain model, OrdoEngine port, repository SPI with conditional updates (insertIfAbsent, completeIfPending) and a TransactionExecutor port for atomic multi-step writes - ordo-core: DefaultOrdoEngine running register/start/approve/reject inside a transaction boundary; in-memory engine and repositories - ordo-storage-jdbc: thread-bound JDBC transactions, normalized V1 schema migration, Jackson-based ProcessContext JSON codec - tests: unit tests plus H2 integration tests; PostgreSQL integration tests run against a local instance via ordo.test.pg.* properties and skip when unreachable Co-Authored-By: Claude Code <noreply@anthropic.com>
4.5 KiB
建议将 JDBC 做成独立模块,并在实现前先补上“跨仓储事务”和“并发安全”两个能力;否则一次审批会拆成多次独立数据库操作,容易留下半完成流程。
- 新建模块
ordo-storage-jdbc/
├── pom.xml
└── src/main/
├── java/com/jetlumen/ordo/storage/jdbc/
│ ├── JdbcTransactionExecutor.java
│ ├── JdbcProcessDefinitionRepository.java
│ ├── JdbcProcessInstanceRepository.java
│ ├── JdbcApprovalTaskRepository.java
│ ├── JdbcConnectionProvider.java
│ └── mapper/
└── resources/db/migration/
└── V1__create_ordo_tables.sql
依赖只需要 ordo-api、javax.sql.DataSource 和 JDBC 驱动;先不要依赖 Spring。
- 先补事务边界
在 ordo-api 增加一个通用端口:
public interface TransactionExecutor {
<T> T execute(Supplier<T> action);
}
DefaultOrdoEngine 的 start、approve、reject 应在同一个事务中执行。
这保证:
- 发起流程时,“创建实例 + 创建第一条任务”要么都成功,要么都回滚。
- 审批时,“完成旧任务 + 创建下一任务 / 结束实例”要么都成功,要么都回滚。
内存实现提供无操作事务执行器;JDBC 实现使用同一条线程绑定的 Connection,执行 commit 或 rollback。
- 调整仓储 SPI 的并发语义
当前 save 是覆盖式写入,JDBC 下无法避免两个用户同时审批同一任务。建议在落 JDBC 前调整:
boolean insertIfAbsent(ProcessDefinition definition);
boolean completeIfPending(ApprovalTask completedTask);
completeIfPending 对应 SQL:
UPDATE ordo_approval_task
SET status = ?, completed_at = ?, action_actor = ?, action_comment = ?, action_at = ?
WHERE id = ? AND status = 'PENDING'
受影响行数为 0 时,抛出 TaskAlreadyCompletedException。这比仅依赖 JVM 内的 synchronized 更可靠。
- 数据库模型
采用规范化表,不把步骤和任务都塞进 JSON。
ordo_process_definition
- id varchar(64) primary key
- name varchar(255) not null
ordo_approval_step
- definition_id varchar(64) not null
- step_id varchar(64) not null
- step_name varchar(255) not null
- assignee varchar(255) not null
- step_order integer not null
- primary key (definition_id, step_id)
ordo_process_instance
- id varchar(36) primary key
- definition_id varchar(64) not null
- initiator varchar(255) not null
- status varchar(32) not null
- context_json text not null
- started_at timestamp not null
- finished_at timestamp null
ordo_approval_task
- id varchar(36) primary key
- instance_id varchar(36) not null
- step_id varchar(64) not null
- task_name varchar(255) not null
- assignee varchar(255) not null
- status varchar(32) not null
- created_at timestamp not null
- completed_at timestamp null
- action_actor varchar(255) null
- action_comment text null
- action_at timestamp null
至少建立:
ordo_approval_task(instance_id)
ordo_approval_task(status, assignee)
ordo_process_instance(definition_id)
ProcessContext的持久化
ProcessContext.variables 适合存为 context_json。在 JDBC 模块内部使用 Jackson 做序列化与反序列化,不要让 ordo-api 依赖 Jackson。
v0.1 可以约定上下文仅支持 JSON 兼容值:字符串、数字、布尔值、列表、嵌套 Map。日期、枚举和自定义 Java 对象以后再通过可插拔 ContextCodec 解决。
- JDBC 实现顺序
- 先写
V1__create_ordo_tables.sql - 实现
JdbcTransactionExecutor - 实现定义仓储与步骤读写
- 实现实例仓储及
context_json - 实现任务仓储及待办查询
- 调整
DefaultOrdoEngine使用事务和条件更新 - 为 JDBC 仓储添加集成测试
- 测试策略
先用 H2 快速验证 CRUD 和映射;并发条件更新、时间类型、唯一约束等最终应使用 Testcontainers 加 PostgreSQL 或 MySQL 验证。
建议第一版目标是 PostgreSQL;表结构、timestamp 语义和 JSON 支持都会更明确。等 JDBC 实现稳定后,再创建 Spring Boot Starter:Starter 只负责注入 DataSource、JDBC 仓储、事务执行器和 DefaultOrdoEngine。
帮忙按照这个实现一下JDBC模块