# 开发规范文档 **版本**: v1.0 **目的**: 确保代码质量、可维护性、团队协作效率 --- ## 一、代码规范 ### 1.1 Java 代码规范 #### 命名规范 ```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 ``` #### 注释规范 ```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 input) { // 1. 验证工作流是否存在 WorkflowDefinition workflow = workflowRepository.findById(workflowId) .orElseThrow(() -> new WorkflowNotFoundException(workflowId)); // 2. 准备执行上下文 Map 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 注释必须标注负责人和日期 ```java // TODO(zhangsan, 2024-01-15): 优化表达式缓存策略 // FIXME(lisi, 2024-01-16): 修复循环依赖检测bug ``` #### 异常处理 ```java // ✅ 正确:具体的异常类型 public WorkflowDefinition getById(String id) { return workflowRepository.findById(id) .orElseThrow(() -> new WorkflowNotFoundException("工作流不存在: " + id)); } // ✅ 正确:记录日志 public void execute(String workflowId, Map 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 { // ... } ``` #### 日志规范 ```java 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 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 代码规范 #### 命名规范 ```typescript // ✅ 正确 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 = () => {} ``` #### 文件命名 ``` src/ ├── components/ │ ├── WorkflowEditor/ # 组件文件夹:大驼峰 │ │ ├── index.tsx # 主文件:小写 │ │ ├── Canvas.tsx # 子组件:大驼峰 │ │ └── WorkflowEditor.css # 样式:大驼峰 │ └── NodeConfigPanel/ │ └── index.tsx ├── api/ │ └── workflow.ts # API文件:小写 ├── types/ │ └── workflow.ts # 类型文件:小写 └── utils/ └── expressionParser.ts # 工具文件:小驼峰 ``` #### TypeScript 类型定义 ```typescript // ✅ 正确:明确的类型 interface WorkflowNode { id: string; type: string; name: string; position: { x: number; y: number }; config: Record; // 如果确实是任意类型 } // ✅ 正确:使用泛型 interface ApiResponse { 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 组件规范 ```tsx 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 ; } // 5. 主要渲染 return (
{/* ... */}
); } ``` **React 最佳实践**: ```tsx // ✅ 使用 memo 优化性能 const CustomNode = memo(({ data }: Props) => { return
{data.name}
; }); // ✅ 使用 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 (
console.log(node.id)}> {/* 每次都创建新函数 */} {/* 每次都创建新对象 */}
); ``` --- ## 二、Git 工作流 ### 2.1 分支策略 ``` main/master # 生产环境(只能通过PR合并) ↑ develop # 开发环境(默认分支) ↑ feature/xxx # 功能分支 ↑ hotfix/xxx # 紧急修复分支 ``` ### 2.2 分支命名规范 ```bash # 功能分支 feature/workflow-editor # 新功能 feature/node-http-request # 新节点类型 # 修复分支 bugfix/expression-parser-null # Bug修复 # 紧急修复 hotfix/security-vulnerability # 安全漏洞 # 重构 refactor/simplify-converter # 重构 ``` ### 2.3 Commit 规范 **格式**:`(): ` ```bash # ✅ 正确示例 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 ``` **描述模板**: ```markdown ## 变更内容 简要描述本次PR的变更内容 ## 变更类型 - [ ] 新功能 - [x] Bug修复 - [ ] 文档更新 - [ ] 重构 - [ ] 其他 ## 相关Issue Closes #123 ## 测试 - [x] 单元测试已通过 - [x] 手动测试已完成 - [ ] 需要补充测试 ## 截图(如果有UI变化) ![before](url) ![after](url) ## Checklist - [x] 代码已自测 - [x] 已添加/更新测试 - [x] 已更新文档 - [x] 代码符合规范 ``` --- ## 三、测试规范 ### 3.1 单元测试 **覆盖率要求**: - 核心业务逻辑:>80% - 工具类:>90% - Controller:>60% **命名规范**: ```java // 测试类:ClassName + Test public class WorkflowServiceTest {} // 测试方法:should_ExpectedBehavior_When_Condition @Test public void should_ThrowException_When_WorkflowNotFound() {} @Test public void should_ReturnSuccess_When_WorkflowExecuted() {} ``` **示例**: ```java @SpringBootTest public class ExpressionEngineTest { @Autowired private ExpressionEngine expressionEngine; @Mock private DelegateExecution execution; @BeforeEach public void setUp() { // 准备测试数据 Map 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 集成测试 ```java @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 前端测试 ```typescript 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( ); expect(screen.getByText('HTTP')).toBeInTheDocument(); }); it('应该生成正确的表达式', () => { const onChange = jest.fn(); render( ); // 选择字段 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 审查注释规范 ```java // ✅ 建设性建议 // 建议:这里可以使用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 ```markdown # 工作流平台 ## 快速开始 ### 环境要求 - 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: ```java @RestController @RequestMapping("/api/workflows") @Tag(name = "工作流管理", description = "工作流的创建、更新、删除、执行") public class WorkflowController { @PostMapping @Operation(summary = "创建工作流", description = "创建一个新的工作流定义") @ApiResponse(responseCode = "200", description = "创建成功") @ApiResponse(responseCode = "400", description = "参数错误") public ResponseEntity createWorkflow( @RequestBody @io.swagger.v3.oas.annotations.parameters.RequestBody( description = "工作流定义", required = true ) WorkflowDefinition workflow ) { // ... } } ``` 访问:http://localhost:8080/swagger-ui.html --- ## 六、部署规范 ### 6.1 环境配置 ```yaml # .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 发布流程 ```bash # 1. 更新版本号 # pom.xml: 1.1.0 # 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 日志级别配置 ```yaml # 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 表,找到卡住的节点 ``` --- **总结**:以上规范是团队协作的基础,请严格遵守。如有疑问,请在团队会议上讨论。