# 开发规范文档

**版本**: 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<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 注释必须标注负责人和日期

```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<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 {
    // ...
}
```

#### 日志规范

```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<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 代码规范

#### 命名规范

```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<Props> = () => {}
```

#### 文件命名

```
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<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 组件规范

```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 <Empty description="请选择一个节点" />;
  }
  
  // 5. 主要渲染
  return (
    <div className="node-config-panel">
      <Form
        form={form}
        onValuesChange={handleValuesChange}
      >
        {/* ... */}
      </Form>
    </div>
  );
}
```

**React 最佳实践**：
```tsx
// ✅ 使用 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 分支命名规范

```bash
# 功能分支
feature/workflow-editor          # 新功能
feature/node-http-request        # 新节点类型

# 修复分支
bugfix/expression-parser-null    # Bug修复

# 紧急修复
hotfix/security-vulnerability    # 安全漏洞

# 重构
refactor/simplify-converter      # 重构
```

### 2.3 Commit 规范

**格式**：`<type>(<scope>): <subject>`

```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<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 集成测试

```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(
      <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 审查注释规范

```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<WorkflowDefinition> 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: <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 日志级别配置

```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 表，找到卡住的节点
```

---

**总结**：以上规范是团队协作的基础，请严格遵守。如有疑问，请在团队会议上讨论。

