Files
ordo/docs/jdbc-plan.md
T
0264408andClaude Code 397a9c229b feat: initial approval workflow engine with in-memory and JDBC storage
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>
2026-09-08 16:56:04 +08:00

132 lines
4.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
建议将 JDBC 做成独立模块,并在实现前先补上“跨仓储事务”和“并发安全”两个能力;否则一次审批会拆成多次独立数据库操作,容易留下半完成流程。
1. 新建模块
```text
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。
2. 先补事务边界
在 `ordo-api` 增加一个通用端口:
```java
public interface TransactionExecutor {
<T> T execute(Supplier<T> action);
}
```
`DefaultOrdoEngine` 的 `start`、`approve`、`reject` 应在同一个事务中执行。
这保证:
- 发起流程时,“创建实例 + 创建第一条任务”要么都成功,要么都回滚。
- 审批时,“完成旧任务 + 创建下一任务 / 结束实例”要么都成功,要么都回滚。
内存实现提供无操作事务执行器;JDBC 实现使用同一条线程绑定的 `Connection`,执行 `commit` 或 `rollback`。
3. 调整仓储 SPI 的并发语义
当前 `save` 是覆盖式写入,JDBC 下无法避免两个用户同时审批同一任务。建议在落 JDBC 前调整:
```java
boolean insertIfAbsent(ProcessDefinition definition);
boolean completeIfPending(ApprovalTask completedTask);
```
`completeIfPending` 对应 SQL:
```sql
UPDATE ordo_approval_task
SET status = ?, completed_at = ?, action_actor = ?, action_comment = ?, action_at = ?
WHERE id = ? AND status = 'PENDING'
```
受影响行数为 `0` 时,抛出 `TaskAlreadyCompletedException`。这比仅依赖 JVM 内的 `synchronized` 更可靠。
4. 数据库模型
采用规范化表,不把步骤和任务都塞进 JSON。
```text
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
```
至少建立:
```text
ordo_approval_task(instance_id)
ordo_approval_task(status, assignee)
ordo_process_instance(definition_id)
```
5. `ProcessContext` 的持久化
`ProcessContext.variables` 适合存为 `context_json`。在 JDBC 模块内部使用 Jackson 做序列化与反序列化,不要让 `ordo-api` 依赖 Jackson。
v0.1 可以约定上下文仅支持 JSON 兼容值:字符串、数字、布尔值、列表、嵌套 Map。日期、枚举和自定义 Java 对象以后再通过可插拔 `ContextCodec` 解决。
6. JDBC 实现顺序
- 先写 `V1__create_ordo_tables.sql`
- 实现 `JdbcTransactionExecutor`
- 实现定义仓储与步骤读写
- 实现实例仓储及 `context_json`
- 实现任务仓储及待办查询
- 调整 `DefaultOrdoEngine` 使用事务和条件更新
- 为 JDBC 仓储添加集成测试
7. 测试策略
先用 H2 快速验证 CRUD 和映射;并发条件更新、时间类型、唯一约束等最终应使用 Testcontainers 加 PostgreSQL 或 MySQL 验证。
建议第一版目标是 PostgreSQL;表结构、`timestamp` 语义和 JSON 支持都会更明确。等 JDBC 实现稳定后,再创建 Spring Boot Starter:Starter 只负责注入 `DataSource`、JDBC 仓储、事务执行器和 `DefaultOrdoEngine`。
帮忙按照这个实现一下JDBC模块