Skip to content

Commit daa03d3

Browse files
committed
add components md
1 parent 59f1d9d commit daa03d3

7 files changed

Lines changed: 889 additions & 0 deletions
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# FlowScriptContext
2+
3+
## 何时使用
4+
5+
当需要在 Groovy 脚本中访问 Spring Bean、流程记录或操作人数据时使用。`FlowScriptContext` 是脚本运行时的 `$bind` 上下文对象,由 `ScriptRuntimeContext` 在每次脚本执行时注入为 `$bind` 变量。典型场景:
6+
7+
- 在自定义 Groovy 脚本中查询流程记录(`$bind.getRecordById(...)`
8+
- 在脚本中获取操作人信息(`$bind.getOperatorById(...)`
9+
- 在脚本中调用 Spring Bean(`$bind.getBean(...)`
10+
- 需要替换脚本上下文的 Bean 工厂实现(如单元测试中注入 Mock)
11+
12+
## 如何引用
13+
14+
### Maven 坐标
15+
16+
```xml
17+
<dependency>
18+
<groupId>com.codingapi.flow</groupId>
19+
<artifactId>flow-engine-framework</artifactId>
20+
<version>0.0.26</version>
21+
</dependency>
22+
```
23+
24+
## API 说明
25+
26+
### 核心类
27+
28+
| 类名 | 包路径 | 说明 |
29+
|------|--------|------|
30+
| `FlowScriptContext` | `com.codingapi.flow.script.runtime` | 脚本运行时 `$bind` 上下文单例,持有 `IBeanFactory` 委托 |
31+
| `IBeanFactory` | `com.codingapi.flow.script.runtime` | Bean 工厂接口,定义 Bean 查询、记录查询、操作人查询方法 |
32+
| `GroovyScriptBind` | `com.codingapi.flow.script.request` | Groovy 脚本绑定对象,包装 `FlowScriptContext` 并作为 `$bind` 注入脚本 |
33+
34+
### 关键方法
35+
36+
#### FlowScriptContext
37+
38+
| 方法签名 | 参数说明 | 返回值 | 说明 |
39+
|----------|----------|--------|------|
40+
| `getInstance()` || `FlowScriptContext` | 获取单例实例 |
41+
| `setBeanFactory(IBeanFactory)` | `beanFactory` — Bean 工厂实现 | `void` | 设置自定义 Bean 工厂(由 Starter 自动配置或测试手动注入) |
42+
| `getBean(Class<T>)` | `clazz` — Bean 类型 | `T` | 按类型获取 Spring Bean |
43+
| `getBean(String, Class<T>)` | `name` — Bean 名称, `clazz` — Bean 类型 | `T` | 按名称和类型获取 Spring Bean |
44+
| `getBeans(Class<T>)` | `clazz` — Bean 类型 | `List<T>` | 按类型获取所有 Spring Bean |
45+
| `getRecordById(long)` | `id` — 记录 ID | `FlowRecord` | 根据 ID 获取流程记录 |
46+
| `getOperatorById(long)` | `userId` — 操作人 ID | `IFlowOperator` | 根据 ID 获取操作人 |
47+
| `findOperatorsByIds(List<Long>)` | `ids` — 操作人 ID 列表 | `List<IFlowOperator>` | 批量获取操作人 |
48+
49+
#### IBeanFactory 接口默认实现
50+
51+
| 方法 | 默认行为 | 说明 |
52+
|------|----------|------|
53+
| `getBean(Class<T>)` | 返回 `null` | 需由 Spring 环境实现覆盖 |
54+
| `getBean(String, Class<T>)` | 返回 `null` | 需由 Spring 环境实现覆盖 |
55+
| `getBeans(Class<T>)` | 返回 `null` | 需由 Spring 环境实现覆盖 |
56+
| `getRecordById(long)` | 返回 `null` | 需由 Spring 环境实现覆盖 |
57+
| `getOperatorById(long)` | 委托给 `GatewayContext` | 默认通过网关上下文获取操作人 |
58+
| `findOperatorsByIds(List<Long>)` | 委托给 `GatewayContext` | 默认通过网关上下文批量获取 |
59+
60+
## 使用示例
61+
62+
```groovy
63+
// 在 Groovy 脚本中使用 $bind(脚本由 ScriptRuntimeContext 自动注入)
64+
def run(request) {
65+
// 查询流程记录
66+
def record = $bind.getRecordById(request.getRecordId())
67+
68+
// 获取操作人信息
69+
def operator = $bind.getOperatorById(record.getCurrentOperatorId())
70+
71+
// 调用自定义 Spring Bean
72+
def notifyService = $bind.getBean(com.example.NotifyService)
73+
notifyService.send(operator.getEmail(), "您有一条待办")
74+
75+
return 'PASS'
76+
}
77+
```
78+
79+
```java
80+
// Spring Boot 自动配置中注册(flow-engine-starter 内部调用)
81+
@Component
82+
public class FlowScriptContextRegister implements InitializingBean {
83+
84+
private final ApplicationContext applicationContext;
85+
private final FlowOperatorGateway flowOperatorGateway;
86+
private final FlowRecordRepository flowRecordRepository;
87+
88+
@Override
89+
public void afterPropertiesSet() {
90+
FlowScriptContext.getInstance().setBeanFactory(new IBeanFactory() {
91+
@Override
92+
public <T> T getBean(Class<T> clazz) {
93+
return applicationContext.getBean(clazz);
94+
}
95+
@Override
96+
public <T> T getBean(String name, Class<T> clazz) {
97+
return applicationContext.getBean(name, clazz);
98+
}
99+
@Override
100+
public <T> List<T> getBeans(Class<T> clazz) {
101+
return new ArrayList<>(applicationContext.getBeansOfType(clazz).values());
102+
}
103+
@Override
104+
public FlowRecord getRecordById(long id) {
105+
return flowRecordRepository.getFlowRecord(id);
106+
}
107+
});
108+
}
109+
}
110+
```
111+
112+
```java
113+
// 单元测试中手动注入
114+
FlowScriptContext.getInstance().setBeanFactory(new IBeanFactory() {
115+
@Override
116+
public FlowRecord getRecordById(long id) {
117+
return testRecordRepository.getFlowRecord(id);
118+
}
119+
});
120+
```
121+
122+
## 注意事项
123+
124+
- **注入机制**`FlowScriptContext` 本身不执行查询逻辑,全部委托给 `IBeanFactory`。生产环境由 `FlowScriptContextRegister` 在 Spring 启动时自动注入,测试环境需手动设置
125+
- **`$bind` 绑定**`GroovyScriptBind` 包装 `FlowScriptContext` 实例,由 `ScriptRuntimeContext.execute()` 自动注入为脚本中的 `$bind` 变量,无需手动创建
126+
- **线程安全性**:单例使用饿汉式初始化,`setBeanFactory()` 无同步保护,应在应用启动阶段完成设置
127+
- **默认实现**`IBeanFactory` 的操作人查询方法默认委托给 `GatewayContext`,而 Bean 和记录查询默认返回 `null`,必须由 Spring 环境覆盖
128+
- **Groovy 脚本中的使用**:脚本内直接使用 `$bind` 关键字,无需 import 或显式获取实例

docs/components/GatewayContext.md

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# GatewayContext
2+
3+
## 何时使用
4+
5+
当需要在无法依赖 Spring 注入的框架层代码中查询流程操作人时使用。`GatewayContext` 是操作人网关的全局访问点,通过单例模式使 JPA 转换器、Groovy 脚本、领域对象等非 Spring 管理的代码也能获取操作人信息。典型场景:
6+
7+
- JPA `AttributeConverter` 中将数据库存储的用户 ID 反序列化为 `IFlowOperator`
8+
- 节点执行时解析流程创建人或操作人
9+
- 领域对象(如 `Workflow`)反序列化时重建操作人引用
10+
- Groovy 脚本默认的 `IBeanFactory` 委托操作人查询
11+
12+
## 如何引用
13+
14+
### Maven 坐标
15+
16+
```xml
17+
<dependency>
18+
<groupId>com.codingapi.flow</groupId>
19+
<artifactId>flow-engine-framework</artifactId>
20+
<version>0.0.26</version>
21+
</dependency>
22+
```
23+
24+
## API 说明
25+
26+
### 核心类
27+
28+
| 类名 | 包路径 | 说明 |
29+
|------|--------|------|
30+
| `GatewayContext` | `com.codingapi.flow.context` | 操作人网关单例,持有 `FlowOperatorGateway` 引用 |
31+
| `FlowOperatorGateway` | `com.codingapi.flow.gateway` | 操作人防腐层接口,定义操作人查询方法 |
32+
33+
### 关键方法
34+
35+
#### GatewayContext
36+
37+
| 方法签名 | 参数说明 | 返回值 | 说明 |
38+
|----------|----------|--------|------|
39+
| `getInstance()` || `GatewayContext` | 获取单例实例 |
40+
| `setFlowOperatorGateway(FlowOperatorGateway)` | `flowOperatorGateway` — 网关实现 | `void` | 设置操作人网关(由 Starter 自动配置调用) |
41+
| `getFlowOperatorGateway()` || `FlowOperatorGateway` | 获取当前网关实例 |
42+
| `getFlowOperator(long)` | `userId` — 操作人 ID | `IFlowOperator` | 根据 ID 获取操作人,委托给 `FlowOperatorGateway.get(long)` |
43+
| `findByIds(List<Long>)` | `ids` — 操作人 ID 列表 | `List<IFlowOperator>` | 批量获取操作人,委托给 `FlowOperatorGateway.findByIds(List)` |
44+
45+
#### FlowOperatorGateway 接口
46+
47+
| 方法签名 | 返回值 | 说明 |
48+
|----------|--------|------|
49+
| `get(long id)` | `IFlowOperator` | 根据 ID 查询单个操作人 |
50+
| `findByIds(List<Long> ids)` | `List<IFlowOperator>` | 根据 ID 列表批量查询操作人 |
51+
52+
## 使用示例
53+
54+
```java
55+
// 基础用法:查询操作人
56+
GatewayContext context = GatewayContext.getInstance();
57+
IFlowOperator operator = context.getFlowOperator(1001L);
58+
List<IFlowOperator> operators = context.findByIds(List.of(1001L, 1002L, 1003L));
59+
```
60+
61+
```java
62+
// Spring Boot 自动配置中注册(flow-engine-starter 内部调用)
63+
@Bean
64+
public GatewayContextRegister gatewayContextRegister(FlowOperatorGateway flowOperatorGateway) {
65+
return new GatewayContextRegister(flowOperatorGateway);
66+
}
67+
68+
// GatewayContextRegister 在 afterPropertiesSet() 中完成注册:
69+
// GatewayContext.getInstance().setFlowOperatorGateway(flowOperatorGateway);
70+
```
71+
72+
```java
73+
// 单元测试中手动注入
74+
@BeforeEach
75+
void setUp() {
76+
GatewayContext.getInstance().setFlowOperatorGateway(new FlowOperatorGateway() {
77+
@Override
78+
public IFlowOperator get(long id) {
79+
return new TestOperator(id, "TestUser");
80+
}
81+
@Override
82+
public List<IFlowOperator> findByIds(List<Long> ids) {
83+
return ids.stream().map(id -> new TestOperator(id, "User" + id)).toList();
84+
}
85+
});
86+
}
87+
```
88+
89+
```java
90+
// 实现 FlowOperatorGateway 对接企业用户系统
91+
@Component
92+
public class EnterpriseOperatorGateway implements FlowOperatorGateway {
93+
94+
private final UserService userService;
95+
96+
@Override
97+
public IFlowOperator get(long id) {
98+
User user = userService.findById(id);
99+
return new FlowOperatorAdapter(user);
100+
}
101+
102+
@Override
103+
public List<IFlowOperator> findByIds(List<Long> ids) {
104+
return userService.findByIds(ids).stream()
105+
.map(FlowOperatorAdapter::new)
106+
.toList();
107+
}
108+
}
109+
```
110+
111+
## 注意事项
112+
113+
- **注册时机**:`GatewayContextRegister` 必须在其他使用 `GatewayContext` 的 Bean 之前初始化。`RepositoryHolderContextRegister` 通过构造器依赖 `GatewayContextRegister` 来保证初始化顺序
114+
- **线程安全性**:单例使用饿汉式初始化,`setFlowOperatorGateway()` 无同步保护,应在应用启动阶段完成设置
115+
- **空指针风险**:在调用 `getFlowOperator()` 或 `findByIds()` 前必须确保 `flowOperatorGateway` 已设置,否则会抛出 `NullPointerException`
116+
- **防腐层设计**:`FlowOperatorGateway` 是防腐层接口,业务系统需提供其实现以桥接用户体系,框架本身不包含具体实现
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# GroovyScriptBind
2+
3+
## 何时使用
4+
5+
当需要在 Groovy 脚本中调用 Spring Bean、查询流程记录或查询操作人时,通过 `$bind` 变量使用。`GroovyScriptBind` 是脚本运行时注入的绑定对象,包装 `FlowScriptContext` 单例,使 Groovy 脚本能够访问应用层资源。典型场景:
6+
7+
- 在脚本中调用自定义 Spring Bean(如通知服务、审批规则服务)
8+
- 在脚本中查询历史流程记录用于条件判断
9+
- 在脚本中根据用户 ID 查询操作人详情
10+
11+
## 如何引用
12+
13+
### Maven 坐标
14+
15+
```xml
16+
<dependency>
17+
<groupId>com.codingapi.flow</groupId>
18+
<artifactId>flow-engine-framework</artifactId>
19+
<version>0.0.26</version>
20+
</dependency>
21+
```
22+
23+
## API 说明
24+
25+
### 核心类
26+
27+
| 类名 | 包路径 | 说明 |
28+
|------|--------|------|
29+
| `GroovyScriptBind` | `com.codingapi.flow.script.request` | `$bind` 脚本绑定对象,委托给 `FlowScriptContext` |
30+
| `FlowScriptContext` | `com.codingapi.flow.script.runtime` | 脚本上下文单例,持有 `IBeanFactory` 实现 |
31+
32+
### 关键方法
33+
34+
| 方法签名 | 参数说明 | 返回值 | 说明 |
35+
|----------|----------|--------|------|
36+
| `getBean(Class<T>)` | `clazz` — Bean 类型 | `T` | 按类型获取 Spring Bean |
37+
| `getBean(String, Class<T>)` | `name` — Bean 名称, `clazz` — Bean 类型 | `T` | 按名称和类型获取 Spring Bean |
38+
| `getBeans(Class<T>)` | `clazz` — Bean 类型 | `List<T>` | 按类型获取所有 Spring Bean |
39+
| `getRecordById(long)` | `id` — 记录 ID | `FlowRecord` | 根据 ID 获取流程记录 |
40+
| `getOperatorById(long)` | `userId` — 操作人 ID | `IFlowOperator` | 根据 ID 获取操作人 |
41+
| `findOperatorsByIds(List<Long>)` | `ids` — 操作人 ID 列表 | `List<IFlowOperator>` | 批量获取操作人 |
42+
43+
## 使用示例
44+
45+
```groovy
46+
// 在 Groovy 脚本中使用 $bind
47+
def run(request) {
48+
// 调用自定义 Spring Bean
49+
def notifyService = $bind.getBean(com.example.NotifyService)
50+
notifyService.send(request.getCurrentOperatorId(), "您有新的审批任务")
51+
52+
return 'PASS'
53+
}
54+
```
55+
56+
```groovy
57+
// 查询流程记录进行条件判断
58+
def run(request) {
59+
def relatedId = request.getFormData("relatedRecordId")
60+
if (relatedId != null) {
61+
def record = $bind.getRecordById(Long.parseLong(relatedId.toString()))
62+
if (record != null && record.getStatus() == 'APPROVED') {
63+
return true
64+
}
65+
}
66+
return false
67+
}
68+
```
69+
70+
```groovy
71+
// 查询操作人信息
72+
def run(request) {
73+
def operatorIds = request.getFormData("approverIds") as List
74+
def operators = $bind.findOperatorsByIds(operatorIds.collect { it as Long })
75+
return operators.collect { it.getUserId() }
76+
}
77+
```
78+
79+
```groovy
80+
// 结合 request 和 $bind 实现复杂逻辑
81+
def run(request) {
82+
def dept = request.getFormData("department")
83+
def ruleService = $bind.getBean(com.example.ApprovalRuleService)
84+
def approverIds = ruleService.getApprovers(dept)
85+
return approverIds
86+
}
87+
```
88+
89+
## 注意事项
90+
91+
- **注入方式**`$bind``ScriptRuntimeContext.execute()` 在每次脚本执行时自动注入,无需手动创建 `GroovyScriptBind` 实例
92+
- **`request` 配合使用**:脚本中同时可用 `request``GroovyScriptRequest`,读取流程上下文)和 `$bind`(本类,访问应用资源),两者互补
93+
- **委托机制**:所有方法调用最终委托给 `FlowScriptContext` 单例,实际行为取决于 `IBeanFactory` 的实现(由 Spring 自动配置注入)
94+
- **Bean 查询依赖 Spring**`getBean()` 系列方法仅在 Spring 环境中可用,单元测试需通过 `FlowScriptContext.setBeanFactory()` 注入 Mock
95+
- **返回值可能为 null**`getBean()` 在 Bean 不存在时返回 null,`getRecordById()` 在记录不存在时返回 null,脚本中需做空判断

0 commit comments

Comments
 (0)