deploy-ease-platform/backend/README.md
2024-12-17 16:49:03 +08:00

1094 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Deploy Ease Platform
Deploy Ease Platform 是一个现代化的部署管理平台,旨在简化和自动化应用程序的部署流程。
## 已实现功能
### 1. 外部系统管理
- [x] 基础功能
- 创建外部系统支持Jenkins、Git等类型
- 更新系统信息
- 删除系统
- 查询系统详情
- 分页查询系统列表
- 条件筛选查询
- [x] Jenkins集成
- 连接测试
- 任务同步
- 状态检查
- 支持用户名密码认证
- 支持Token认证
- [x] Git集成
- 仓库连接测试
- 代码分支同步
- 提交历史查询
- 支持用户名密码认证
- 支持SSH密钥认证
- 支持Token认证
### 2. 用户权限管理
- [x] 用户管理
- 用户注册
- 用户登录
- 密码重置
- 用户信息修改
- 用户状态管理
- 部门分配
- [x] 角色管理
- 角色创建与维护
- 角色标签管理
- 角色权限分配
- 用户角色分配
- [x] 权限管理
- 权限创建与维护
- 权限分类管理
- 权限与菜单关联
- 权限检查与控制
### 3. 菜单管理
- [x] 基础功能
- 菜单树维护
- 菜单排序
- 菜单显示控制
- [x] 权限集成
- 菜单权限配置
- 按钮权限配置
- 数据权限配置
- [x] 前端集成
- 动态路由生成
- 菜单组件加载
- 权限指令集成
### 4. 部门管理
- [x] 基础功能
- 部门树维护
- 部门编码管理
- 部门描述维护
- 部门排序
- [x] 人员管理
- 部门负责人设置
- 部门人员管理
- 部门权限控制
### 5. 系统功能
- [x] 认证授权
- JWT令牌认证
- 权限拦截器
- 角色检查器
- 权限注解支持
- [x] 数据审计
- 创建人记录
- 创建时间记录
- 更新人记录
- 更新时间记录
- 版本控制
- [x] 异常处理
- 统一异常处理
- 业务异常处理
- 系统异常处理
- 参数校验异常处理
- [x] 数据库支持
- Flyway数据库版本控制
- 基础表结构
- 初始化数据
- 软删除支持
### 6. 开发支持
- [x] 基础框架
- 统一响应格式
- 统一分页格式
- 基础CRUD封装
- DTO自动转换
- [x] 代码规范
- 开发规范文档
- 接口文档规范
- 测试规范
- 注释规范
- [x] 测试支持
- 单元测试框架
- 集成测试支持
- 测试数据构建
- Mock支持
### 7. 租户管理API
#### 7.1 分页查询租户
```http
GET /api/v1/tenant/page
请求参数:
{
"pageNum": 1, // 页码从1开始
"pageSize": 10, // 每页大小
"sortField": "createTime", // 排序字段
"sortOrder": "desc", // 排序方式
"code": "string", // 租户编码(可选)
"name": "string", // 租户名称(可选)
"enabled": true // 是否启用(可选)
}
响应结果:
{
"success": true,
"code": 200,
"message": "操作成功",
"data": {
"content": [{
"id": 1,
"createTime": "2024-01-01 12:00:00",
"createBy": "admin",
"updateTime": "2024-01-01 12:00:00",
"updateBy": "admin",
"code": "TEST",
"name": "测试租户",
"address": "北京市朝阳区",
"contactName": "张三",
"contactPhone": "13800138000",
"email": "test@example.com",
"enabled": true
}],
"totalElements": 100,
"totalPages": 10,
"size": 10,
"number": 0,
"first": true,
"last": false,
"empty": false
}
}
```
#### 7.2 创建租户
```http
POST /api/v1/tenant
请求参数:
{
"code": "TEST", // 租户编码(必填)
"name": "测试租户", // 租户名称(必填)
"address": "北京市朝阳区", // 地址(可选)
"contactName": "张三", // 联系人姓名(可选)
"contactPhone": "13800138000", // 联系人电话(可选)
"email": "test@example.com", // 联系人邮箱(可选)
"enabled": true // 是否启用可选默认true
}
响应结果:
{
"success": true,
"code": 200,
"message": "操作成功",
"data": {
"id": 1,
// ... 其他字段同上
}
}
```
#### 7.3 更新租户
```http
PUT /api/v1/tenant/{id}
请求参数:
{
// 同创建接口
}
响应结果:
{
"success": true,
"code": 200,
"message": "操作成功",
"data": {
"id": 1,
// ... 其他字段同上
}
}
```
#### 7.4 删除租户
```http
DELETE /api/v1/tenant/{id}
响应结果:
{
"success": true,
"code": 200,
"message": "操作成功"
}
```
#### 7.5 获取租户状态
```http
GET /api/v1/tenant/{id}/enabled
响应结果:
{
"success": true,
"code": 200,
"message": "操作成功",
"data": true // true: 启用false: 禁用
}
```
#### 7.6 更新租户状态
```http
PUT /api/v1/tenant/{id}/enabled
请求参数:
enabled=true // 是否启用(必填)
响应结果:
{
"success": true,
"code": 200,
"message": "操作成功"
}
```
## 技术栈
### 后端技术栈
- Spring Boot 3.x
- Spring Security + JWT
- Spring Data JPA
- Flyway数据库版本控制
- MapStruct对象映射
- Lombok
- JUnit 5
### 数据库
- MySQL 8.0+
## API文档
### 1. 外部系统管理API
#### 1.1 分页查询
```http
GET /api/v1/external-system/page
请求参数:
{
"pageNum": 1, // 页码从1开始
"pageSize": 10, // 每页大小
"sortField": "createTime", // 排序字段
"sortOrder": "desc", // 排序方式
"name": "string", // 系统名称(可选)
"type": "JENKINS", // 系统类型(可选)
"enabled": true // 是否启用(可选)
}
响应结果:
{
"success": true,
"code": 200,
"message": "操作成功",
"data": {
"content": [{
"id": 1,
"createTime": "2024-01-01 12:00:00",
"createBy": "admin",
"updateTime": "2024-01-01 12:00:00",
"updateBy": "admin",
"name": "Jenkins测试环境",
"type": "JENKINS",
"url": "http://jenkins.test.com",
"remark": "测试环境Jenkins服务器",
"sort": 1,
"enabled": true,
"authType": "USERNAME_PASSWORD",
"username": "admin",
"password": "******",
"token": null,
"syncStatus": "SUCCESS",
"lastSyncTime": "2024-01-01 12:00:00",
"config": "{}"
}],
"totalElements": 100,
"totalPages": 10,
"size": 10,
"number": 0,
"first": true,
"last": false,
"empty": false
}
}
```
#### 1.2 创建外部系统
```http
POST /api/v1/external-system
请求参数:
{
"name": "Jenkins测试环境", // 系统名称(必填)
"type": "JENKINS", // 系统类型(必填)
"url": "http://jenkins.test.com", // 系统地址(必填)
"remark": "测试环境Jenkins服务器", // 备注(可选)
"sort": 1, // 排序(可选)
"enabled": true, // 是否启用可选默认true
"authType": "USERNAME_PASSWORD", // 认证方式(必填)
"username": "admin", // 用户名(可选)
"password": "password123", // 密码(可选)
"token": null, // 访问令牌(可选)
"config": "{}" // 配置信息(可选)
}
响应结果:
{
"success": true,
"code": 200,
"message": "操作成功",
"data": {
"id": 1,
// ... 其他字段同上
}
}
```
#### 1.3 更新外部系统
```http
PUT /api/v1/external-system/{id}
请求参数:
{
// 同创建接口
}
响应结果:
{
"success": true,
"code": 200,
"message": "操作成功",
"data": {
"id": 1,
// ... 其他字段同上
}
}
```
#### 1.4 删除外部系统
```http
DELETE /api/v1/external-system/{id}
响应结果:
{
"success": true,
"code": 200,
"message": "操作成功"
}
```
#### 1.5 测试连接
```http
POST /api/v1/external-system/{id}/test-connection
响应结果:
{
"success": true,
"code": 200,
"message": "操作成功",
"data": true
}
```
#### 1.6 同步数据
```http
POST /api/v1/external-system/{id}/sync
响应结果:
{
"success": true,
"code": 200,
"message": "操作成功"
}
```
#### 1.7 检查状态
```http
GET /api/v1/external-system/{id}/status
响应结果:
{
"success": true,
"code": 200,
"message": "操作成功",
"data": {
"syncStatus": "SUCCESS",
"lastSyncTime": "2024-01-01 12:00:00"
}
}
```
### 2. 用户认证API
#### 2.1 用户登录
```http
POST /api/v1/user/login
请求参数:
{
"username": "admin", // 用户名(必填)
"password": "password" // 密码(必填)
}
响应结果:
{
"success": true,
"code": 200,
"message": "登录成功",
"data": {
"token": "eyJhbGciOiJIUzI1NiJ9...",
"user": {
"id": 1,
"username": "admin",
"nickname": "管理员",
"email": "admin@example.com",
"phone": "13800138000",
"departmentId": 1,
"departmentName": "技术部"
}
}
}
```
#### 2.2 获取当前用户信息
```http
GET /api/v1/user/current
响应结果:
{
"success": true,
"code": 200,
"message": "操作成功",
"data": {
"id": 1,
"username": "admin",
"nickname": "管理员",
"email": "admin@example.com",
"phone": "13800138000",
"departmentId": 1,
"departmentName": "技术部"
}
}
```
### 3. 菜单管理API
#### 3.1 获取当前用户菜单
```http
GET /api/v1/menu/current
响应结果:
{
"success": true,
"code": 200,
"message": "操作成功",
"data": [{
"id": 1,
"name": "系统管理",
"path": "/system",
"component": "Layout",
"icon": "setting",
"permission": "system:view",
"type": 1, // 1目录 2菜单 3按钮
"parentId": null,
"sort": 1,
"hidden": false,
"children": [{
"id": 2,
"name": "用户管理",
"path": "/system/user",
"component": "system/user/index",
"icon": "user",
"permission": "system:user:view",
"type": 2,
"parentId": 1,
"sort": 1,
"hidden": false,
"children": []
}]
}]
}
```
#### 3.2 获取菜单树
```http
GET /api/v1/menu/tree
响应结果:
{
"success": true,
"code": 200,
"message": "操作成功",
"data": [{
// 结构同上
}]
}
```
#### 3.3 获取权限树
```http
GET /api/v1/menu/permission-tree
响应结果:
{
"success": true,
"code": 200,
"message": "操作成功",
"data": [{
"id": 1,
"name": "系统管理",
"type": 1,
"children": [{
"id": 2,
"name": "用户管理",
"type": 2,
"permissions": [{
"id": 3,
"code": "system:user:add",
"name": "新增用户",
"type": "BUTTON"
}]
}]
}]
}
```
### 4. 部门管理API
#### 4.1 获取部门树
```http
GET /api/v1/department/tree
响应结果:
{
"success": true,
"code": 200,
"message": "操作成功",
"data": [{
"id": 1,
"code": "TECH",
"name": "技术部",
"description": "技术研发部门",
"parentId": null,
"sort": 1,
"enabled": true,
"leaderId": 1,
"leaderName": "张三",
"children": [{
"id": 2,
"code": "DEV",
"name": "研发组",
"description": "研发团队",
"parentId": 1,
"sort": 1,
"enabled": true,
"leaderId": 2,
"leaderName": "李四",
"children": []
}]
}]
}
```
#### 4.2 创建部门
```http
POST /api/v1/department
请求参数:
{
"code": "TECH", // 部门编码(必填)
"name": "技术部", // 部门名称(必填)
"description": "技术研发部门", // 描述(可选)
"parentId": null, // 上级部门ID可选
"sort": 1, // 排序(可选)
"enabled": true, // 是否启用可选默认true
"leaderId": 1, // 负责人ID可选
"leaderName": "张三" // 负责人姓名(可选)
}
响应结果:
{
"success": true,
"code": 200,
"message": "操作成功",
"data": {
"id": 1,
// ... 其他字段同上
}
}
```
### 5. 角色管理API
#### 5.1 分配角色标签
```http
POST /api/v1/role/{id}/tags
请求参数:
{
"tagIds": [1, 2, 3] // 标签ID列表
}
响应结果:
{
"success": true,
"code": 200,
"message": "操作成功"
}
```
#### 5.2 分配用户角色
```http
POST /api/v1/role/{userId}/assignRoles
请求参数:
{
"roleIds": [1, 2, 3] // 角色ID列表
}
响应结果:
{
"success": true,
"code": 200,
"message": "操作成功"
}
```
#### 5.3 获取角色权限
```http
GET /api/v1/role/{id}/permissions
响应结果:
{
"success": true,
"code": 200,
"message": "操作成功",
"data": [{
"id": 1,
"code": "system:user:view",
"name": "查看用户",
"type": "MENU",
"sort": 1,
"menuName": "用户管理"
}]
}
```
#### 5.4 分配角色权限
```http
POST /api/v1/role/{id}/permissions
请求参数:
{
"permissionIds": [1, 2, 3] // 权限ID列表
}
响应结果:
{
"success": true,
"code": 200,
"message": "操作成功"
}
```
### 6. 权限管理API
#### 6.1 分页查询权限
```http
GET /api/v1/permission/page
请求参数:
{
"pageNum": 1,
"pageSize": 10,
"code": "system", // 权限编码(可选)
"name": "系统", // 权限名称(可选)
"type": "MENU", // 权限类型(可选)
"enabled": true, // 是否启用(可选)
"menuId": 1 // 菜单ID可选
}
响应结果:
{
"success": true,
"code": 200,
"message": "操作成功",
"data": {
"content": [{
"id": 1,
"code": "system:user:view",
"name": "查看用户",
"type": "MENU",
"sort": 1,
"menuId": 2,
"menuName": "用户管理"
}],
"totalElements": 100,
"totalPages": 10,
"size": 10,
"number": 0,
"first": true,
"last": false,
"empty": false
}
}
```
#### 6.2 创建权限
```http
POST /api/v1/permission
请求参数:
{
"code": "system:user:add", // 权限编码(必填)
"name": "新增用户", // 权限名称(必填)
"type": "BUTTON", // 权限类型(必填)
"menuId": 2, // 菜单ID必填
"sort": 1 // 排序(可选)
}
响应结果:
{
"success": true,
"code": 200,
"message": "操作成功",
"data": {
"id": 1,
// ... 其他字段同上
}
}
```
## 开发规范
### 后端开发规范
- 遵循DDD设计思想
- 统一的异常处理
- 统一的返回格式
- 完整的单元测试
- 详细的接口文档
- 规范的代码注释
详细规范请参考:[后端开发规范](.cursorrules1)
### 前端开发规范
- 统一的接口调用方式
- 统一的错误处理
- 标准的时间格式
- 规范的国际化实现
详细规范请参考:[前端开发规范](frontend.rules)
## 注意事项
### 1. 数据库
- 所有表必须包含基础字段:
```sql
id BIGINT PRIMARY KEY # 主键
create_time DATETIME # 创建时间
create_by VARCHAR(50) # 创建人
update_time DATETIME # 更新时间
update_by VARCHAR(50) # 更新人
version INT # 版本号
deleted BOOLEAN # 是否删除
```
- 使用Flyway进行数据库版本控制
- 新建表结构写入V1.0.0__init_schema.sql
- 初始数据写入V1.0.1__init_data.sql
### 2. 接口开发
- 所有接口都需要JWT认证
- 接口版本统一使用v1
- 返回数据格式统一为:
```json
{
"success": true,
"code": 200,
"message": "操作成功",
"data": {}
}
```
- 分页查询参数统一使用:
```json
{
"pageNum": 1,
"pageSize": 10,
"sortField": "createTime",
"sortOrder": "desc"
}
```
### 3. 异常处理
- 业务异常统一使用BusinessException
- 异常码在ResponseCode中定义
- 异常信息在messages.properties中定义
- 所有异常都通过GlobalExceptionHandler处理
### 4. 安全性
- 敏感信息密码、Token等需要加密存储
- 外部系统认证信息需要加密传输
- 定期清理过期的Token
- 限制API调用频率
### 5. 测试
- 所有Service层代码必须有单元测试
- 所有Controller必须有集成测试
- 测试覆盖率要求80%以上
- 测试数据使用@Sql注入避免影响生产数据
## 环境要求
- JDK 17+
- Maven 3.8+
- MySQL 8.0+
- Node.js 16+
## 快速开始
1. 克隆项目
```bash
git clone [项目地址]
```
2. 配置数据库
```bash
# 创建数据库
CREATE DATABASE deploy_ease DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
```
3. 修改配置
```yaml
# application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/deploy_ease
username: your_username
password: your_password
```
4. 启动项目
```bash
mvn spring-boot:run
```
## 贡献指南
1. Fork 项目
2. 创建功能分支
3. 提交代码
4. 创建Pull Request
## 许可证
[MIT License](LICENSE)
```
workflow_definition工作流定义表
存储我们自定义的工作流定义信息
包含流程名称、标识、版本、BPMN XML内容等
与Flowable的act_re_procdef表是一对一的关系
额外存储了图形编辑器的JSON数据方便前端展示和编辑
workflow_instance工作流实例表
记录工作流的执行实例信息
存储流程实例ID、业务标识、状态、变量等
与Flowable的act_ru_execution表是一对一的关系
提供了更多的业务字段,如开始时间、结束时间等
workflow_node_instance工作流节点实例表
记录工作流中每个节点的执行情况
存储节点ID、名称、类型、状态等
与Flowable的act_ru_task和act_hi_actinst表有关联
特别记录了Shell任务的执行结果、错误信息等
workflow_log工作流日志表
记录工作流执行过程中的详细日志
可以关联到具体的实例和节点
包含日志类型、级别、内容等
方便排查问题和审计
这些表与Flowable的原生表的主要区别是
更贴近业务需求,字段设计更符合我们的使用场景
提供了更丰富的元数据和状态信息
支持更细粒度的日志记录
可以存储自定义的业务数据
这样的设计让我们可以在使用Flowable强大的工作流引擎的同时也能满足特定的业务需求。
{
"name": "Simple Shell Workflow6",
"key": "simple_shell_workflow",
"description": "A simple workflow with shell task",
"graphJson": {
"cells": [
{
"id": "start",
"shape": "start",
"data": {
"label": "开始"
},
"position": {
"x": 100,
"y": 100
}
},
{
"id": "shell",
"shape": "serviceTask",
"data": {
"label": "Shell脚本",
"serviceTask": {
"type": "shell",
"implementation": "${shellTaskDelegate}",
"fields": {
"script": "timeout 10000000",
"workDir": "/tmp",
"env": {
"TEST_VAR": "test_value"
}
}
}
},
"position": {
"x": 300,
"y": 100
}
},
{
"id": "shellErrorBoundary",
"shape": "boundaryEvent",
"data": {
"label": "Shell错误",
"boundaryEvent": {
"type": "error",
"attachedToRef": "shell",
"errorRef": "SHELL_EXECUTION_ERROR"
}
},
"position": {
"x": 300,
"y": 160
}
},
{
"id": "errorHandler",
"shape": "serviceTask",
"data": {
"label": "错误处理",
"serviceTask": {
"type": "service",
"implementation": "${errorHandlerDelegate}"
}
},
"position": {
"x": 300,
"y": 220
}
},
{
"id": "end",
"shape": "end",
"data": {
"label": "结束"
},
"position": {
"x": 500,
"y": 100
}
},
{
"id": "flow1",
"shape": "edge",
"source": "start",
"target": "shell",
"data": {
"label": "流转到Shell"
}
},
{
"id": "flow2",
"shape": "edge",
"source": "shell",
"target": "end",
"data": {
"label": "完成"
}
},
{
"id": "flow_error",
"shape": "edge",
"source": "shellErrorBoundary",
"target": "errorHandler",
"data": {
"label": "错误处理"
}
},
{
"id": "flow_error_end",
"shape": "edge",
"source": "errorHandler",
"target": "end",
"data": {
"label": "处理完成"
}
}
]
}
}
死信队列的问题:
需要再异常里直接移除:
runtimeService.deleteProcessInstance(processInstanceId, errorMessage);
const eventSource = new EventSource(`/api/v1/shell-logs/${processInstanceId}`);
eventSource.addEventListener('STDOUT', (event) => {
console.log('Output:', event.data);
});
eventSource.addEventListener('STDERR', (event) => {
console.error('Error:', event.data);
});
// 当流程结束时关闭连接
eventSource.onerror = () => {
eventSource.close();
};
SELECT
hi.PROC_INST_ID_ as process_instance_id,
hi.PROC_DEF_ID_ as process_definition_id,
hi.START_TIME_ as start_time,
hi.END_TIME_ as end_time,
hi.DELETE_REASON_ as delete_reason,
-- 使用 GROUP_CONCAT 合并错误信息
GROUP_CONCAT(DISTINCT
CASE
WHEN var.NAME_ = 'errorDetail' THEN var.TEXT_
WHEN var.NAME_ IN ('errorMessage', 'error', 'exception') THEN var.TEXT_
END
SEPARATOR '; '
) as error_message,
-- 获取最后一个活动节点
MAX(CASE WHEN act.END_TIME_ IS NULL THEN act.ACT_NAME_ END) as last_activity_name,
MAX(CASE WHEN act.END_TIME_ IS NULL THEN act.ACT_ID_ END) as last_activity_id
FROM
ACT_HI_PROCINST hi
LEFT JOIN
ACT_HI_VARINST var ON hi.PROC_INST_ID_ = var.PROC_INST_ID_
LEFT JOIN
ACT_HI_ACTINST act ON hi.PROC_INST_ID_ = act.PROC_INST_ID_
WHERE
hi.PROC_INST_ID_ = '86faae44-bc47-11ef-a4cd-00155dcaffac'
AND (
var.NAME_ IN ('errorDetail', 'errorMessage', 'error', 'exception')
OR var.TEXT_ LIKE '%error%'
OR var.TEXT_ LIKE '%exception%'
OR var.TEXT_ LIKE '%失败%'
)
GROUP BY
hi.PROC_INST_ID_,
hi.PROC_DEF_ID_,
hi.START_TIME_,
hi.END_TIME_,
hi.DELETE_REASON_
ORDER BY
hi.START_TIME_ DESC;