837 lines
19 KiB
Markdown
837 lines
19 KiB
Markdown
# 开发规范文档
|
||
|
||
**版本**: 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变化)
|
||

|
||

|
||
|
||
## 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 表,找到卡住的节点
|
||
```
|
||
|
||
---
|
||
|
||
**总结**:以上规范是团队协作的基础,请严格遵守。如有疑问,请在团队会议上讨论。
|
||
|