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>
132 lines
4.5 KiB
Markdown
132 lines
4.5 KiB
Markdown
建议将 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模块
|