Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,7 @@ build/
*.db

### macOS ###
.DS_Store
.DS_Store

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

```typescript
// ✅ 正确:使用 @/ 路径别名
import { GroovySyntaxConverter } from '@/components/design-editor/script/service/groovy-syntax-converter';
import { ScriptType } from '@/components/design-editor/typings/groovy-script';

// ❌ 错误:避免使用相对路径
import { GroovySyntaxConverter } from '../../../src/components/...';
```

### 面向对象开发规范

除前端 **.tsx 组件** 以外的所有代码(Java 后端、TypeScript 非组件文件)均采用面向对象方式开发:

- **Service 层**:使用 class 定义,通过依赖注入或单例模式使用
- **工具类**:使用 class 定义,提供实例方法或静态方法
- **适配器/转换器**:使用 class 实现,支持扩展

```typescript
// ✅ 正确:使用 class 定义 Service
export class GroovySyntaxConverter {
private adapters: Map<ScriptType, ScriptAdapter> = new Map();

public registerAdapter(adapter: ScriptAdapter): void {
this.adapters.set(adapter.scriptType, adapter);
}

public toScript(...): string { ... }
}

// ❌ 错误:避免直接使用函数导出
export function toScript(...): string { ... }
export const toScript = (...): string => { ... };
```

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.codingapi.flow.script.node;

import com.codingapi.flow.script.request.ConditionGroovyRequest;
import com.codingapi.flow.script.runtime.ScriptRuntimeContext;
import com.codingapi.flow.session.FlowSession;
import lombok.AllArgsConstructor;
Expand All @@ -8,12 +9,13 @@
@AllArgsConstructor
public class ConditionScript {

public static final String SCRIPT_DEFAULT = "def run(session){return true}";
public static final String SCRIPT_DEFAULT = "def run(request){return true}";

@Getter
private final String script;

public boolean execute(FlowSession request) {
public boolean execute(FlowSession session) {
ConditionGroovyRequest request = new ConditionGroovyRequest(session);
return ScriptRuntimeContext.getInstance().run(script, Boolean.class, request);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.codingapi.flow.script.node;

import com.codingapi.flow.script.runtime.ScriptRuntimeContext;
import com.codingapi.flow.script.runtime.TitleGroovyRequest;
import com.codingapi.flow.script.request.TitleGroovyRequest;
import com.codingapi.flow.session.FlowSession;
import lombok.AllArgsConstructor;
import lombok.Getter;
Expand All @@ -18,16 +18,7 @@ public class NodeTitleScript {
private final String script;

public String execute(FlowSession session) {
TitleGroovyRequest request = session.createTitleRequest();
return execute(request);
}

/**
* 执行标题脚本(直接使用TitleGroovyRequest)
* @param request 标题请求对象
* @return 标题字符串
*/
public String execute(TitleGroovyRequest request) {
TitleGroovyRequest request = new TitleGroovyRequest(session);
return ScriptRuntimeContext.getInstance().run(script, String.class, request);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.codingapi.flow.script.node;

import com.codingapi.flow.operator.IFlowOperator;
import com.codingapi.flow.script.request.OperatorLoadGroovyRequest;
import com.codingapi.flow.script.runtime.ScriptRuntimeContext;
import com.codingapi.flow.session.FlowSession;
import lombok.AllArgsConstructor;
Expand All @@ -20,7 +21,8 @@ public class OperatorLoadScript {
private final String script;

@SuppressWarnings("unchecked")
public List<IFlowOperator> execute(FlowSession request) {
public List<IFlowOperator> execute(FlowSession session) {
OperatorLoadGroovyRequest request = new OperatorLoadGroovyRequest(session);
return ScriptRuntimeContext.getInstance().run(script, List.class, request);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package com.codingapi.flow.script.request;

import com.codingapi.flow.session.FlowSession;
import lombok.Getter;
import lombok.Setter;

import java.util.Map;

/**
* Groovy脚本请求对象抽象基类
* 从FlowSession中提取数据供脚本使用
*/
@Getter
@Setter
public abstract class BaseGroovyRequest {

/**
* 当前操作人姓名
*/
protected String operatorName;

/**
* 当前操作人ID
*/
protected Integer operatorId;

/**
* 是否流程管理员
*/
protected Boolean isFlowManager;

/**
* 流程标题
*/
protected String workflowTitle;

/**
* 流程编码
*/
protected String workflowCode;

/**
* 流程编号
*/
protected String workCode;

/**
* 流程创建人姓名
*/
protected String creatorName;

/**
* 表单字段值
*/
protected Map<String, Object> formData;

/**
* 从FlowSession构建请求对象(模板方法模式)
* @param session 流程会话(不能为null)
*/
public BaseGroovyRequest(FlowSession session) {
// 提取操作人信息
if (session.getCurrentOperator() != null) {
this.operatorName = session.getCurrentOperator().getName();
this.operatorId = (int) session.getCurrentOperator().getUserId();
this.isFlowManager = session.getCurrentOperator().isFlowManager();
}

// 提取流程信息
if (session.getWorkflow() != null) {
this.workflowTitle = session.getWorkflow().getTitle();
this.workflowCode = session.getWorkflow().getCode();
if (session.getWorkflow().getCreatedOperator() != null) {
this.creatorName = session.getWorkflow().getCreatedOperator().getName();
}
}

// 提取表单数据
if (session.getFormData() != null) {
this.formData = session.getFormData().toMapData();
}
}

/**
* 获取表单字段值(Groovy脚本调用)
* @param key 字段key
* @return 字段值
*/
public Object getFormData(String key) {
if (formData == null) {
return null;
}
return formData.get(key);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.codingapi.flow.script.request;

import com.codingapi.flow.session.FlowSession;
import lombok.Getter;
import lombok.Setter;

/**
* 条件表达式Groovy脚本请求对象
* 提供给ConditionScript使用的上下文数据
* 继承BaseGroovyRequest,从FlowSession自动提取通用数据
*/
@Getter
@Setter
public class ConditionGroovyRequest extends BaseGroovyRequest {

/**
* 当前节点名称
*/
private String nodeName;

/**
* 从FlowSession构建ConditionGroovyRequest
* @param session 流程会话(不能为null)
*/
public ConditionGroovyRequest(FlowSession session) {
super(session);
// 提取节点信息
if (session.getCurrentNode() != null) {
this.nodeName = session.getCurrentNode().getName();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.codingapi.flow.script.request;

import com.codingapi.flow.operator.IFlowOperator;
import com.codingapi.flow.session.FlowSession;
import lombok.Getter;
import lombok.Setter;

/**
* 人员加载Groovy脚本请求对象
* 提供给OperatorLoadScript使用的上下文数据
* 继承BaseGroovyRequest,从FlowSession自动提取通用数据
*/
@Getter
@Setter
public class OperatorLoadGroovyRequest extends BaseGroovyRequest {

/**
* 流程创建人
*/
private IFlowOperator createdOperator;

/**
* 从FlowSession构建OperatorLoadGroovyRequest
* @param session 流程会话(不能为null)
*/
public OperatorLoadGroovyRequest(FlowSession session) {
super(session);
// 提取创建人信息
if (session.getWorkflow() != null && session.getWorkflow().getCreatedOperator() != null) {
this.createdOperator = session.getWorkflow().getCreatedOperator();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.codingapi.flow.script.request;

import com.codingapi.flow.session.FlowSession;
import lombok.Getter;
import lombok.Setter;

/**
* 标题表达式Groovy脚本请求对象
* 提供给NodeTitleScript使用的上下文数据
* 继承BaseGroovyRequest,从FlowSession自动提取通用数据
*/
@Getter
@Setter
public class TitleGroovyRequest extends BaseGroovyRequest {

/**
* 当前节点名称
*/
private String nodeName;

/**
* 当前节点类型
*/
private String nodeType;

/**
* 从FlowSession构建TitleGroovyRequest
* @param session 流程会话
*/
public TitleGroovyRequest(FlowSession session) {
super(session);
// 提取节点信息
if (session != null && session.getCurrentNode() != null) {
this.nodeName = session.getCurrentNode().getName();
this.nodeType = session.getCurrentNode().getType();
}
// 提取流程编号(从record获取)
if (session != null && session.getCurrentRecord() != null) {
this.workCode = session.getCurrentRecord().getWorkCode();
}
}
}
Loading