Skip to content

Commit d4f873d

Browse files
authored
Merge pull request #44 from codingapi/feature/groovy-script-architecture
Feature/groovy script architecture
2 parents 01ff5c1 + 42cedc3 commit d4f873d

31 files changed

Lines changed: 1661 additions & 201 deletions

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,7 @@ build/
3434
*.db
3535

3636
### macOS ###
37-
.DS_Store
37+
.DS_Store
38+
39+
### Worktrees ###
40+
.worktrees/

CLAUDE.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,43 @@ flow-types (类型定义)
137137
- **与用户沟通及编写文档时,所有内容必须使用中文表述**
138138
- 前端包管理使用 pnpm(根据用户配置)
139139
- 前端文件命名规范:使用小写字母 + 下划线组合(如 `script_editor.tsx``variable_picker.tsx`
140+
- **前端导入规范**:使用 `@/` 路径别名导入项目内部模块,避免使用相对路径导入
140141
- 设计涉及流程或 UML 图形的解决方案时,使用 Mermaid Markdown 语法
141142
- 在编写计划的时候要遵循 TDD 的开发规范,务必要在方案中进行对实现代码逻辑的单元测试设计。
142143
- 在设计计划方案或执行方案过程中,对于代码的设计规划与调整修改要遵循本项目的代码风格和架构设计规则
143144
- 设计的计划要保存到本地的 `docs/` 目录下,每一个计划以时间+标题的方式命名创建文件夹,例如 `2026-02-26-xxxx`,文件夹下内容分为 `plan.md` 以及其他设计文件(如设计文件 xxx.pen 或其他设计内容信息)
144145

146+
```typescript
147+
// ✅ 正确:使用 @/ 路径别名
148+
import { GroovySyntaxConverter } from '@/components/design-editor/script/service/groovy-syntax-converter';
149+
import { ScriptType } from '@/components/design-editor/typings/groovy-script';
150+
151+
// ❌ 错误:避免使用相对路径
152+
import { GroovySyntaxConverter } from '../../../src/components/...';
153+
```
154+
155+
### 面向对象开发规范
156+
157+
除前端 **.tsx 组件** 以外的所有代码(Java 后端、TypeScript 非组件文件)均采用面向对象方式开发:
158+
159+
- **Service 层**:使用 class 定义,通过依赖注入或单例模式使用
160+
- **工具类**:使用 class 定义,提供实例方法或静态方法
161+
- **适配器/转换器**:使用 class 实现,支持扩展
162+
163+
```typescript
164+
// ✅ 正确:使用 class 定义 Service
165+
export class GroovySyntaxConverter {
166+
private adapters: Map<ScriptType, ScriptAdapter> = new Map();
167+
168+
public registerAdapter(adapter: ScriptAdapter): void {
169+
this.adapters.set(adapter.scriptType, adapter);
170+
}
171+
172+
public toScript(...): string { ... }
173+
}
174+
175+
// ❌ 错误:避免直接使用函数导出
176+
export function toScript(...): string { ... }
177+
export const toScript = (...): string => { ... };
178+
```
179+

flow-engine-framework/src/main/java/com/codingapi/flow/script/node/ConditionScript.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.codingapi.flow.script.node;
22

3+
import com.codingapi.flow.script.request.ConditionGroovyRequest;
34
import com.codingapi.flow.script.runtime.ScriptRuntimeContext;
45
import com.codingapi.flow.session.FlowSession;
56
import lombok.AllArgsConstructor;
@@ -8,12 +9,13 @@
89
@AllArgsConstructor
910
public class ConditionScript {
1011

11-
public static final String SCRIPT_DEFAULT = "def run(session){return true}";
12+
public static final String SCRIPT_DEFAULT = "def run(request){return true}";
1213

1314
@Getter
1415
private final String script;
1516

16-
public boolean execute(FlowSession request) {
17+
public boolean execute(FlowSession session) {
18+
ConditionGroovyRequest request = new ConditionGroovyRequest(session);
1719
return ScriptRuntimeContext.getInstance().run(script, Boolean.class, request);
1820
}
1921

flow-engine-framework/src/main/java/com/codingapi/flow/script/node/NodeTitleScript.java

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.codingapi.flow.script.node;
22

33
import com.codingapi.flow.script.runtime.ScriptRuntimeContext;
4-
import com.codingapi.flow.script.runtime.TitleGroovyRequest;
4+
import com.codingapi.flow.script.request.TitleGroovyRequest;
55
import com.codingapi.flow.session.FlowSession;
66
import lombok.AllArgsConstructor;
77
import lombok.Getter;
@@ -18,16 +18,7 @@ public class NodeTitleScript {
1818
private final String script;
1919

2020
public String execute(FlowSession session) {
21-
TitleGroovyRequest request = session.createTitleRequest();
22-
return execute(request);
23-
}
24-
25-
/**
26-
* 执行标题脚本(直接使用TitleGroovyRequest)
27-
* @param request 标题请求对象
28-
* @return 标题字符串
29-
*/
30-
public String execute(TitleGroovyRequest request) {
21+
TitleGroovyRequest request = new TitleGroovyRequest(session);
3122
return ScriptRuntimeContext.getInstance().run(script, String.class, request);
3223
}
3324

flow-engine-framework/src/main/java/com/codingapi/flow/script/node/OperatorLoadScript.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.codingapi.flow.script.node;
22

33
import com.codingapi.flow.operator.IFlowOperator;
4+
import com.codingapi.flow.script.request.OperatorLoadGroovyRequest;
45
import com.codingapi.flow.script.runtime.ScriptRuntimeContext;
56
import com.codingapi.flow.session.FlowSession;
67
import lombok.AllArgsConstructor;
@@ -20,7 +21,8 @@ public class OperatorLoadScript {
2021
private final String script;
2122

2223
@SuppressWarnings("unchecked")
23-
public List<IFlowOperator> execute(FlowSession request) {
24+
public List<IFlowOperator> execute(FlowSession session) {
25+
OperatorLoadGroovyRequest request = new OperatorLoadGroovyRequest(session);
2426
return ScriptRuntimeContext.getInstance().run(script, List.class, request);
2527
}
2628

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package com.codingapi.flow.script.request;
2+
3+
import com.codingapi.flow.session.FlowSession;
4+
import lombok.Getter;
5+
import lombok.Setter;
6+
7+
import java.util.Map;
8+
9+
/**
10+
* Groovy脚本请求对象抽象基类
11+
* 从FlowSession中提取数据供脚本使用
12+
*/
13+
@Getter
14+
@Setter
15+
public abstract class BaseGroovyRequest {
16+
17+
/**
18+
* 当前操作人姓名
19+
*/
20+
protected String operatorName;
21+
22+
/**
23+
* 当前操作人ID
24+
*/
25+
protected Integer operatorId;
26+
27+
/**
28+
* 是否流程管理员
29+
*/
30+
protected Boolean isFlowManager;
31+
32+
/**
33+
* 流程标题
34+
*/
35+
protected String workflowTitle;
36+
37+
/**
38+
* 流程编码
39+
*/
40+
protected String workflowCode;
41+
42+
/**
43+
* 流程编号
44+
*/
45+
protected String workCode;
46+
47+
/**
48+
* 流程创建人姓名
49+
*/
50+
protected String creatorName;
51+
52+
/**
53+
* 表单字段值
54+
*/
55+
protected Map<String, Object> formData;
56+
57+
/**
58+
* 从FlowSession构建请求对象(模板方法模式)
59+
* @param session 流程会话(不能为null)
60+
*/
61+
public BaseGroovyRequest(FlowSession session) {
62+
// 提取操作人信息
63+
if (session.getCurrentOperator() != null) {
64+
this.operatorName = session.getCurrentOperator().getName();
65+
this.operatorId = (int) session.getCurrentOperator().getUserId();
66+
this.isFlowManager = session.getCurrentOperator().isFlowManager();
67+
}
68+
69+
// 提取流程信息
70+
if (session.getWorkflow() != null) {
71+
this.workflowTitle = session.getWorkflow().getTitle();
72+
this.workflowCode = session.getWorkflow().getCode();
73+
if (session.getWorkflow().getCreatedOperator() != null) {
74+
this.creatorName = session.getWorkflow().getCreatedOperator().getName();
75+
}
76+
}
77+
78+
// 提取表单数据
79+
if (session.getFormData() != null) {
80+
this.formData = session.getFormData().toMapData();
81+
}
82+
}
83+
84+
/**
85+
* 获取表单字段值(Groovy脚本调用)
86+
* @param key 字段key
87+
* @return 字段值
88+
*/
89+
public Object getFormData(String key) {
90+
if (formData == null) {
91+
return null;
92+
}
93+
return formData.get(key);
94+
}
95+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.codingapi.flow.script.request;
2+
3+
import com.codingapi.flow.session.FlowSession;
4+
import lombok.Getter;
5+
import lombok.Setter;
6+
7+
/**
8+
* 条件表达式Groovy脚本请求对象
9+
* 提供给ConditionScript使用的上下文数据
10+
* 继承BaseGroovyRequest,从FlowSession自动提取通用数据
11+
*/
12+
@Getter
13+
@Setter
14+
public class ConditionGroovyRequest extends BaseGroovyRequest {
15+
16+
/**
17+
* 当前节点名称
18+
*/
19+
private String nodeName;
20+
21+
/**
22+
* 从FlowSession构建ConditionGroovyRequest
23+
* @param session 流程会话(不能为null)
24+
*/
25+
public ConditionGroovyRequest(FlowSession session) {
26+
super(session);
27+
// 提取节点信息
28+
if (session.getCurrentNode() != null) {
29+
this.nodeName = session.getCurrentNode().getName();
30+
}
31+
}
32+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.codingapi.flow.script.request;
2+
3+
import com.codingapi.flow.operator.IFlowOperator;
4+
import com.codingapi.flow.session.FlowSession;
5+
import lombok.Getter;
6+
import lombok.Setter;
7+
8+
/**
9+
* 人员加载Groovy脚本请求对象
10+
* 提供给OperatorLoadScript使用的上下文数据
11+
* 继承BaseGroovyRequest,从FlowSession自动提取通用数据
12+
*/
13+
@Getter
14+
@Setter
15+
public class OperatorLoadGroovyRequest extends BaseGroovyRequest {
16+
17+
/**
18+
* 流程创建人
19+
*/
20+
private IFlowOperator createdOperator;
21+
22+
/**
23+
* 从FlowSession构建OperatorLoadGroovyRequest
24+
* @param session 流程会话(不能为null)
25+
*/
26+
public OperatorLoadGroovyRequest(FlowSession session) {
27+
super(session);
28+
// 提取创建人信息
29+
if (session.getWorkflow() != null && session.getWorkflow().getCreatedOperator() != null) {
30+
this.createdOperator = session.getWorkflow().getCreatedOperator();
31+
}
32+
}
33+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.codingapi.flow.script.request;
2+
3+
import com.codingapi.flow.session.FlowSession;
4+
import lombok.Getter;
5+
import lombok.Setter;
6+
7+
/**
8+
* 标题表达式Groovy脚本请求对象
9+
* 提供给NodeTitleScript使用的上下文数据
10+
* 继承BaseGroovyRequest,从FlowSession自动提取通用数据
11+
*/
12+
@Getter
13+
@Setter
14+
public class TitleGroovyRequest extends BaseGroovyRequest {
15+
16+
/**
17+
* 当前节点名称
18+
*/
19+
private String nodeName;
20+
21+
/**
22+
* 当前节点类型
23+
*/
24+
private String nodeType;
25+
26+
/**
27+
* 从FlowSession构建TitleGroovyRequest
28+
* @param session 流程会话
29+
*/
30+
public TitleGroovyRequest(FlowSession session) {
31+
super(session);
32+
// 提取节点信息
33+
if (session != null && session.getCurrentNode() != null) {
34+
this.nodeName = session.getCurrentNode().getName();
35+
this.nodeType = session.getCurrentNode().getType();
36+
}
37+
// 提取流程编号(从record获取)
38+
if (session != null && session.getCurrentRecord() != null) {
39+
this.workCode = session.getCurrentRecord().getWorkCode();
40+
}
41+
}
42+
}

0 commit comments

Comments
 (0)