# Ordo 轻量审批流程引擎。宿主通过 `OrdoEngine` 注册流程定义、发起实例、审批/驳回/转派/撤回/取消,并查询任务、实例与审计历史。引擎不绑定业务表单、用户体系或设计器 UI。可选 REST(`ordo.rest.enabled`)。业务数据放在 `ProcessContext` 里。 要求 **Java 17+**。当前版本 `0.0.1-SNAPSHOT`。 ## 模块 | 模块 | 作用 | |---|---| | `ordo-api` | 公共模型与 `OrdoEngine` 端口 | | `ordo-core` | 运行时(`DefaultOrdoEngine` / `InMemoryOrdoEngine`) | | `ordo-storage-jdbc` | JDBC 存储 + 按方言 Flyway 基线(PostgreSQL / MySQL) | | `ordo-spring-boot-starter` | Spring Boot 自动装配(JDBC + Flyway) | | `ordo-example` | 内存引擎示例 | 存储实现通过 dialect 层支持 **PostgreSQL** 与 **MySQL**(测试可用 H2 PostgreSQL 兼容模式)。Starter **不携带** JDBC 驱动,宿主自行加入 `postgresql`、`mysql-connector-j` 或 `h2`。Spring Boot 4 还需额外引入 `spring-boot-starter-flyway`,否则迁移不会跑。 ## 能力 - 线性或多步图:`StepTransition` + 内置谓词 / 宿主 `RoutingCondition` - 会签/或签:`ApprovalPolicy.ALL` / `ANY`(多候选人) - 结构化并行:`kind: PARALLEL` 块(一层、join ALL) - ACTION 步骤:事务提交后调用宿主 `ActionHandler`(可经 `NamedAction` 分发) - 发起人撤回:`WITHDRAWN`,待办任务 `SKIPPED` - 管理员/系统取消:`cancel` → `CANCELLED`,待办任务 `SKIPPED`(引擎不鉴权角色) - 任务转派:当前办理人 `reassign`,审计 `TASK_REASSIGNED` - 到期升级:步骤 `due` + `processDue`,审计 `TASK_ESCALATED` - 分页查询:任务 / 实例 / 流程定义 - 审计时间线:`ProcessEvent` + `queryHistory` - 扩展点:`AssigneeResolver`、`RoutingCondition`、`ActionHandler`、`NamedCondition`、`NamedAction`、`OrdoEventListener` 开发计划:独立设计器(不进本仓库)。多租户 **暂不在计划中**。见 [docs/roadmap.md](docs/roadmap.md)。 详细用法(定义 JSON、扩展点、异常、查询、ACTION/审计语义)见 **[docs/usage.md](docs/usage.md)**。对外行为变更时同步更新该文档。 ## 内存快速开始 ```xml com.jetlumen ordo-core 0.0.1-SNAPSHOT ``` ```java import com.jetlumen.ordo.api.OrdoEngine; import com.jetlumen.ordo.api.definition.ApprovalStep; import com.jetlumen.ordo.api.definition.ProcessDefinition; import com.jetlumen.ordo.api.runtime.ProcessContext; import com.jetlumen.ordo.core.InMemoryOrdoEngine; OrdoEngine ordo = new InMemoryOrdoEngine(); ordo.publish(ProcessDefinition.linear("leave-request", "Leave request", List.of( ApprovalStep.single("manager", "Manager approval", "maria"), ApprovalStep.single("hr", "HR approval", "henry") ))); ProcessInstance instance = ordo.start("leave-request", "alice", new ProcessContext(Map.of("requestId", "LEAVE-2026-001"))); ApprovalTask manager = ordo.findPendingTasksByInstanceId(instance.id()).get(0); ordo.approve(manager.id(), "maria", "ok"); ``` 完整示例:`ordo-example` 的 `LeaveRequestExample`。 ## JSON 流程定义 ```json { "id": "leave-request-routed", "name": "Leave request", "steps": [ { "id": "notify-submitted", "name": "Notify submitted", "kind": "ACTION", "action": "leave-submitted-mail" }, { "id": "manager", "name": "Manager approval", "candidates": ["maria"], "policy": "ANY" }, { "id": "hr", "name": "HR approval", "candidates": ["henry"], "policy": "ANY" }, { "id": "notify-approved", "name": "Notify approved", "kind": "ACTION", "action": "leave-approved-mail" } ], "transitions": [ { "from": "notify-submitted", "to": "manager" }, { "from": "manager", "to": "hr", "when": { "gt": ["days", 3] }, "priority": 0 }, { "from": "manager", "to": "notify-approved", "priority": 1 }, { "from": "hr", "to": "notify-approved" }, { "from": "notify-approved", "to": null } ] } ``` - `kind` 默认 `APPROVAL`;ACTION 用 `"action"` 作为 handler 查找键;`PARALLEL` 用 `branches`(结构化并行块,见 [docs/usage.md](docs/usage.md))。 - 带 `when` 的边先按 `priority` 匹配,都未命中再走无条件边;`to: null` 表示结束。 - 代码侧可用 `ProcessDefinitionParser.fromJson(...)`(`com.jetlumen.ordo.api.definition`)。 ## Spring Boot ```xml com.jetlumen ordo-spring-boot-starter 0.0.1-SNAPSHOT ``` 需要 `DataSource`。存在 `DataSource` 且 `ordo.enabled` 不为 `false` 时装配 JDBC 引擎。REST 另需宿主的 Web starter,且 `ordo.rest.enabled=true`。 ```yaml ordo: enabled: true jdbc: dialect: # 可选 postgresql / mysql;空则按 DataSource 探测 definitions: location: classpath*:ordo/*.json # 默认值;启动时 publish 加载 due: poll-ms: 0 # >0 时轮询 processDue rest: enabled: false base-path: /ordo ``` 宿主提供 Bean 即可覆盖默认值: | Bean | 默认 | |---|---| | `AssigneeResolver` | 候选人即办理人 | | `NamedCondition` / `NamedAction` | 可多个;无对应门面时按 key 分发 | | `RoutingCondition` | 无具名 condition 时始终匹配;有则未知 `ref` 为 false | | `ActionHandler` | 无具名 action 时空操作;有则未知 key 抛错 | | `OrdoCatalog` | REST 打开时从 Named* 投影;`assignees` 空 | | `OrdoEventListener` | 可注册多个,提交后按顺序调用 | ## 运行时约定 **办理人** 必须等于任务 `assignee`,否则 `UnauthorizedTaskOperationException`(`approve` / `reject` / `reassign`)。 **转派** 只改 PENDING 任务的 `assignee`,不推进步骤。 **撤回** 仅发起人可操作,且实例须为 `RUNNING`。 **取消** `cancel` 任意非空 `actor`,实例须为 `RUNNING`;谁能调用由宿主决定。 实例状态:`RUNNING` / `APPROVED` / `REJECTED` / `WITHDRAWN` / `CANCELLED`。 任务状态:`PENDING` / `APPROVED` / `REJECTED` / `SKIPPED`。 **ACTION** 在审批事务提交之后执行。失败只记 `FAILED`、打日志、发 `ACTION_FAILED`,**不回滚已生效审批、不阻塞后续步骤**。可重试策略留给宿主(listener 或 `queryActionExecutions`)。 **历史** `queryHistory(instanceId, page)` 按时间升序。事件类型包括实例起止、任务创建/审批/跳过/转派、ACTION 成败。 `OrdoEventListener.onEvent` 在提交后派发;单个 listener 抛错不影响流程和其他 listener。 ## 查询 ```java ordo.queryTasks(TaskQuery.any().withAssignee("maria").withStatus(TaskStatus.PENDING), PageRequest.of(0, 20)); ordo.queryInstances(InstanceQuery.any().withInitiator("alice"), PageRequest.of(0, 20)); ordo.queryDefinitions(PageRequest.of(0, 20)); ordo.queryHistory(instanceId, PageRequest.of(0, 50)); ordo.queryActionExecutions(instanceId, PageRequest.of(0, 20)); ``` 任务/实例列表默认最新优先;历史与 ACTION 执行记录按发生时间升序。 ## 构建 ```bash mvn -pl ordo-api,ordo-core,ordo-storage-jdbc,ordo-spring-boot-autoconfigure,ordo-spring-boot-starter install ``` 先 `install` 再给外部工程(如宿主应用)引用。