From e177e9592accbb5cb5d0ee957cfc17e69ef1acfe Mon Sep 17 00:00:00 2001 From: 0264408 Date: Thu, 17 Sep 2026 10:30:52 +0800 Subject: [PATCH] chore: delete useless docs --- docs/jdbc-plan.md | 131 ------------------------- docs/plan-conditionRouting.md | 177 ---------------------------------- 2 files changed, 308 deletions(-) delete mode 100644 docs/jdbc-plan.md delete mode 100644 docs/plan-conditionRouting.md diff --git a/docs/jdbc-plan.md b/docs/jdbc-plan.md deleted file mode 100644 index dd870e1..0000000 --- a/docs/jdbc-plan.md +++ /dev/null @@ -1,131 +0,0 @@ -建议将 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 execute(Supplier 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模块 diff --git a/docs/plan-conditionRouting.md b/docs/plan-conditionRouting.md deleted file mode 100644 index 28f571f..0000000 --- a/docs/plan-conditionRouting.md +++ /dev/null @@ -1,177 +0,0 @@ -# Plan: 条件路由 (Conditional Routing / RoutingCondition) - -## 已与用户确认的关键决策 -1. transitions 挂在 `ProcessDefinition` 级别(独立列表),不改动 `ApprovalStep`。 -2. `RoutingCondition` 采用与 `AssigneeResolver` 一致的模式:全局单例 bean, - `boolean matches(String conditionKey, ProcessContext context)`;数据库只存 `conditionKey` - 字符串,不持久化任何 Java lambda/表达式(因为 JDBC 场景下引擎重启要从数据库重新装配 - `ProcessDefinition`,无法反序列化任意代码逻辑)。 -3. **不保留线性回退**:每个 `ProcessDefinition` 必须显式声明 transitions,覆盖每个 step 的 - 出边;没有 transitions 完全是非法状态(构造期校验报错),不再依赖 `steps()` 列表顺序做 - 执行期推进(`step_order`/列表顺序仅用于候选人/去重校验和展示,不再决定执行流)。 -4. 匹配不到任何 transition 时,fail-fast 抛出 `NoRouteFoundException`(不静默回退)。 - -## 模型设计 (ordo-api) - -### 新增 `StepTransition` record -``` -record StepTransition(String fromStepId, String toStepId, String conditionKey, int priority) -``` -- `toStepId == null` 表示"终止流程"(当匹配到这条边时,实例被判 APPROVED 并完成)。 -- `conditionKey == null/blank` 表示无条件(总是匹配),用作 else/默认分支。 -- 便捷工厂: - - `StepTransition.always(fromStepId, toStepId)` — 无条件跳转,priority=0 - - `StepTransition.when(fromStepId, toStepId, conditionKey, priority)` — 条件跳转 - - `StepTransition.end(fromStepId)` — 无条件终止 - - `StepTransition.endWhen(fromStepId, conditionKey, priority)` — 条件终止 -- 评估顺序:按 `priority` 升序,第一个匹配的生效(无条件 = 恒真)。 - -### 新增 `RoutingCondition` functional interface(与 AssigneeResolver 同目录/同风格) -``` -@FunctionalInterface -public interface RoutingCondition { - boolean matches(String conditionKey, ProcessContext context); - static RoutingCondition always() { return (key, context) -> true; } -} -``` - -### `ProcessDefinition` 改为 4 个分量(破坏性变更) -``` -record ProcessDefinition(String id, String name, List steps, List transitions) -``` -构造期校验(在现有 id/name/steps 非空+去重校验基础上新增): -- `transitions` 非 null,`List.copyOf` 防御性拷贝。 -- 每条 transition 的 `fromStepId` 必须是 `steps` 中存在的 id;`toStepId`(非 null 时)也必须存在。 -- 每个 step 的 id 必须至少作为一条 transition 的 `fromStepId` 出现一次(强制显式声明出边), - 否则 `IllegalArgumentException("step X has no outgoing transition")`。 -- 同一 `(fromStepId, priority)` 不能重复(避免评估顺序有歧义)。 -- 新增便捷工厂 `ProcessDefinition.linear(id, name, steps)`:自动生成"上一步无条件指向下一步、 - 最后一步无条件终止"的 transitions,等价于旧的线性行为,用于降低无需真正条件路由的测试/demo - 的迁移成本(生成的仍然是显式 transitions,满足"强制显式"的约束,只是自动拼装)。 - -### 新增异常 `NoRouteFoundException extends OrdoException` -- message 形如 `"no matching transition for step: in instance: "`。 -- 放在 `com.jetlumen.ordo.api.exception` 包,命名/风格对齐现有 `DefinitionNotFoundException` 等。 - -## 引擎改造 (ordo-core) - -### `DefaultOrdoEngine` -- 构造函数新增 `RoutingCondition routingCondition` 参数(在 `assigneeResolver` 之后插入, - 与 Spring autoconfigure bean 顺序一致)。 -- 删除 `indexOf(...)` 私有方法(不再需要基于下标推进)。 -- `advanceOrComplete(instance, definition, step, now)` 重写为: - 1. 调用新的私有 `resolveTransition(definition, step, context)`: - 过滤 `definition.transitions()` 中 `fromStepId == step.id()` 的项,按 `priority` 升序排序, - 依次判断 `conditionKey` 为空/blank(无条件恒真)或 `routingCondition.matches(conditionKey, context)`, - 返回第一个匹配的 `StepTransition`;若无匹配,抛出 `NoRouteFoundException`。 - 2. 若匹配结果 `toStepId() == null` → `completeInstance(instance, ProcessStatus.APPROVED, now)`。 - 3. 否则 → `createStepTasks(instance, requireStep(definition, matched.toStepId()), now)`。 -- `advanceAfterDecision` 中原有 ANY/ALL 策略判断逻辑不变,只是"推进到下一步"的调用点改为走上面的 - transition 解析,不再用 `stepIndex == size-1` 判断是否最后一步。 -- `NoRouteFoundException` 在 `transactionExecutor.execute(...)` 内抛出,天然触发整个 approve/reject - 事务回滚(复用现有 `rollsBackTheWholeApprovalWhenTheNextStepCannotBeCreated` 验证过的回滚机制)。 -- `start()` 的起始 step 仍取 `definition.steps().getFirst()`(未改变,属于本次范围外的假设, - 如需显式声明起始 step 可后续再提出)。 - -### `InMemoryOrdoEngine` -- 仿照现有 `AssigneeResolver` 构造函数重载模式,新增: - - `InMemoryOrdoEngine(RoutingCondition routingCondition)` - - `InMemoryOrdoEngine(AssigneeResolver assigneeResolver, RoutingCondition routingCondition)` - - `InMemoryOrdoEngine(Clock clock, AssigneeResolver assigneeResolver, RoutingCondition routingCondition)` - - 默认无参/仅 Clock 的构造函数内部使用 `RoutingCondition.always()`。 - -## 持久化 (ordo-storage-jdbc) - -### 新迁移 `V3__add_step_transitions.sql` -```sql -CREATE TABLE ordo_step_transition ( - definition_id VARCHAR(64) NOT NULL, - from_step_id VARCHAR(64) NOT NULL, - to_step_id VARCHAR(64), - condition_key VARCHAR(255), - priority INTEGER NOT NULL, - PRIMARY KEY (definition_id, from_step_id, priority), - CONSTRAINT fk_transition_from FOREIGN KEY (definition_id, from_step_id) - REFERENCES ordo_approval_step (definition_id, step_id), - CONSTRAINT fk_transition_to FOREIGN KEY (definition_id, to_step_id) - REFERENCES ordo_approval_step (definition_id, step_id) -); -``` -**重要提醒(已踩过的坑,来自 /memories/repo/any-all-multi-approval.md)**: -- 必须同步把这个新迁移文件加进 `ordo-storage-jdbc/src/test/java/.../JdbcTestSupport.java` 的 - `MIGRATIONS` 数组,否则测试库缺表导致全部 JDBC 测试失败。 -- 注释里不要出现分号(或依赖已有的按行剥离 `--` 注释逻辑,已经是健壮的,无需再担心)。 - -### 新增 `StepTransitionMapper`(仿照 `ApprovalStepMapper` 的 `StepRow` 模式) -- `readRow(ResultSet) -> TransitionRow(fromStepId, toStepId, conditionKey, priority)`。 - -### `JdbcProcessDefinitionRepository` -- `insertIfAbsent`:在插入 steps/candidates 之后,遍历 `definition.transitions()` 执行 - `INSERT INTO ordo_step_transition (...)`。 -- `findById`:新增 `SELECT_TRANSITIONS`(按 `from_step_id, priority` 排序),组装 - `List` 传入新的 4 参 `ProcessDefinition` 构造函数。 - -## Spring Boot 自动配置 (ordo-spring-boot-autoconfigure) -- `OrdoJdbcAutoConfiguration` 新增: - ```java - @Bean - @ConditionalOnMissingBean - public RoutingCondition ordoRoutingCondition() { - return RoutingCondition.always(); - } - ``` -- `ordoEngine(...)` 方法签名新增 `RoutingCondition ordoRoutingCondition` 参数,传给 - `DefaultOrdoEngine` 构造函数。 -- `OrdoJdbcAutoConfigurationTest`:更新 `LEAVE_REQUEST` 定义使用 `ProcessDefinition.linear(...)`; - 新增一个 `honoursUserDefinedRoutingCondition` 测试,仿照现有 - `honoursUserDefinedAssigneeResolver` 的写法。 - -## 调用点迁移(约 29 处 `new ProcessDefinition(...)`) -- 绝大多数纯线性场景(测试/demo 不需要真正的条件路由)直接把 - `new ProcessDefinition(id, name, steps)` 换成 `ProcessDefinition.linear(id, name, steps)`。 -- 受影响文件: - - `ordo-api/src/test/java/.../ProcessDefinitionTest.java`(同时新增 transitions 相关校验用例: - 缺少出边报错、fromStepId/toStepId 引用不存在的 step 报错、重复 priority 报错) - - `ordo-core/src/test/java/.../InMemoryOrdoEngineTest.java`(8 处调用点 + 新增条件路由专项测试) - - `ordo-spring-boot-autoconfigure/src/test/java/.../OrdoJdbcAutoConfigurationTest.java` - - `ordo-storage-jdbc/src/test/java/.../Jdbc*Test.java`(约 10 处,含 - `JdbcProcessDefinitionRepositoryTest` 需要新增"读写 transitions 往返"专项用例) - - `ordo-storage-jdbc/src/main/java/.../JdbcProcessDefinitionRepository.java`(`findById` 内部 - 组装 `ProcessDefinition` 那一处,改为传入真实加载到的 transitions) - - `ordo-example/src/main/java/.../LeaveRequestExample.java` - - `d:\Projects\Personal\java\rhizome\src\main\java\...\LeaveRequestDemoService.java` - -## 新增测试(条件路由专项,主要在 `InMemoryOrdoEngineTest` 或新建 -`ConditionalRoutingTest`) -1. 基础分支:manager 通过后,根据 `conditionKey`(如 `"amount-gt-1000"`)走向不同的下一 step - (高额 -> director 审批 step;低额 -> 直接 end),验证 `RoutingCondition.matches` 被正确调用 - 并影响推进结果。 -2. 无匹配 transition 时抛出 `NoRouteFoundException`,且整个 approve 事务回滚(沿用现有 - rollback 测试模式)。 -3. `ProcessDefinition` 构造期校验: - - 某 step 没有任何出边 -> `IllegalArgumentException`。 - - transition 引用不存在的 `fromStepId`/`toStepId` -> `IllegalArgumentException`。 - - 同一 `(fromStepId, priority)` 重复 -> `IllegalArgumentException`。 -4. JDBC 往返测试:`JdbcProcessDefinitionRepositoryTest` 新增用例,插入一个带条件分支的 - definition,`findById` 读回后 `transitions()` 内容与写入一致(包括 `toStepId == null` 终止边)。 - -## 范围外 / 明确不做 -- 不支持在 transition 上直接内嵌 Java lambda(会破坏 JDBC 持久化重启场景,已由用户确认)。 -- 不引入显式 `startStepId` 概念,起始步骤仍是 `steps().getFirst()`。 -- 不做并行分支/合并(fork-join),仅支持"单一当前 step -> 单一下一 step 或终止"的有向图推进, - 与现有 ANY/ALL 多候选人机制正交(transition 发生在"某个 step 整体被判定通过/拒绝"之后)。 - -## 实施顺序(有依赖关系,需按序执行) -1. ordo-api:`StepTransition`、`RoutingCondition`、`ProcessDefinition`(含 `linear` 工厂)、 - `NoRouteFoundException`。 -2. ordo-core:`DefaultOrdoEngine` 改造、`InMemoryOrdoEngine` 构造函数重载。 -3. ordo-storage-jdbc:`V3` 迁移 + `StepTransitionMapper` + `JdbcProcessDefinitionRepository` 改造 - + 同步更新 `JdbcTestSupport.MIGRATIONS`。 -4. ordo-spring-boot-autoconfigure:`RoutingCondition` bean 及 `ordoEngine(...)` 装配。 -5. 迁移全部 ~29 处旧调用点(`ProcessDefinition.linear(...)`),含 rhizome demo。 -6. 新增条件路由专项测试(引擎行为 + 校验 + JDBC 往返)。 - -## 验证 -- `ordo-api`/`ordo-core`/`ordo-storage-jdbc`/`ordo-spring-boot-autoconfigure` 全量单元测试通过。 -- 手动确认 rhizome 端到端 curl 流程(线性场景切到 `ProcessDefinition.linear` 后)仍然可用 - (非阻塞,可在实现完成后单独验证)。