19 KiB
19 KiB
开发规范文档
版本: v1.0
目的: 确保代码质量、可维护性、团队协作效率
一、代码规范
1.1 Java 代码规范
命名规范
// ✅ 正确
public class WorkflowService {} // 类名:大驼峰
public interface WorkflowNode {} // 接口名:大驼峰
public enum ExecutionStatus {} // 枚举:大驼峰
private String userId; // 变量:小驼峰
public static final int MAX_RETRY = 3; // 常量:大写下划线
public void executeWorkflow() {} // 方法:小驼峰
// ❌ 错误
public class workflow_service {} // 不要用下划线
private String UserId; // 变量不要大驼峰
public static final int maxRetry = 3; // 常量不要小驼峰
包结构
com.workflow
├── controller # REST API控制器
│ ├── WorkflowController.java
│ └── NodeTypeController.java
├── service # 业务逻辑
│ ├── WorkflowService.java
│ └── ExecutionService.java
├── repository # 数据访问
│ └── WorkflowRepository.java
├── model # 数据模型
│ ├── entity/ # 实体类(对应数据库表)
│ ├── dto/ # 数据传输对象
│ └── vo/ # 视图对象
├── engine # 核心引擎
│ ├── ExpressionEngine.java
│ └── WorkflowConverter.java
├── nodes # 节点实现
│ ├── WorkflowNode.java
│ ├── HttpRequestNode.java
│ └── SendEmailNode.java
├── registry # 注册中心
│ └── NodeTypeRegistry.java
├── executor # 执行器
│ └── GenericNodeExecutor.java
├── exception # 自定义异常
│ └── WorkflowException.java
└── config # 配置类
└── FlowableConfig.java
注释规范
/**
* 工作流服务
*
* 职责:
* 1. 工作流的创建、更新、删除
* 2. 工作流的执行
* 3. 工作流定义的转换(JSON → BPMN)
*
* @author zhangsan
* @since 1.0.0
*/
@Service
public class WorkflowService {
/**
* 执行工作流
*
* @param workflowId 工作流ID
* @param input 输入参数
* @return 执行结果
* @throws WorkflowNotFoundException 工作流不存在
* @throws WorkflowExecutionException 执行失败
*/
public WorkflowExecutionResult execute(String workflowId, Map<String, Object> input) {
// 1. 验证工作流是否存在
WorkflowDefinition workflow = workflowRepository.findById(workflowId)
.orElseThrow(() -> new WorkflowNotFoundException(workflowId));
// 2. 准备执行上下文
Map<String, Object> variables = prepareVariables(workflow, input);
// 3. 启动Flowable流程
ProcessInstance instance = runtimeService.startProcessInstanceById(
workflow.getFlowableProcessDefinitionId(),
variables
);
// 4. 返回执行结果
return WorkflowExecutionResult.builder()
.executionId(instance.getId())
.status("running")
.build();
}
}
注释要求:
- 所有
public方法必须有 JavaDoc - 复杂逻辑必须有行内注释(中文)
- TODO/FIXME 注释必须标注负责人和日期
// TODO(zhangsan, 2024-01-15): 优化表达式缓存策略
// FIXME(lisi, 2024-01-16): 修复循环依赖检测bug
异常处理
// ✅ 正确:具体的异常类型
public WorkflowDefinition getById(String id) {
return workflowRepository.findById(id)
.orElseThrow(() -> new WorkflowNotFoundException("工作流不存在: " + id));
}
// ✅ 正确:记录日志
public void execute(String workflowId, Map<String, Object> input) {
try {
// 执行逻辑
doExecute(workflowId, input);
} catch (Exception e) {
log.error("工作流执行失败: workflowId={}, error={}", workflowId, e.getMessage(), e);
throw new WorkflowExecutionException("执行失败", e);
}
}
// ❌ 错误:吞掉异常
try {
doSomething();
} catch (Exception e) {
// 什么都不做
}
// ❌ 错误:抛出原始 Exception
public void doSomething() throws Exception {
// ...
}
日志规范
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Service
public class WorkflowService {
private static final Logger log = LoggerFactory.getLogger(WorkflowService.class);
public void execute(String workflowId, Map<String, Object> input) {
// INFO: 重要的业务操作
log.info("开始执行工作流: workflowId={}, input={}", workflowId, input);
try {
// DEBUG: 调试信息
log.debug("准备执行上下文: variables={}", variables);
// ... 执行逻辑
log.info("工作流执行成功: workflowId={}, executionId={}", workflowId, executionId);
} catch (Exception e) {
// ERROR: 错误信息(必须包含异常栈)
log.error("工作流执行失败: workflowId={}", workflowId, e);
throw e;
}
}
}
日志级别:
- ERROR: 错误,需要立即处理
- WARN: 警告,可能有问题但不影响运行
- INFO: 重要的业务操作(创建、更新、删除、执行)
- DEBUG: 调试信息(开发环境)
1.2 TypeScript/React 代码规范
命名规范
// ✅ 正确
interface WorkflowDefinition {} // 接口:大驼峰
type NodeType = 'http' | 'email'; // 类型:大驼峰
enum ExecutionStatus {} // 枚举:大驼峰
const userId = '123'; // 变量:小驼峰
const MAX_RETRY = 3; // 常量:大写下划线
function executeWorkflow() {} // 函数:小驼峰
// 组件:大驼峰
export default function WorkflowEditor() {}
const CustomNode: React.FC<Props> = () => {}
文件命名
src/
├── components/
│ ├── WorkflowEditor/ # 组件文件夹:大驼峰
│ │ ├── index.tsx # 主文件:小写
│ │ ├── Canvas.tsx # 子组件:大驼峰
│ │ └── WorkflowEditor.css # 样式:大驼峰
│ └── NodeConfigPanel/
│ └── index.tsx
├── api/
│ └── workflow.ts # API文件:小写
├── types/
│ └── workflow.ts # 类型文件:小写
└── utils/
└── expressionParser.ts # 工具文件:小驼峰
TypeScript 类型定义
// ✅ 正确:明确的类型
interface WorkflowNode {
id: string;
type: string;
name: string;
position: { x: number; y: number };
config: Record<string, any>; // 如果确实是任意类型
}
// ✅ 正确:使用泛型
interface ApiResponse<T> {
code: number;
message: string;
data: T;
}
// ❌ 错误:滥用 any
function doSomething(data: any): any {
return data;
}
// ✅ 正确:使用 unknown 或具体类型
function doSomething(data: unknown): string {
if (typeof data === 'string') {
return data;
}
return JSON.stringify(data);
}
React 组件规范
import React, { useState, useEffect, useCallback, memo } from 'react';
/**
* 节点配置面板
*
* @param node - 当前选中的节点
* @param onConfigChange - 配置变化回调
*/
interface Props {
node: Node | null;
onConfigChange: (nodeId: string, config: any) => void;
}
export default function NodeConfigPanel({ node, onConfigChange }: Props) {
// 1. hooks 放在最前面
const [form] = Form.useForm();
const [loading, setLoading] = useState(false);
// 2. useEffect
useEffect(() => {
if (node) {
form.setFieldsValue(node.data.config);
}
}, [node, form]);
// 3. 事件处理函数(使用 useCallback 优化)
const handleValuesChange = useCallback(
(changedValues: any, allValues: any) => {
if (node) {
onConfigChange(node.id, allValues);
}
},
[node, onConfigChange]
);
// 4. 条件渲染
if (!node) {
return <Empty description="请选择一个节点" />;
}
// 5. 主要渲染
return (
<div className="node-config-panel">
<Form
form={form}
onValuesChange={handleValuesChange}
>
{/* ... */}
</Form>
</div>
);
}
React 最佳实践:
// ✅ 使用 memo 优化性能
const CustomNode = memo(({ data }: Props) => {
return <div>{data.name}</div>;
});
// ✅ 使用 useMemo 缓存计算结果
const upstreamNodes = useMemo(() => {
return edges
.filter(edge => edge.target === node.id)
.map(edge => nodes.find(n => n.id === edge.source));
}, [node.id, nodes, edges]);
// ✅ 使用 useCallback 缓存函数
const handleClick = useCallback(() => {
console.log(node.id);
}, [node.id]);
// ❌ 在渲染中创建新对象/函数(每次重新渲染)
return (
<div onClick={() => console.log(node.id)}> {/* 每次都创建新函数 */}
<Component style={{ color: 'red' }} /> {/* 每次都创建新对象 */}
</div>
);
二、Git 工作流
2.1 分支策略
main/master # 生产环境(只能通过PR合并)
↑
develop # 开发环境(默认分支)
↑
feature/xxx # 功能分支
↑
hotfix/xxx # 紧急修复分支
2.2 分支命名规范
# 功能分支
feature/workflow-editor # 新功能
feature/node-http-request # 新节点类型
# 修复分支
bugfix/expression-parser-null # Bug修复
# 紧急修复
hotfix/security-vulnerability # 安全漏洞
# 重构
refactor/simplify-converter # 重构
2.3 Commit 规范
格式:<type>(<scope>): <subject>
# ✅ 正确示例
feat(editor): 添加字段映射选择器
fix(expression): 修复表达式解析空指针异常
docs(readme): 更新部署文档
style(format): 格式化代码
refactor(converter): 简化BPMN转换逻辑
perf(cache): 优化表达式缓存策略
test(service): 添加工作流服务单元测试
chore(deps): 升级Spring Boot到3.2.1
# ❌ 错误示例
update code # 太模糊
修复bug # 用中文
fix: fixed a bug # 重复
Type 类型:
feat: 新功能fix: Bug修复docs: 文档更新style: 代码格式(不影响代码运行)refactor: 重构perf: 性能优化test: 测试chore: 构建/工具链
2.4 PR (Pull Request) 规范
标题:
feat(editor): 实现字段映射选择器
fix(#123): 修复表达式解析bug
描述模板:
## 变更内容
简要描述本次PR的变更内容
## 变更类型
- [ ] 新功能
- [x] Bug修复
- [ ] 文档更新
- [ ] 重构
- [ ] 其他
## 相关Issue
Closes #123
## 测试
- [x] 单元测试已通过
- [x] 手动测试已完成
- [ ] 需要补充测试
## 截图(如果有UI变化)


## Checklist
- [x] 代码已自测
- [x] 已添加/更新测试
- [x] 已更新文档
- [x] 代码符合规范
三、测试规范
3.1 单元测试
覆盖率要求:
- 核心业务逻辑:>80%
- 工具类:>90%
- Controller:>60%
命名规范:
// 测试类:ClassName + Test
public class WorkflowServiceTest {}
// 测试方法:should_ExpectedBehavior_When_Condition
@Test
public void should_ThrowException_When_WorkflowNotFound() {}
@Test
public void should_ReturnSuccess_When_WorkflowExecuted() {}
示例:
@SpringBootTest
public class ExpressionEngineTest {
@Autowired
private ExpressionEngine expressionEngine;
@Mock
private DelegateExecution execution;
@BeforeEach
public void setUp() {
// 准备测试数据
Map<String, Object> nodes = Map.of(
"node1", Map.of(
"output", Map.of("email", "test@example.com")
)
);
when(execution.getVariable("nodes")).thenReturn(nodes);
}
@Test
public void should_ResolveExpression_When_ValidExpression() {
// Given
String expression = "${nodes.node1.output.email}";
// When
String result = (String) expressionEngine.evaluate(expression, execution);
// Then
assertEquals("test@example.com", result);
}
@Test
public void should_ThrowException_When_InvalidExpression() {
// Given
String expression = "${invalid.path}";
// When & Then
assertThrows(ExpressionEvaluationException.class, () -> {
expressionEngine.evaluate(expression, execution);
});
}
}
3.2 集成测试
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
public class WorkflowControllerIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@Test
public void should_CreateWorkflow_When_ValidRequest() throws Exception {
// Given
WorkflowDefinition workflow = WorkflowDefinition.builder()
.name("Test Workflow")
.build();
// When & Then
mockMvc.perform(post("/api/workflows")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(workflow)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").exists())
.andExpect(jsonPath("$.name").value("Test Workflow"));
}
}
3.3 前端测试
import { render, screen, fireEvent } from '@testing-library/react';
import FieldMappingSelector from './FieldMappingSelector';
describe('FieldMappingSelector', () => {
it('应该显示上游节点的字段', () => {
const upstreamNodes = [
{
id: 'node1',
data: { type: 'http_request', name: 'HTTP' },
},
];
render(
<FieldMappingSelector
upstreamNodes={upstreamNodes}
nodeTypes={mockNodeTypes}
/>
);
expect(screen.getByText('HTTP')).toBeInTheDocument();
});
it('应该生成正确的表达式', () => {
const onChange = jest.fn();
render(
<FieldMappingSelector
upstreamNodes={upstreamNodes}
onChange={onChange}
/>
);
// 选择字段
fireEvent.change(screen.getByRole('combobox'), {
target: { value: 'nodes.node1.output.email' },
});
// 验证
expect(onChange).toHaveBeenCalledWith('${nodes.node1.output.email}');
});
});
四、代码审查(Code Review)
4.1 审查清单
功能性:
- 代码实现了需求
- 边界情况已处理
- 错误处理完善
代码质量:
- 命名清晰易懂
- 逻辑简洁
- 没有重复代码
- 注释充分
性能:
- 没有明显的性能问题
- 数据库查询优化
- 缓存使用合理
安全:
- 输入验证
- SQL注入防护
- XSS防护
测试:
- 有单元测试
- 测试覆盖核心逻辑
- 测试通过
4.2 审查注释规范
// ✅ 建设性建议
// 建议:这里可以使用Optional避免空指针
if (user != null) {
return user.getName();
}
// 可以改为:
return Optional.ofNullable(user)
.map(User::getName)
.orElse("Unknown");
// ✅ 指出问题
// 问题:这里有SQL注入风险
String sql = "SELECT * FROM users WHERE name = '" + userName + "'";
// 应该使用:
String sql = "SELECT * FROM users WHERE name = ?";
// ❌ 不要这样
// 这代码写得太烂了
五、文档规范
5.1 README.md
# 工作流平台
## 快速开始
### 环境要求
- JDK 17+
- Node.js 18+
- PostgreSQL 15+
- Redis 7+
### 本地开发
1. 克隆代码
\`\`\`bash
git clone https://github.com/yourorg/workflow-platform.git
cd workflow-platform
\`\`\`
2. 启动后端
\`\`\`bash
cd backend
./mvnw spring-boot:run
\`\`\`
3. 启动前端
\`\`\`bash
cd frontend
npm install
npm run dev
\`\`\`
4. 访问
http://localhost:3000
## 项目结构
...
## 开发文档
- [架构总览](docs/01-架构总览.md)
- [后端技术设计](docs/02-后端技术设计.md)
- [前端技术设计](docs/03-前端技术设计.md)
5.2 API 文档
使用 Swagger/OpenAPI:
@RestController
@RequestMapping("/api/workflows")
@Tag(name = "工作流管理", description = "工作流的创建、更新、删除、执行")
public class WorkflowController {
@PostMapping
@Operation(summary = "创建工作流", description = "创建一个新的工作流定义")
@ApiResponse(responseCode = "200", description = "创建成功")
@ApiResponse(responseCode = "400", description = "参数错误")
public ResponseEntity<WorkflowDefinition> createWorkflow(
@RequestBody
@io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "工作流定义",
required = true
)
WorkflowDefinition workflow
) {
// ...
}
}
访问:http://localhost:8080/swagger-ui.html
六、部署规范
6.1 环境配置
# .env.example(提交到代码库)
SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/workflow_db
SPRING_DATASOURCE_USERNAME=postgres
SPRING_DATASOURCE_PASSWORD=
REDIS_HOST=localhost
REDIS_PORT=6379
SMTP_HOST=smtp.example.com
SMTP_PASSWORD=
# .env(不提交,本地使用)
SPRING_DATASOURCE_PASSWORD=your_password
SMTP_PASSWORD=your_smtp_password
6.2 版本号
使用语义化版本:MAJOR.MINOR.PATCH
1.0.0 - 第一个正式版本
1.1.0 - 新增功能(向后兼容)
1.1.1 - Bug修复
2.0.0 - 重大变更(不向后兼容)
6.3 发布流程
# 1. 更新版本号
# pom.xml: <version>1.1.0</version>
# package.json: "version": "1.1.0"
# 2. 更新 CHANGELOG.md
git add CHANGELOG.md
git commit -m "docs: 更新版本到 1.1.0"
# 3. 打标签
git tag -a v1.1.0 -m "Release v1.1.0"
git push origin v1.1.0
# 4. 构建
docker build -t workflow-platform:1.1.0 .
# 5. 部署
kubectl apply -f k8s/deployment.yaml
七、故障排查
7.1 日志级别配置
# application-dev.yml
logging:
level:
root: INFO
com.workflow: DEBUG
org.flowable: DEBUG
# application-prod.yml
logging:
level:
root: WARN
com.workflow: INFO
org.flowable: WARN
7.2 常见问题
问题1:表达式解析失败
错误:ExpressionEvaluationException: 表达式解析失败
原因:上游节点输出结构与预期不符
解决:检查 outputSchema 定义,查看实际输出
问题2:工作流执行卡住
错误:流程实例一直处于 running 状态
原因:某个节点执行超时或死锁
解决:查询 act_ru_execution 表,找到卡住的节点
总结:以上规范是团队协作的基础,请严格遵守。如有疑问,请在团队会议上讨论。