From e27bbca4b351388fad690da30fe0353c9309d07f Mon Sep 17 00:00:00 2001 From: dengqichen Date: Tue, 4 Nov 2025 16:30:16 +0800 Subject: [PATCH] =?UTF-8?q?=E6=89=93=E5=8D=B0=E4=BA=86JENKINS=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/README.md | 660 ------- backend/docs/api.md | 659 ------- backend/docs/architecture.md | 1598 ----------------- backend/docs/backend-development-guide.md | 176 -- backend/docs/deploy-ease-platform-v2.md | 347 ---- backend/docs/deploy-ease-workflow.md | 887 --------- backend/docs/frontend-development-guide.md | 1 - backend/docs/frontend-guide.md | 869 --------- backend/docs/workflow-data-format.md | 332 ---- backend/docs/workflow-development.md | 494 ----- backend/docs/workflow-engine-design.md | 670 ------- .../docs/workflow/workflow_instance_sync.md | 380 ---- backend/readme1.md | 322 ---- .../deploy/api/TeamConfigApiController.java | 94 - .../TeamEnvironmentConfigApiController.java | 66 + .../deploy/converter/TeamConfigConverter.java | 17 - .../TeamEnvironmentConfigConverter.java | 17 + .../deploy/dto/DeployableEnvironmentDTO.java | 6 + .../backend/deploy/dto/TeamConfigDTO.java | 35 - .../deploy/dto/TeamEnvironmentConfigDTO.java | 76 + .../backend/deploy/entity/TeamConfig.java | 59 - .../deploy/entity/TeamEnvironmentConfig.java | 104 ++ .../backend/deploy/query/TeamConfigQuery.java | 24 - .../query/TeamEnvironmentConfigQuery.java | 42 + .../repository/IDeployRecordRepository.java | 6 + .../ITeamApplicationRepository.java | 19 + .../repository/ITeamConfigRepository.java | 44 - .../ITeamEnvironmentConfigRepository.java | 49 + .../deploy/service/ITeamConfigService.java | 24 - .../ITeamEnvironmentConfigService.java | 33 + .../service/impl/DeployServiceImpl.java | 264 ++- .../service/impl/EnvironmentServiceImpl.java | 53 +- .../impl/TeamApplicationServiceImpl.java | 160 +- .../service/impl/TeamConfigServiceImpl.java | 42 - .../TeamEnvironmentConfigServiceImpl.java | 152 ++ .../framework/utils/NestedMapUtils.java | 193 ++ .../adapter/impl/WeworkChannelAdapter.java | 59 +- .../api/NotificationChannelApiController.java | 19 +- .../workflow/delegate/BaseNodeDelegate.java | 22 +- .../delegate/NotificationNodeDelegate.java | 6 +- .../db/changelog/changes/v1.0.0-data.sql | 7 - .../db/changelog/changes/v1.0.0-schema.sql | 47 +- .../List/components/TeamApplicationDialog.tsx | 527 ++++++ 43 files changed, 1740 insertions(+), 7921 deletions(-) delete mode 100644 backend/README.md delete mode 100644 backend/docs/api.md delete mode 100644 backend/docs/architecture.md delete mode 100644 backend/docs/backend-development-guide.md delete mode 100644 backend/docs/deploy-ease-platform-v2.md delete mode 100644 backend/docs/deploy-ease-workflow.md delete mode 100644 backend/docs/frontend-development-guide.md delete mode 100644 backend/docs/frontend-guide.md delete mode 100644 backend/docs/workflow-data-format.md delete mode 100644 backend/docs/workflow-development.md delete mode 100644 backend/docs/workflow-engine-design.md delete mode 100644 backend/docs/workflow/workflow_instance_sync.md delete mode 100644 backend/readme1.md delete mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/api/TeamConfigApiController.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/api/TeamEnvironmentConfigApiController.java delete mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/converter/TeamConfigConverter.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/converter/TeamEnvironmentConfigConverter.java delete mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamConfigDTO.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamEnvironmentConfigDTO.java delete mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/TeamConfig.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/TeamEnvironmentConfig.java delete mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/query/TeamConfigQuery.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/query/TeamEnvironmentConfigQuery.java delete mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamConfigRepository.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamEnvironmentConfigRepository.java delete mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/service/ITeamConfigService.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/service/ITeamEnvironmentConfigService.java delete mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/TeamConfigServiceImpl.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/TeamEnvironmentConfigServiceImpl.java create mode 100644 frontend/src/pages/Deploy/Team/List/components/TeamApplicationDialog.tsx diff --git a/backend/README.md b/backend/README.md deleted file mode 100644 index c8845cc9..00000000 --- a/backend/README.md +++ /dev/null @@ -1,660 +0,0 @@ -# Deploy Ease Platform - -Deploy Ease Platform 是一个基于Spring Boot 3.x和Flowable 7.x的现代化部署管理平台,提供工作流驱动的自动化部署解决方案。 - -## 技术栈 - -### 后端 -- **Java 21** + **Spring Boot 3.2.0** -- **Spring Security 6.2.0** + JWT认证 -- **Spring Data JPA** + Hibernate -- **Flowable 7.2.0** - 工作流引擎 -- **MySQL 8.0+** + Flyway数据库版本控制 -- **MapStruct 1.5.5** - 对象映射 -- **QueryDSL 5.0.0** - 类型安全查询 -- **OpenAPI/Swagger** - API文档 -- **Lombok** - 代码简化 -- **Hutool** - 工具类库 - -### 构建工具 -- **Maven 3.8+** - -## 核心功能模块 - -### ✅ 1. 系统管理(已完成) - -#### 1.1 用户管理 -- [x] 用户注册和登录 -- [x] JWT Token认证 -- [x] 密码重置 -- [x] 用户信息维护 -- [x] 用户状态管理(启用/禁用) -- [x] 部门分配 -- [x] 基础CRUD操作(分页查询、创建、更新、删除) - -**主要接口:** -``` -POST /api/v1/user/login - 用户登录 -GET /api/v1/user/current - 获取当前用户信息 -POST /api/v1/user/{id}/reset-password - 重置密码 -PUT /api/v1/user/{id}/department - 分配部门 -``` - -#### 1.2 角色管理 -- [x] 角色创建与维护 -- [x] 角色标签管理 -- [x] 角色权限分配 -- [x] 用户角色分配 -- [x] 角色权限查询 - -**主要接口:** -``` -POST /api/v1/role - 创建角色 -POST /api/v1/role/{id}/tags - 分配标签 -POST /api/v1/role/{userId}/assignRoles - 分配角色 -GET /api/v1/role/{id}/permissions - 获取角色权限 -POST /api/v1/role/{id}/permissions - 分配权限 -``` - -#### 1.3 权限管理 -- [x] 权限创建与维护 -- [x] 权限分类管理(菜单权限、按钮权限) -- [x] 权限与菜单关联 -- [x] 基于权限的访问控制 - -#### 1.4 菜单管理 -- [x] 菜单树维护 -- [x] 菜单排序 -- [x] 菜单显示控制 -- [x] 动态菜单加载 -- [x] 菜单权限配置 -- [x] 权限树查询 - -**主要接口:** -``` -GET /api/v1/menu/current - 获取当前用户菜单 -GET /api/v1/menu/tree - 获取菜单树 -GET /api/v1/menu/permission-tree - 获取权限树 -``` - -#### 1.5 部门管理 -- [x] 部门树形结构维护 -- [x] 部门编码管理 -- [x] 部门负责人设置 -- [x] 部门人员管理 -- [x] 部门排序 - -#### 1.6 租户管理 -- [x] 多租户支持 -- [x] 租户信息管理 -- [x] 租户状态控制 - -**实体统计:** 9个核心实体 -- User(用户) -- Role(角色) -- RoleTag(角色标签) -- Permission(权限) -- PermissionTemplate(权限模板) -- Menu(菜单) -- Department(部门) -- Tenant(租户) -- SysParam(系统参数) - ---- - -### ✅ 2. 外部系统集成(已完成) - -#### 2.1 外部系统管理 -- [x] 外部系统统一管理(Jenkins、Git等) -- [x] 多种认证方式支持 - - 用户名密码认证 - - Token认证 - - SSH密钥认证 -- [x] 连接测试 -- [x] 同步状态追踪 - -**主要接口:** -``` -POST /api/v1/external-system - 创建外部系统 -GET /api/v1/external-system/{id}/test-connection - 测试连接 -POST /api/v1/external-system/{id}/sync - 同步数据 -GET /api/v1/external-system/{id}/status - 获取同步状态 -``` - -#### 2.2 Jenkins集成 -- [x] Jenkins实例管理 -- [x] 视图(View)同步 -- [x] 任务(Job)同步 -- [x] 构建(Build)信息同步 -- [x] 构建历史记录 -- [x] 同步历史追踪 - -**主要接口:** -``` -POST /api/v1/jenkins-manager/{externalSystemId}/sync-all - 同步所有数据 -POST /api/v1/jenkins-manager/{externalSystemId}/sync-views - 同步视图 -POST /api/v1/jenkins-manager/{externalSystemId}/sync-jobs - 同步任务 -POST /api/v1/jenkins-manager/{externalSystemId}/sync-builds - 同步构建 -GET /api/v1/jenkins-manager/{externalSystemId}/instance - 获取实例信息 -``` - -**相关实体:** -- JenkinsView(Jenkins视图) -- JenkinsJob(Jenkins任务) -- JenkinsBuild(Jenkins构建) -- JenkinsSyncHistory(同步历史) - -#### 2.3 Git仓库集成 -- [x] Git仓库组(Group)管理 -- [x] Git项目(Project)管理 -- [x] Git分支(Branch)管理 -- [x] 多级同步(组 → 项目 → 分支) -- [x] 同步历史记录 - -**主要接口:** -``` -POST /api/v1/repository-manager/{externalSystemId}/sync-groups - 同步仓库组 -POST /api/v1/repository-manager/{externalSystemId}/groups/sync-projects - 同步项目 -POST /api/v1/repository-manager/{externalSystemId}/projects/sync-branches - 同步分支 -GET /api/v1/repository-manager/{externalSystemId}/instance - 获取实例信息 -``` - -**相关实体:** -- RepositoryGroup(仓库组) -- RepositoryProject(仓库项目) -- RepositoryBranch(仓库分支) -- RepositorySyncHistory(同步历史) - ---- - -### ✅ 3. 部署管理(已完成) - -#### 3.1 应用管理 -- [x] 应用创建与维护 -- [x] 应用编码管理 -- [x] 开发语言类型支持(Java、Python、Node.js等) -- [x] 应用与仓库关联 -- [x] 应用分组管理 - -**主要接口:** -``` -POST /api/v1/applications - 创建应用 -GET /api/v1/applications/development-languages - 获取开发语言列表 -``` - -**相关实体:** -- Application(应用) -- ProjectGroup(项目组) - -#### 3.2 环境管理 -- [x] 环境配置管理(开发、测试、生产等) -- [x] 环境编码管理 -- [x] 构建类型配置(Jenkins、GitLab Runner、GitHub Action) -- [x] 部署方式配置(K8S、Docker、VM) -- [x] 项目组与环境关联 - -**相关实体:** -- Environment(环境) - -#### 3.3 部署配置 -- [x] 应用部署配置管理 -- [x] 构建配置Schema定义 -- [x] 部署操作 -- [x] 部署日志记录 - -**主要接口:** -``` -POST /api/v1/deploy-app-config - 创建部署配置 -GET /api/v1/deploy-app-config/defined - 获取构建类型Schema -POST /api/v1/deploy-app-config/deploy - 执行部署 -``` - -**相关实体:** -- DeployAppConfig(部署配置) -- DeployLog(部署日志) - ---- - -### ✅ 4. 工作流引擎(已完成,基于Flowable) - -#### 4.1 工作流定义 -- [x] 工作流设计和保存 -- [x] 工作流发布管理 -- [x] 工作流启用/禁用 -- [x] 工作流状态管理(草稿、已发布、已禁用) -- [x] 图形化配置(GraphJSON) -- [x] BPMN XML自动生成 -- [x] 工作流分类管理 - -**主要接口:** -``` -POST /api/v1/workflow/definition/design - 保存工作流设计 -GET /api/v1/workflow/definition/published - 查询已发布工作流 -POST /api/v1/workflow/definition/{id}/published - 发布工作流 -POST /api/v1/workflow/definition/{id}/disable - 禁用工作流 -POST /api/v1/workflow/definition/{id}/enable - 启用工作流 -GET /api/v1/workflow/definition/categories - 获取工作流分类 -``` - -#### 4.2 工作流实例 -- [x] 工作流实例启动 -- [x] 工作流实例挂起 -- [x] 工作流实例恢复 -- [x] 工作流执行状态查询 -- [x] 历史实例查询 -- [x] 实例与模板关联查询 - -**主要接口:** -``` -POST /api/v1/workflow/instance/start - 启动工作流 -GET /api/v1/workflow/instance/templates-with-instances - 查询模板及实例 -GET /api/v1/workflow/instance/historical-instances - 查询历史实例 -POST /api/v1/workflow/definition/{processInstanceId}/suspend - 挂起实例 -POST /api/v1/workflow/definition/{processInstanceId}/resume - 恢复实例 -``` - -#### 4.3 节点定义与执行 -- [x] 节点类型管理 -- [x] 节点配置Schema -- [x] 节点实例状态追踪 -- [x] 节点重试机制 -- [x] 节点跳过机制 - -**主要接口:** -``` -POST /api/v1/workflow/node-instance/{id}/retry - 重试节点 -POST /api/v1/workflow/node-instance/{id}/skip - 跳过节点 -``` - -#### 4.4 节点类型实现(Delegate) -- [x] **ShellNodeDelegate** - Shell脚本执行 -- [x] **DeployNodeDelegate** - 部署节点执行 -- [x] **ApprovalNodeDelegate** - 审批节点 -- [x] **NotificationNodeDelegate** - 通知节点 -- [x] **BaseNodeDelegate** - 基础节点抽象 - -#### 4.5 工作流日志 -- [x] 实时日志记录 -- [x] 节点级日志 -- [x] 日志查询 - -**相关实体:** -- WorkflowDefinition(工作流定义) -- WorkflowNodeDefinition(节点定义) -- WorkflowInstance(工作流实例) -- WorkflowNodeInstance(节点实例) -- WorkflowLog(工作流日志) - ---- - -### ✅ 5. 基础框架(已完成) - -#### 5.1 通用CRUD框架 -- [x] BaseController - 统一REST控制器 -- [x] BaseServiceImpl - 通用服务实现 -- [x] IBaseRepository - 通用数据访问接口 -- [x] BaseConverter - 对象转换抽象 -- [x] 统一响应格式(Response) -- [x] 统一分页格式(Page) - -**标准CRUD接口(所有Controller继承):** -``` -POST /{resource} - 创建 -PUT /{resource}/{id} - 更新 -DELETE /{resource}/{id} - 删除 -GET /{resource}/{id} - 查询详情 -GET /{resource} - 查询列表 -GET /{resource}/page - 分页查询 -GET /{resource}/list - 条件查询 -POST /{resource}/batch - 批量处理 -GET /{resource}/export - 导出数据 -``` - -#### 5.2 查询框架 -- [x] 基于QueryDSL的类型安全查询 -- [x] 动态查询条件构建 -- [x] @QueryField注解支持 -- [x] 多种查询类型(EQUAL、LIKE、BETWEEN、IN等) -- [x] 软删除查询支持 - -#### 5.3 安全框架 -- [x] Spring Security集成 -- [x] JWT Token认证 -- [x] 权限拦截器 -- [x] 自定义认证入口点 -- [x] JWT过滤器 - -#### 5.4 审计框架 -- [x] 审计注解(@Audited) -- [x] 自动记录创建人/创建时间 -- [x] 自动记录更新人/更新时间 -- [x] 乐观锁版本控制 -- [x] 审计事件监听 - -#### 5.5 异常处理 -- [x] 统一异常处理(GlobalExceptionHandler) -- [x] 业务异常(BusinessException) -- [x] 系统异常(SystemException) -- [x] 唯一约束异常(UniqueConstraintException) -- [x] 实体未找到异常(EntityNotFoundException) -- [x] 国际化错误消息 - -#### 5.6 数据库版本控制 -- [x] Flyway集成 -- [x] 表结构版本管理(V1.0.0__init_schema.sql) -- [x] 初始数据管理(V1.0.1__init_data.sql) -- [x] 28个数据库表 - -#### 5.7 其他框架功能 -- [x] 国际化支持(i18n) -- [x] JSON序列化配置 -- [x] 租户过滤器 -- [x] 逻辑删除支持(@LogicDelete) -- [x] 依赖注入后处理器 -- [x] Formily Schema生成(用于前端表单) - ---- - -## 数据库表结构(28张表) - -### 系统管理表(7张) -1. `sys_tenant` - 租户表 -2. `sys_department` - 部门表 -3. `sys_user` - 用户表 -4. `sys_param` - 系统参数表 -5. `sys_role` - 角色表 -6. `sys_role_tag` - 角色标签表 -7. `sys_menu` - 菜单表 - -### 权限管理表(5张) -8. `sys_role_tag_relation` - 角色标签关系表 -9. `sys_user_role` - 用户角色关系表 -10. `sys_role_menu` - 角色菜单关系表 -11. `sys_permission_template` - 权限模板表 -12. `sys_template_menu` - 模板菜单关系表 -13. `sys_permission` - 权限表 - -### 外部系统表(1张) -14. `sys_external_system` - 外部系统表 - -### Git仓库表(3张) -15. `deploy_repo_group` - 仓库组表 -16. `deploy_repo_project` - 仓库项目表 -17. `deploy_repo_branch` - 仓库分支表 - -### Jenkins表(4张) -18. Jenkins相关表(通过代码推断,未在SQL中完整展示) - -### 工作流表(5张) -19. `workflow_definition` - 工作流定义表 -20. `workflow_node_definition` - 节点定义表 -21. `workflow_instance` - 工作流实例表 -22. `workflow_node_instance` - 节点实例表 -23. `workflow_log` - 工作流日志表 - -### 部署管理表(6张) -24. `deploy_project_group` - 项目组表 -25. `deploy_application` - 应用表 -26. `deploy_environment` - 环境表 -27. `deploy_project_group_environment` - 项目组环境关系表 -28. `deploy_log` - 部署日志表 -29. `deploy_app_config` - 应用部署配置表 - ---- - -## API接口统计 - -### Controller统计(26个) -**系统管理(7个):** -- UserApiController -- RoleApiController -- RoleTagApiController -- PermissionApiController -- MenuApiController -- DepartmentApiController -- TenantApiController - -**部署管理(8个):** -- ApplicationApiController -- ProjectGroupApiController -- EnvironmentApiController -- DeployAppConfigApiController -- DeployLogApiController -- ExternalSystemApiController -- RepositoryManagerApiController -- RepositoryProjectApiController -- RepositoryGroupApiController -- RepositoryBranchApiController - -**Jenkins管理(5个):** -- JenkinsManagerApiController -- JenkinsViewApiController -- JenkinsJobApiController -- JenkinsBuildApiController -- JenkinsSyncHistoryApiController - -**工作流管理(4个):** -- WorkflowDefinitionApiController -- WorkflowNodeDefinitionApiController -- WorkflowInstanceApiController -- WorkflowNodeInstanceApiController - -**接口方法统计:** -- 约49个自定义@Operation接口方法 -- 每个Controller继承BaseController,获得9个标准CRUD方法 -- **总计约280+个API接口** - ---- - -## 🚧 待完善功能 - -### 1. 测试覆盖 -- [ ] Service层单元测试 -- [ ] Controller层集成测试 -- [ ] Repository层数据访问测试 -- [ ] 工作流引擎测试 -- **当前测试覆盖率:约1%(仅1个测试类)** - -### 2. 缓存机制 -- [ ] Redis集成 -- [ ] 工作流定义缓存 -- [ ] 权限数据缓存 -- [ ] 菜单数据缓存 -- **当前状态:已引入Caffeine依赖,但未使用** - -### 3. 监控和运维 -- [ ] 健康检查端点完善 -- [ ] 性能监控(APM)集成 -- [ ] 日志聚合(ELK) -- [ ] 告警机制 -- [ ] 链路追踪 - -### 4. 部署和DevOps -- [ ] Docker镜像构建 -- [ ] Kubernetes部署配置 -- [ ] CI/CD Pipeline -- [ ] 环境配置分离(dev/test/prod) -- **当前配置:application.yml包含硬编码数据库密码和JWT密钥** - -### 5. 文档完善 -- [ ] API文档完善(Swagger注释) -- [ ] 架构图和流程图 -- [ ] 部署手册 -- [ ] 运维手册 -- [ ] 最佳实践指南 - -### 6. 代码质量 -- [ ] 代码质量检查工具集成(Checkstyle、PMD、SonarQube) -- [ ] TODO注释处理(14处待处理) -- [ ] System.out.println清理(14处) -- [ ] 泛化异常处理优化(95处catch Exception) - -### 7. 安全加固 -- [ ] 敏感配置外部化 -- [ ] JWT密钥加密存储 -- [ ] 数据库连接加密 -- [ ] API访问频率限制 -- [ ] SQL注入防护验证 -- [ ] XSS攻击防护 - -### 8. 性能优化 -- [ ] 数据库索引优化 -- [ ] N+1查询问题排查 -- [ ] 连接池配置优化(当前仅10个) -- [ ] 大数据量分页优化 -- [ ] 异步任务处理 - ---- - -## 快速开始 - -### 环境要求 -- JDK 21+ -- Maven 3.8+ -- MySQL 8.0+ -- Node.js 16+(前端) - -### 1. 数据库准备 -```sql -CREATE DATABASE `deploy-ease-platform` - DEFAULT CHARACTER SET utf8mb4 - COLLATE utf8mb4_unicode_ci; -``` - -### 2. 配置修改 -编辑 `src/main/resources/application.yml`: -```yaml -spring: - datasource: - url: jdbc:mysql://localhost:3306/deploy-ease-platform?... - username: your_username - password: your_password - -jwt: - secret: 'your_secret_key' # 建议使用环境变量 -``` - -⚠️ **安全提示:** 生产环境请使用环境变量或配置中心管理敏感信息! - -### 3. 启动项目 -```bash -# 编译项目 -mvn clean package -DskipTests - -# 启动服务 -mvn spring-boot:run - -# 或直接运行 -java -jar target/deploy-cd-1.0-SNAPSHOT.jar -``` - -### 4. 访问服务 -- **API文档:** http://localhost:8080/swagger-ui.html -- **健康检查:** http://localhost:8080/actuator/health - -### 5. 默认用户(需通过初始化SQL配置) -``` -用户名:admin -密码:(请查看V1.0.1__init_data.sql) -``` - ---- - -## 项目规范 - -### 代码规范 -详见项目根目录下的规范文件: -- `.cursor/rules/project.mdc` - 完整开发规范 -- `frontend.rules` - 前端开发规范 - -### 核心规范要点 - -#### 包结构 -``` -com.qqchen.deploy.backend -├── framework/ # 框架核心 -│ ├── annotation/ # 注解定义 -│ ├── controller/ # 基础控制器 -│ ├── service/ # 基础服务 -│ ├── repository/ # 基础仓库 -│ └── ... -├── system/ # 系统模块 -│ ├── api/ # REST接口 -│ ├── entity/ # 实体类 -│ ├── service/ # 业务服务 -│ └── ... -├── deploy/ # 部署模块 -└── workflow/ # 工作流模块 -``` - -#### 命名规范 -- **Entity:** 名词,如User、Role -- **DTO:** 实体名+DTO,如UserDTO -- **Service接口:** I+实体名+Service,如IUserService -- **Service实现:** 实体名+ServiceImpl,如UserServiceImpl -- **Controller:** 实体名+ApiController,如UserApiController -- **Repository:** I+实体名+Repository,如IUserRepository - -#### 注解使用 -- **@ServiceType(DATABASE)** - 数据库服务 -- **@ServiceType(INTEGRATION)** - 集成服务(禁用数据库操作) -- **@LogicDelete** - 启用逻辑删除 -- **@Audited** - 启用审计 -- **@QueryField** - 查询字段映射 - ---- - -## 贡献指南 - -### 提交规范 -``` -feat: 新功能 -fix: Bug修复 -docs: 文档更新 -style: 代码格式调整 -refactor: 重构 -perf: 性能优化 -test: 测试相关 -chore: 构建/工具变动 -``` - -### 分支管理 -- `master` - 生产环境分支 -- `develop` - 开发分支 -- `feature/*` - 功能分支 -- `bugfix/*` - Bug修复分支 -- `hotfix/*` - 热修复分支 - ---- - -## 许可证 -MIT License - ---- - -## 联系方式 -项目维护者:Deploy Ease Team - ---- - -## 更新日志 - -### v1.0.0(当前版本) -- ✅ 完成系统管理核心功能 -- ✅ 完成Jenkins和Git集成 -- ✅ 完成工作流引擎基础功能 -- ✅ 完成部署管理框架 -- ✅ 完成28张数据库表设计 -- ✅ 完成280+个API接口 -- ⚠️ 测试覆盖率待提升 -- ⚠️ 缓存机制待实现 -- ⚠️ 生产环境配置待优化 - ---- - -**注意事项:** -1. 本项目处于开发阶段,部分功能仍在完善中 -2. 生产环境使用前请务必完成安全加固 -3. 建议补充完整的单元测试和集成测试 -4. 数据库配置和JWT密钥需要外部化管理 -5. 建议集成专业的日志分析和监控方案 diff --git a/backend/docs/api.md b/backend/docs/api.md deleted file mode 100644 index 9e674001..00000000 --- a/backend/docs/api.md +++ /dev/null @@ -1,659 +0,0 @@ -# API 接口文档 - -## 1. 通用说明 - -### 1.1 接口规范 - -- 基础路径: `/api/v1` -- 请求方式: REST风格 -- 数据格式: JSON -- 字符编码: UTF-8 -- 时间格式: ISO8601 (YYYY-MM-DDTHH:mm:ss.SSSZ) - -### 1.2 通用响应格式 - -```json -{ - "code": 0, // 响应码,0表示成功,非0表示失败 - "message": "成功", // 响应消息 - "data": { // 响应数据 - // 具体数据结构 - } -} -``` - -### 1.3 通用查询参数 - -分页查询参数: -```json -{ - "page": 1, // 页码,从1开始 - "size": 10, // 每页大小 - "sort": ["id,desc"], // 排序字段,可选 - "keyword": "", // 关键字搜索,可选 -} -``` - -### 1.4 通用错误码 - -| 错误码 | 说明 | 处理建议 | -|--------|------|----------| -| 0 | 成功 | - | -| 1000 | 系统内部错误 | 联系管理员 | -| 1001 | 数据库操作失败 | 重试或联系管理员 | -| 1002 | 并发操作冲突 | 刷新后重试 | -| 2000 | 参数验证失败 | 检查参数 | -| 2001 | 数据不存在 | 检查参数 | -| 2002 | 数据已存在 | 检查参数 | - -## 2. 工作流定义��口 - -### 2.1 创建工作流定义 - -**接口说明**:创建新的工作流定义 - -**请求路径**:POST /workflow-definitions - -**请求参数**: -```json -{ - "code": "string", // 工作流编码,必填,唯一 - "name": "string", // 工作流名称,必填 - "description": "string", // 描述,可选 - "nodeConfig": { // 节点配置,必填 - "nodes": [{ - "id": "string", // 节点ID - "type": "string", // 节点类型 - "name": "string", // 节点名称 - "config": { // 节点配置 - // 具体配置项根据节点类型定义 - } - }] - }, - "transitionConfig": { // 流转配置,必填 - "transitions": [{ - "from": "string", // 来源节点ID - "to": "string", // 目标节点ID - "condition": "string" // 流转条件 - }] - }, - "formDefinition": { // 表单定义,必填 - // 表单配置项 - }, - "graphDefinition": { // 图形定义,必填 - // 图形布局配置 - } -} -``` - -**响应数据**: -```json -{ - "code": 0, - "message": "成功", - "data": { - "id": "long", // 工作流定义ID - "code": "string", // 工作流编码 - "name": "string", // 工作流名称 - "description": "string", // 描述 - "version": 1, // 版本号 - "status": "DRAFT", // 状态:DRAFT-草稿、PUBLISHED-已发布、DISABLED-已禁用 - "enabled": true, // 是否启用 - "nodeConfig": {}, // 节点配置 - "transitionConfig": {}, // 流转配置 - "formDefinition": {}, // 表单定义 - "graphDefinition": {}, // 图形定义 - "createTime": "string", // 创建时间 - "updateTime": "string" // 更新时间 - } -} -``` - -### 2.2 更新工作流定义 - -**接口说明**:更新工作流定义,仅草稿状态可更新 - -**请求路径**:PUT /workflow-definitions/{id} - -**路径参数**: -- id: 工作流定义ID - -**请求参数**:同创建接口 - -**响应数据**:同创建接口 - -### 2.3 发布工作流定义 - -**接口说明**:发布工作流定义,使其可被使用 - -**请求路径**:POST /workflow-definitions/{id}/publish - -**路径参数**: -- id: 工作流定义ID - -**响应数据**:同创建接口 - -### 2.4 禁用工作流定义 - -**接口说明**:禁用工作流定义,禁止新建实例 - -**请求路径**:POST /workflow-definitions/{id}/disable - -**路径参数**: -- id: 工作流定义ID - -**响应数据**:同创建接口 - -### 2.5 启用工作流定义 - -**接口说明**:启用已��用的工作流定义 - -**请求路径**:POST /workflow-definitions/{id}/enable - -**路径参数**: -- id: 工作流定义ID - -**响应数据**:同创建接口 - -### 2.6 创建新版本 - -**接口说明**:基于现有工作流定义创建新版本 - -**请求路径**:POST /workflow-definitions/{id}/versions - -**路径参数**: -- id: 工作流定义ID - -**响应数据**:同创建接口 - -### 2.7 查询工作流定义列表 - -**接口说明**:分页查询工作流定义列表 - -**请求路径**:GET /workflow-definitions - -**请求参数**: -```json -{ - "page": 1, // 页码,从1开始 - "size": 10, // 每页大小 - "sort": ["id,desc"], // 排序字段 - "keyword": "", // 关键字搜索 - "status": "DRAFT", // 状态过滤 - "enabled": true // 是否启用 -} -``` - -**响应数据**: -```json -{ - "code": 0, - "message": "成功", - "data": { - "content": [{ // 列表数据 - // 工作流定义数据,同创建接口 - }], - "totalElements": 100, // 总记录数 - "totalPages": 10, // 总页数 - "size": 10, // 每页大小 - "number": 1 // 当前页码 - } -} -``` - -## 3. 工作流实例接口 - -### 3.1 创建工作流实例 - -**接口说明**:创建工作流实例 - -**请求路径**:POST /workflow-instances - -**请求参数**: -```json -{ - "definitionId": "long", // 工作流定义ID,必填 - "businessKey": "string", // 业务标识,可选 - "variables": { // 初始变量,可选 - "key": "value" - } -} -``` - -**响应数据**: -```json -{ - "code": 0, - "message": "成功", - "data": { - "id": "long", // 实例ID - "definitionId": "long", // 定义ID - "businessKey": "string",// 业务标识 - "status": "CREATED", // 状态:CREATED-已创建、RUNNING-运行中、SUSPENDED-已暂停、COMPLETED-已完成、TERMINATED-已终止 - "startTime": "string", // 开始时间 - "endTime": "string", // 结束时间 - "variables": {}, // 变量 - "createTime": "string", // 创建时间 - "updateTime": "string" // 更新时间 - } -} -``` - -### 3.2 启动工作流实例 - -**接口说明**:启动工作流实例 - -**请求路径**:POST /workflow-instances/{id}/start - -**路径参数**: -- id: 实例ID - -**响应数据**:同创建接口 - -### 3.3 暂停工作流实例 - -**接口说明**:暂停运行中的工作流实例 - -**请求路径**:POST /workflow-instances/{id}/suspend - -**路径参数**: -- id: 实例ID - -**响应数据**:同创建接口 - -### 3.4 恢复工作流实例 - -**接口说明**:恢复已暂停的工作流实例 - -**请求路径**:POST /workflow-instances/{id}/resume - -**路径参数**: -- id: 实例ID - -**响应数据**:同创建接口 - -### 3.5 终止工作流实例 - -**接口说明**:强制终止工作流实例 - -**请求路径**:POST /workflow-instances/{id}/terminate - -**路径参数**: -- id: 实例ID - -**响应数据**:同创建接口 - -### 3.6 查询工作流实例列表 - -**接口说明**:分页查询工作流实例列表 - -**请求路径**:GET /workflow-instances - -**请求参数**: -```json -{ - "page": 1, // 页码,从1开始 - "size": 10, // 每页大小 - "sort": ["id,desc"], // 排序字段 - "keyword": "", // 关键字搜索 - "status": "RUNNING", // 状态过滤 - "definitionId": "long", // 定义ID过滤 - "businessKey": "string" // 业务标识过滤 -} -``` - -**响应数据**: -```json -{ - "code": 0, - "message": "成功", - "data": { - "content": [{ // 列表数据 - // 工作流实例数据,同创建接口 - }], - "totalElements": 100, // 总记录数 - "totalPages": 10, // 总页数 - "size": 10, // 每页大小 - "number": 1 // 当前页码 - } -} -``` - -## 4. 节点实例接口 - -### 4.1 查询节点实例列表 - -**接口说明**:查询工作流实例的节点列表 - -**请求路径**:GET /workflow-instances/{instanceId}/nodes - -**路径参数**: -- instanceId: 工作流实例ID - -**请求参数**: -```json -{ - "status": "RUNNING" // 状态过滤,可选 -} -``` - -**响应数据**: -```json -{ - "code": 0, - "message": "成功", - "data": [{ - "id": "long", // 节点实例ID - "workflowInstanceId": "long", // 工作流实例ID - "nodeId": "string", // 节点定义ID - "nodeName": "string", // 节点名称 - "nodeType": "string", // 节点类型 - "status": "string", // 状态:CREATED、RUNNING、COMPLETED、FAILED - "startTime": "string", // 开始时间 - "endTime": "string", // 结束时间 - "output": "string", // 输出结果 - "error": "string", // 错误信息 - "createTime": "string", // 创建时间 - "updateTime": "string" // 更新时间 - }] -} -``` - -## 5. 工作流变量接口 - -### 5.1 设置变量 - -**接口说明**:设置工作流实例变量 - -**请求路径**:POST /workflow-instances/{instanceId}/variables - -**路径参数**: -- instanceId: 工作流实例ID - -**请求参数**: -```json -{ - "name": "string", // 变量名,必填 - "value": "object", // 变量值,必�� - "scope": "GLOBAL" // 作用域:GLOBAL-全局、NODE-节点,可选,默认GLOBAL -} -``` - -**响应数据**: -```json -{ - "code": 0, - "message": "成功" -} -``` - -### 5.2 获取变量 - -**接口说明**:获取工作流实例变量 - -**请求路径**:GET /workflow-instances/{instanceId}/variables - -**路径参数**: -- instanceId: 工作流实例ID - -**请求参数**: -```json -{ - "scope": "GLOBAL" // 作用域过滤,可选 -} -``` - -**响应数据**: -```json -{ - "code": 0, - "message": "成功", - "data": { - "variableName": "variableValue" - } -} -``` - -## 6. 工作流日志接口 - -### 6.1 查询日志列表 - -**接口说明**:查询工作流实例日志 - -**请求路径**:GET /workflow-instances/{instanceId}/logs - -**路径参数**: -- instanceId: 工作流实例ID - -**请求参数**: -```json -{ - "nodeId": "string", // 节点ID过滤,可选 - "level": "INFO", // 日志级别过滤:DEBUG、INFO、WARN、ERROR,可选 - "startTime": "string", // 开始时间过滤,可选 - "endTime": "string" // 结束时间过滤,可选 -} -``` - -**响应数据**: -```json -{ - "code": 0, - "message": "成功", - "data": [{ - "id": "long", // 日志ID - "workflowInstanceId": "long", // 工作流实例ID - "nodeId": "string", // 节点ID - "level": "string", // 日志级别 - "content": "string", // 日志内容 - "detail": "string", // 详细信息 - "createTime": "string" // 创建时间 - }] -} -``` - -## 7. 节点类型接口 - -### 7.1 查询节点类型列表 - -**接口说明**:查询可用的节点类型列表 - -**请求路径**:GET /node-types - -**请求参数**: -```json -{ - "enabled": true, // 是否启用过滤,可选 - "category": "TASK" // 类型分类过滤:TASK、EVENT、GATEWAY,可选 -} -``` - -**响应数据**: -```json -{ - "code": 0, - "message": "成功", - "data": [{ - "id": "long", // 节点类型ID - "code": "string", // 节点类型编码 - "name": "string", // 节点类型名称 - "category": "string", // 分类 - "description": "string", // 描述 - "enabled": true, // 是否启用 - "icon": "string", // 图标 - "color": "string", // 颜色 - "executors": [{ // 执行器列表(仅TASK类型) - "code": "string", // 执行器编码 - "name": "string", // 执行器名称 - "description": "string", // 描述 - "configSchema": {} // 配置模式 - }], - "createTime": "string", // 创建时间 - "updateTime": "string" // 更新时间 - }] -} -``` - -### 7.2 获取节点执行器列表 - -**接口说明**:获取指定节点类型支持的执行器列表 - -**请求路径**:GET /node-types/{code}/executors - -**路径参数**: -- code: 节点类型编码 - -**响应数据**: -```json -{ - "code": 0, - "message": "成功", - "data": [{ - "code": "string", // 执行器编码 - "name": "string", // 执行器名称 - "description": "string", // 描述 - "configSchema": { // 配置模式 - "type": "object", - "properties": { - // 具体配置项定义 - }, - "required": [] // 必填项 - } - }] -} -``` - -## 8. 错误码说明 - -### 8.1 系统错误 (1xxx) -- 1000: 系统内部错误 -- 1001: 数据库操作失败 -- 1002: 并发操作冲突 - -### 8.2 通用业务错误 (2xxx) -- 2000: 参数验证失败 -- 2001: 数据不存在 -- 2002: 数据已存在 - -### 8.3 工作流定义错误 (3xxx) -- 3000: 工作流定义不存在 -- 3001: 工作流编码已存在 -- 3002: 工作流定义非草稿状态 -- 3003: 工作流定义未发布 -- 3004: 工作流定义已禁用 -- 3005: 节点配置无效 -- 3006: 流转配置无效 -- 3007: 表单配置无效 - -### 8.4 工作流实例错误 (4xxx) -- 4000: 工作流实例不存在 -- 4001: 工作流实例状态无效 -- 4002: 工作流实例已终止 -- 4003: 节点实例不存在 -- 4004: 变量类型无效 - -## 9. 数据结构说明 - -### 9.1 工作流状态 - -```typescript -enum WorkflowStatus { - DRAFT = "DRAFT", // 草稿 - PUBLISHED = "PUBLISHED", // 已发布 - DISABLED = "DISABLED" // 已禁用 -} -``` - -### 9.2 实例状态 - -```typescript -enum InstanceStatus { - CREATED = "CREATED", // 已创建 - RUNNING = "RUNNING", // 运行中 - SUSPENDED = "SUSPENDED", // 已暂停 - COMPLETED = "COMPLETED", // 已完成 - TERMINATED = "TERMINATED"// 已终止 -} -``` - -### 9.3 节点状态 - -```typescript -enum NodeStatus { - CREATED = "CREATED", // 已创建 - RUNNING = "RUNNING", // 运行中 - COMPLETED = "COMPLETED", // 已完成 - FAILED = "FAILED" // 失败 -} -``` - -### 9.4 日志级别 - -```typescript -enum LogLevel { - DEBUG = "DEBUG", - INFO = "INFO", - WARN = "WARN", - ERROR = "ERROR" -} -``` - -### 9.5 变量作用域 - -```typescript -enum VariableScope { - GLOBAL = "GLOBAL", // 全局变量 - NODE = "NODE" // 节点变量 -} -``` - -## 10. 最佳实践 - -### 10.1 工作流设计 - -1. 工作流定义创建流程: - - 创建草稿 - - 配置节点和流转 - - 验证配置 - - 发布使用 - -2. 节点类型选择: - - 根据业务场景选择合适的节点类型 - - 配置合适的执行器 - - 设置必要的变量 - -### 10.2 实例管理 - -1. 实例生命周期管理: - - 创建 -> 启动 -> 运行 -> 完成 - - 必要时可暂停或终止 - - 记录关键节点日志 - -2. 变量使用: - - 合理使用变量作用域 - - 及时清理无用变量 - - 注意变量类型匹配 - -### 10.3 错误处理 - -1. 异常处理: - - 捕获所有可能的错误码 - - 提供友好的错误提示 - - 记录详细的错误日志 - -2. 重试机制: - - 对非致命错误进行重试 - - 设置合理的重试间隔 - - 限制最大重试次数 - -### 10.4 性能优化 - -1. 查询优化: - - 合理使用分页 - - 避免大量数据查询 - - 使用合适的查询条件 - -2. 缓存使用: - - 缓存常用数据 - - 及时更新缓存 - - 避免缓存穿透 \ No newline at end of file diff --git a/backend/docs/architecture.md b/backend/docs/architecture.md deleted file mode 100644 index b2a7c1fd..00000000 --- a/backend/docs/architecture.md +++ /dev/null @@ -1,1598 +0,0 @@ -# 工作流引擎架构文档 - -## 1. 系统架构 - -### 1.1 整体架构 -``` -com.qqchen.deploy.backend.workflow -├── engine/ # 工作流引擎核心 -│ ├── context/ # 工作流上下文 -│ ├── executor/ # 节点执行器 -│ ├── parser/ # 工作流定义解析器 -│ ├── transition/ # 流转控制 -│ └── node/ # 节点实现 -├── entity/ # 实体类 -├── repository/ # 数据访问层 -├── service/ # 业务逻辑层 -├── dto/ # 数据传输对象 -├── converter/ # 对象转换器 -├── query/ # 查询条件 -└── enums/ # 枚举定义 -``` - -### 1.2 核心模块说明 - -#### 1.2.1 工作流引擎核心 (engine) -- **WorkflowEngine**: 工作流引擎接口,定义工作流生命周期管理方法 -- **DefaultWorkflowEngine**: 默认实现,负责工作流实例的创建、执行、暂停、恢复等 -- **WorkflowContext**: 工作流上下文,管理工作流执行过程中的变量和状态 -- **NodeExecutor**: 节点执行器接口,不同类型节点的执行实现 -- **TransitionExecutor**: 流转执行器,负责节点间的流转逻辑 - -#### 1.2.2 实体模型 (entity) -- **WorkflowDefinition**: 工作流定义,包含节点和流转配置 -- **WorkflowInstance**: 工作流实例,工作流定义的运行时实例 -- **NodeInstance**: 节点实例,工作流中具体节点的运行时实例 -- **NodeConfig**: 节点配置,定义节点的属性和行为 -- **TransitionConfig**: 流转配置,定义节点间的连接和条件 -- **WorkflowVariable**: 工作流变量,存储工作流执行过程中的数据 -- **WorkflowLog**: 工作流日志,记录工作流执行过程 - -#### 1.2.3 数据访问层 (repository) -- **IWorkflowDefinitionRepository**: 工作流定义仓库 -- **IWorkflowInstanceRepository**: 工作流实例仓库 -- **INodeInstanceRepository**: 节点实例仓库 -- **IWorkflowVariableRepository**: 工作流变量仓库 -- **IWorkflowLogRepository**: 工作流日志仓库 - -#### 1.2.4 业务逻辑层 (service) -- **IWorkflowDefinitionService**: 工作流定义服务 -- **IWorkflowInstanceService**: 工作流实例服务 -- **IWorkflowVariableService**: 工作流变量服务 -- **IWorkflowLogService**: 工作流日志服务 - -## 2. 已实现功能 - -### 2.1 工作流定义 -- [x] 工作流基本信息管理(创建、修改、删除) -- [x] 工作流状态管理(草稿、发布、禁 -- [x] 节点配置管理 -- [x] 流转配置管理 -- [x] 配置验证 - -### 2.2 工作流执行 -- [x] 工作流实例创建 -- [x] 节点执行框架 -- [x] 基本流转控制 -- [x] 变量管理 -- [x] 日志记录 - -### 2.3 节点类型 -- [x] 开始节点 -- [x] 结束节点 -- [x] 任务节点 - - [x] Shell执行器 - - [x] HTTP执行器 - - [x] Java执行器 -- [x] 网关节点 - - [x] 排他网关 - - [x] 并行网关 - - [x] 包容网关 - -### 2.4 基础设施 -- [x] 数据库表结构 -- [x] 基础异常处理 -- [x] 基本日志记录 -- [x] DTO/Entity转换 - -## 3. 待改进功能 - -### 3.1 核心功能完善 - -#### 3.1.1 工作流定义 -- [ ] 工作流定义版本控制 -- [ ] 工作流模板管理 -- [ ] 工作流导入导出 -- [ ] 工作流克隆 -- [ ] 表单配置管理 - -#### 3.1.2 工作流执行 -- [ ] 工作流实例暂停/恢复 -- [ ] 子流程支持 -- [ ] 定时调度 -- [ ] 条件表达式引擎优化 -- [ ] 变量作用域隔离 -- [ ] 变量类型自动转换 -- [ ] 变量生命周期管理 - -#### 3.1.3 节点功能 -- [ ] 节点执行重试机制 -- [ ] 节点超时控制 -- [ ] 节点执行资源限制 -- [ ] Shell执行安全控制 -- [ ] HTTP请求重试配置 -- [ ] 自定义节点扩展机制 - -### 3.2 性能优化 - -#### 3.2.1 数据库优化 -- [ ] 添加必要索引 - - [ ] NodeConfig表工作定ID索引 - - [ ] NodeConfig表节点类型索引 - - [ ] TransitionConfig表源节点和目标节点索引 -- [ ] 大表分表策略 - - [ ] 工作流实例表 - - [ ] 节点实例表 - - [ ] 工作流日志表 -- [ ] 数据库连接池优化 - -#### 3.2.2 并发控制 -- [ ] 工作流实例锁机制 -- [ ] 节点实例锁机制 -- [ ] 分布式锁实现 -- [ ] 并发限制 - -#### 3.2.3 缓存优化 -- [ ] 工作流定义缓存 -- [ ] 节点配置缓存 -- [ ] 变量缓存 - -### 3.3 可用性提升 - -#### 3.3.1 高可用 -- [ ] 工作流热备份 -- [ ] 故障自动恢复 -- [ ] 限流熔断 -- [ ] 任务队列 - -#### 3.3.2 监控运维 -- [ ] 工作流监控指标 -- [ ] 性能统计 -- [ ] 日志归档 -- [ ] 数据清理策略 - -### 3.4 安全性增强 - -#### 3.4.1 访问控制 -- [ ] 工作流权限管理 -- [ ] 节点权限控制 -- [ ] 变量访问控制 -- [ ] 操作审计日志 - -#### 3.4.2 数据安全 -- [ ] 敏感数据加密 -- [ ] 节点执行隔离 -- [ ] 资源使用限制 -- [ ] 安全规则配置 - -## 4. 技术栈 - -### 4.1 基础框架 -- Spring Boot -- Spring Data JPA -- Hibernate -- MapStruct - -### 4.2 数据库 -- MySQL -- Flyway (数据库版本管理) - -### 4.3 工具库 -- Jackson (JSON处理) -- Apache Commons -- Lombok - -## 5. 开发规范 - -### 5.1 代码规范 -- 遵循阿里巴巴Java开发手册 -- 使用统一的代码格式化配置 -- 必要的注释和文档 - -### 5.2 命名规范 -- 包名:com.qqchen.deploy.backend.workflow.* -- 类名:驼峰命名,见名知意 -- 方法名:动词开头,驼峰命名 -- 变量名:驼峰命名,有意义的名称 - -### 5.3 异常处理 -- 使用自定义异常类 -- 统一的错误码管理 -- 详细的错误信息 - -### 5.4 日志规范 -- 使用SLF4J + Logback -- 分级别记录日志 -- 包含必要的上下文信息 - -## 6. 部署要求 - -### 6.1 环境要求 -- JDK 17+ -- MySQL 8.0+ -- Maven 3.8+ - -### 6.2 配置要求 -- 数据库连接配置 -- 线程池配置 -- 日志配置 -- 缓存配置 - -### 6.3 监控要求 -- JVM监控 -- 数据库监控 -- 业务监控 -- 日志监控 - -## 7. API接口说明 - -### 7.1 工作流定义接口 - -#### 7.1.1 已实现接口 -- 暂无已实现的HTTP接口,但Service层已实现以下功能: - - 创建工作流定义 - - 更新工作流定义 - - 删除工作流定义 - - 查询工作流定义列表 - - 获取工作流定义详情 - -### 7.2 工作流实例接口 - -#### 7.2.1 已实现接口 -- 暂无已实现的HTTP接口,但Service层已实现以下功能: - - 创建工作流实例 - - 查询工作流实例列表 - - 获取工作流实例详情 - -### 7.3 节点实例接口 - -#### 7.3.1 已实现接口 -- 暂无已实现的HTTP接口,但Service层已实现以下功能: - - 查询节点实例列表 - - 获取节点实例详情 - -### 7.4 工作流变量接口 - -#### 7.4.1 已实现接口 -- 暂无已实现的HTTP接口,但Service层已实现以下功能: - - 查询工作流变量列表 - -### 7.5 工作流日志接口 - -#### 7.5.1 已实现接口 -- 暂无已实现的HTTP接口,但Service层已实现以下功能: - - 查询工作流日志列表 - -### 7.6 数据结构说明 - -#### 7.6.1 工作流定义相关 - -1. **WorkflowDefinitionDTO** -```java -{ - Long id; // 主键ID - String name; // 工作流名称 - String description; // 工作流描述 - WorkflowStatusEnum status; // 状态(DRAFT/PUBLISHED/DISABLED) - List nodes; // 节点配置列表 - List transitions; // 流转配置列表 - String createBy; // 创建人 - LocalDateTime createTime; // 创建时间 - String updateBy; // 更新人 - LocalDateTime updateTime; // 更新时间 - Integer version; // 版本号 -} -``` - -2. **NodeConfig** -```java -{ - String id; // 节点ID - String name; // 节点名称 - NodeTypeEnum type; // 节点类型 - Map config; // 节点配置(JSON) -} -``` - -3. **TransitionConfig** -```java -{ - String id; // 流转ID - String sourceNodeId; // 源节点ID - String targetNodeId; // 目标节点ID - String condition; // 流转条件 -} -``` - -#### 7.6.2 工作流实例相关 - -1. **WorkflowInstanceDTO** -```java -{ - Long id; // 主键ID - Long definitionId; // 工作流定义ID - String name; // 工作流名称 - WorkflowStatusEnum status; // 状态 - String createBy; // 创建人 - LocalDateTime createTime; // 创建时间 - String updateBy; // 更新人 - LocalDateTime updateTime; // 更新时间 -} -``` - -2. **NodeInstanceDTO** -```java -{ - Long id; // 主键ID - Long workflowInstanceId; // 工作流实例ID - String nodeId; // 节点定义ID - String nodeName; // 节点名称 - NodeTypeEnum nodeType; // 节点类型 - NodeStatusEnum status; // 节点状态 - LocalDateTime startTime; // 开始时间 - LocalDateTime endTime; // 结束时间 - String error; // 错误信息 -} -``` - -#### 7.6.3 工作流变量相关 - -1. **WorkflowVariableDTO** -```java -{ - Long id; // 主键ID - Long workflowInstanceId; // 工作流实例ID - Long nodeInstanceId; // 节点实例ID - String name; // 变量名 - String value; // 变量值 - VariableScopeEnum scope; // 作用域 -} -``` - -#### 7.6.4 工作流日志相关 - -1. **WorkflowLogDTO** -```java -{ - Long id; // 主键ID - Long workflowInstanceId; // 工作流实例ID - Long nodeInstanceId; // 节点实例ID - String level; // 日志级别 - String content; // 日志内容 - LocalDateTime createTime; // 创建时间 -} -``` - -### 7.7 枚举值说明 - -1. **WorkflowStatusEnum** -```java -{ - DRAFT, // 草稿 - PUBLISHED, // 已发布 - DISABLED // 已禁用 -} -``` - -2. **NodeTypeEnum** -```java -{ - START, // 开始节点 - END, // 结束节点 - TASK, // 任务节点 - GATEWAY // 网关节点 -} -``` - -3. **NodeStatusEnum** -```java -{ - PENDING, // 待执行 - RUNNING, // 执行中 - COMPLETED, // 已完成 - FAILED, // 执行失败 - SKIPPED // 已跳过 -} -``` - -4. **VariableScopeEnum** -```java -{ - GLOBAL, // 全局变量 - WORKFLOW, // 工作流级变量 - NODE // 节点级变量 -} -``` - -### 7.8 错误码说明 - -1. **系统错误 (1xxx)** -``` -1000: 系统内��错误 -1001: 数据库操作失败 -1002: 并发操作冲突 -``` - -2. **工作流定义错误 (2xxx)** -``` -2000: 工作流定义不存在 -2001: 工作流定义名称重复 -2002: 非法状态转换 -2003: 节点配置无效 -2004: 流转配置无效 -``` - -3. **工作流实例错误 (3xxx)** -``` -3000: 工作流实例不存在 -3001: 实例状态不允许操作 -3002: 节点执行失败 -3003: 变量不存在 -``` - -4. **权限错误 (4xxx)** -``` -4000: 无操作权限 -4001: 用户未认证 -4002: 用户未授权 -``` - -## 8. 详细设计 - -### 8.1 工作流引擎核心设计 - -#### 8.1.1 工作流定义解析 -1. **解析流程** -```java -WorkflowDefinition (JSON) -> WorkflowDefinitionParser -> RuntimeWorkflow -``` - -2. **主要组件** -- WorkflowDefinitionParser: 负责解析工作流定义 -- NodeParser: 负责解析节点配置 -- TransitionParser: 负责解析流转配置 -- ValidatorChain: 配置验证链 - -3. **验证规则** -- 节点完整性验证 -- 流转完整性验证 -- 起始节点验证 -- 结束节点验证 -- 环路检测 - -#### 8.1.2 工作流执行引擎 -1. **核心接口** -```java -public interface WorkflowEngine { - // 创建工作流实例 - WorkflowInstance createWorkflowInstance(Long definitionId, Map variables); - - // 执行工作流实例 - void executeWorkflowInstance(Long instanceId); - - // 暂停工作流实例 - void suspendWorkflowInstance(Long instanceId); - - // 恢复工作流实例 - void resumeWorkflowInstance(Long instanceId); - - // 终止工作流实例 - void terminateWorkflowInstance(Long instanceId); -} -``` - -2. **执行流程** -``` -开始 - ↓ -加载工作流定义 - ↓ -创建工作流上下文 - ↓ -执行开始节点 - ↓ -while (存在待执行节点) { - 获取下一个节点 - 执行节点 - 更新节点状态 - 处理节点输出 - 确定下一个节点 -} - ↓ -执行结束节点 - ↓ -结束 -``` - -3. **状态管理** -- 工作流状态机 -``` -PENDING → RUNNING → COMPLETED - ↓ ↓ ↑ - └→ FAILED ←┘ CANCELLED -``` - -- 节点状态机 -``` -PENDING → RUNNING → COMPLETED - ↓ ↓ ↑ - └→ FAILED ←┘ SKIPPED -``` - -#### 8.1.3 节点执行器 -1. **基础抽象类** -```java -public abstract class AbstractNodeExecutor implements NodeExecutor { - // 执行前处理 - protected void beforeExecute(NodeInstance node, WorkflowContext context); - - // 执行节点 - protected abstract ExecuteResult doExecute(NodeInstance node, WorkflowContext context); - - // 执行后处理 - protected void afterExecute(NodeInstance node, WorkflowContext context, ExecuteResult result); - - // 异常处理 - protected void handleException(NodeInstance node, WorkflowContext context, Exception e); -} -``` - -2. **内置执行器** -- StartNodeExecutor: 启动节点执行器 -- EndNodeExecutor: 结束节点执行器 -- TaskNodeExecutor: 任务节点执行器 -- GatewayNodeExecutor: 网关节点执行器 - -3. **任务执行器** -- ShellTaskExecutor: Shell命令执行器 -- HttpTaskExecutor: HTTP请求执行器 -- JavaTaskExecutor: Java代码执行器 - -#### 8.1.4 流转控制 -1. **流转规则引擎** -```java -public interface TransitionRuleEngine { - // 评估流转条件 - boolean evaluate(String condition, WorkflowContext context); - - // 获取下一个节点 - List getNextNodes(String currentNodeId, WorkflowContext context); -} -``` - -2. **条件表达式** -- 支持SpEL表达式 -- 支持变量引用 -- 支持函数调用 -- 支持逻辑运算 - -3. **网关类型** -- 排他网关(XOR):只选择一个分支 -- 并行网关(AND):并行执行所有分支 -- 包容网关(OR):选择满足条件的所有分支 - -### 8.2 数据模型设计 - -#### 8.2.1 工作流定义相关表 -1. **工作流定义表 (wf_workflow_definition)** -```sql -CREATE TABLE wf_workflow_definition ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - create_by VARCHAR(255) NULL COMMENT '创建人', - create_time DATETIME(6) NULL COMMENT '创建时间', - deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除', - update_by VARCHAR(255) NULL COMMENT '更新人', - update_time DATETIME(6) NULL COMMENT '更新时间', - version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', - - code VARCHAR(100) NOT NULL COMMENT '工作流编码', - name VARCHAR(100) NOT NULL COMMENT '工作流名称', - description VARCHAR(255) NULL COMMENT '工作流描述', - status VARCHAR(20) NOT NULL COMMENT '工作流状态', - version_no INT NOT NULL DEFAULT 1 COMMENT '版本号', - node_config TEXT NULL COMMENT '节点配置(JSON)', - transition_config TEXT NULL COMMENT '流转配置(JSON)', - form_definition TEXT NULL COMMENT '表单定义(JSON)', - graph_definition TEXT NULL COMMENT '图形信息(JSON)', - enabled BIT NOT NULL DEFAULT 1 COMMENT '是否启用', - - CONSTRAINT UK_workflow_definition_code_version UNIQUE (code, version_no) -); -``` - -2. **节点定义表 (wf_node_definition)** -```sql -CREATE TABLE wf_node_definition ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - create_by VARCHAR(255) NULL COMMENT '创建人', - create_time DATETIME(6) NULL COMMENT '创建时间', - deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除', - update_by VARCHAR(255) NULL COMMENT '更新人', - update_time DATETIME(6) NULL COMMENT '更新时间', - version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', - - workflow_definition_id BIGINT NOT NULL COMMENT '工作流定义ID', - node_id VARCHAR(100) NOT NULL COMMENT '节点ID', - name VARCHAR(100) NOT NULL COMMENT '节点名称', - type VARCHAR(20) NOT NULL COMMENT '节点类型', - config TEXT NULL COMMENT '节点配置(JSON)', - order_num INT NOT NULL DEFAULT 0 COMMENT '排序号', - - CONSTRAINT FK_node_definition_workflow FOREIGN KEY (workflow_definition_id) - REFERENCES wf_workflow_definition (id), - CONSTRAINT UK_node_definition_workflow_node UNIQUE (workflow_definition_id, node_id) -); -``` - -3. **节点类型表 (wf_node_type)** -```sql -CREATE TABLE wf_node_type ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - create_by VARCHAR(255) NULL COMMENT '创建人', - create_time DATETIME(6) NULL COMMENT '创建时间', - deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除', - update_by VARCHAR(255) NULL COMMENT '更新人', - update_time DATETIME(6) NULL COMMENT '更新时间', - version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', - - code VARCHAR(100) NOT NULL COMMENT '节点类型编码', - name VARCHAR(100) NOT NULL COMMENT '节点类型名称', - description TEXT NULL COMMENT '节点类型描述', - category VARCHAR(50) NOT NULL COMMENT '节点类型分类', - icon VARCHAR(100) NULL COMMENT '节点图标', - color VARCHAR(20) NULL COMMENT '节点颜色', - executors TEXT NULL COMMENT '执行器列表(JSON)', - config_schema TEXT NULL COMMENT '节点配置模式(JSON)', - default_config TEXT NULL COMMENT '默认配置(JSON)', - enabled BIT NOT NULL DEFAULT 1 COMMENT '是否启用', - - CONSTRAINT UK_node_type_code UNIQUE (code) -); -``` - -#### 8.2.2 工作流实例相关表 -1. **工作流实例表 (wf_workflow_instance)** -```sql -CREATE TABLE wf_workflow_instance ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - create_by VARCHAR(255) NULL COMMENT '创建人', - create_time DATETIME(6) NULL COMMENT '创建时间', - deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除', - update_by VARCHAR(255) NULL COMMENT '更新人', - update_time DATETIME(6) NULL COMMENT '更新时间', - version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', - - workflow_definition_id BIGINT NOT NULL COMMENT '工作流定义ID', - business_key VARCHAR(100) NOT NULL COMMENT '业务标识', - status VARCHAR(20) NOT NULL COMMENT '状态', - start_time DATETIME(6) NULL COMMENT '开始时间', - end_time DATETIME(6) NULL COMMENT '结束时间', - error TEXT NULL COMMENT '错误信息', - - CONSTRAINT FK_workflow_instance_definition FOREIGN KEY (workflow_definition_id) - REFERENCES wf_workflow_definition (id), - CONSTRAINT UK_workflow_instance_business_key UNIQUE (business_key) -); -``` - -2. **节点实例表 (wf_node_instance)** -```sql -CREATE TABLE wf_node_instance ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - create_by VARCHAR(255) NULL COMMENT '创建人', - create_time DATETIME(6) NULL COMMENT '创建时间', - deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除', - update_by VARCHAR(255) NULL COMMENT '更新人', - update_time DATETIME(6) NULL COMMENT '更新时间', - version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', - - workflow_instance_id BIGINT NOT NULL COMMENT '工作流实例ID', - node_id VARCHAR(100) NOT NULL COMMENT '节点ID', - node_type VARCHAR(20) NOT NULL COMMENT '节点类型', - name VARCHAR(100) NOT NULL COMMENT '节点名称', - status VARCHAR(20) NOT NULL COMMENT '状态', - start_time DATETIME(6) NULL COMMENT '开始时间', - end_time DATETIME(6) NULL COMMENT '结束时间', - config TEXT NULL COMMENT '节点配置(JSON)', - input TEXT NULL COMMENT '输入参���(JSON)', - output TEXT NULL COMMENT '输出结果(JSON)', - error TEXT NULL COMMENT '错误信息', - pre_node_id VARCHAR(100) NULL COMMENT '前置节点ID', - - CONSTRAINT FK_node_instance_workflow FOREIGN KEY (workflow_instance_id) - REFERENCES wf_workflow_instance (id) -); -``` - -#### 8.2.3 工作流变量表 (wf_workflow_variable) -```sql -CREATE TABLE wf_workflow_variable ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - create_by VARCHAR(255) NULL COMMENT '创建人', - create_time DATETIME(6) NULL COMMENT '创建时间', - deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除', - update_by VARCHAR(255) NULL COMMENT '更新人', - update_time DATETIME(6) NULL COMMENT '更新时间', - version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', - - workflow_instance_id BIGINT NOT NULL COMMENT '工作流实例ID', - name VARCHAR(100) NOT NULL COMMENT '变量名', - value TEXT NULL COMMENT '变量值', - type VARCHAR(20) NOT NULL COMMENT '变量类型', - - CONSTRAINT FK_workflow_variable_instance FOREIGN KEY (workflow_instance_id) - REFERENCES wf_workflow_instance (id), - CONSTRAINT UK_workflow_variable_instance_name UNIQUE (workflow_instance_id, name) -); -``` - -#### 8.2.4 工作流日志表 (wf_log) -```sql -CREATE TABLE wf_log ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - workflow_instance_id BIGINT NOT NULL, - node_id VARCHAR(50), - message VARCHAR(1000) NOT NULL, - level VARCHAR(20) NOT NULL, - detail TEXT, - create_time DATETIME NOT NULL, - create_by VARCHAR(50), - update_time DATETIME, - update_by VARCHAR(50), - version INT DEFAULT 0, - deleted BOOLEAN DEFAULT FALSE -); - --- 索引 -CREATE INDEX idx_wf_log_instance ON wf_log (workflow_instance_id); -CREATE INDEX idx_wf_log_node ON wf_log (workflow_instance_id, node_id); -``` - -#### 8.2.5 工作流权限表 (wf_workflow_permission) -```sql -CREATE TABLE wf_workflow_permission ( - id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键', - workflow_definition_id BIGINT NOT NULL COMMENT '工作流定义ID', - type VARCHAR(50) NOT NULL COMMENT '权限类型', - user_id BIGINT COMMENT '用户ID', - role_id BIGINT COMMENT '角色ID', - department_id BIGINT COMMENT '部门ID', - create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - create_by BIGINT NOT NULL COMMENT '创建人', - update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - update_by BIGINT NOT NULL COMMENT '更新人', - version INT NOT NULL DEFAULT 0 COMMENT '版本号', - deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否删除', - - INDEX idx_workflow_definition_id (workflow_definition_id), - INDEX idx_user_id (user_id), - INDEX idx_role_id (role_id), - INDEX idx_department_id (department_id) -); -``` - -### 8.3 核心功能设计 - -#### 8.3.1 版本控制 -1. **版本策略** -- 主版本号:重大更新 -- 次版本号:功能更新 -- 修订版本号:Bug修复 - -2. **版本管理** -- 草稿版本:可以修改 -- 发布版本:只读 -- 禁用版本:不可用 - -3. **版本操作** -- 创建新版本 -- 复制版本 -- 回滚版本 -- 删除版本 - -#### 8.3.2 变量管理 -1. **变量作用域** -- 全局变量:整个工作流可见 -- 工作流变量:当前工作流实例可见 -- 节点变量:当前节点可见 - -2. **变量生命周期** -- 创建:工作流启动时 -- 更新:节点执行时 -- 销毁:工作流结束时 - -3. **变量操作** -- 设置变量 -- 获取变量 -- 删除变量 -- 清空变量 - -#### 8.3.3 任务执行 -1. **Shell任务** -```java -public class ShellTaskExecutor implements TaskExecutor { - ExecuteResult execute(TaskConfig config) { - // 1. 解析命令 - String command = config.getCommand(); - - // 2. 设置环境变量 - Map env = config.getEnvironment(); - - // 3. 执行命令 - Process process = Runtime.getRuntime().exec(command); - - // 4. 获取结果 - String output = readOutput(process); - - // 5. 检查退出码 - int exitCode = process.waitFor(); - - return new ExecuteResult(exitCode == 0, output); - } -} -``` - -2. **HTTP任务** -```java -public class HttpTaskExecutor implements TaskExecutor { - ExecuteResult execute(TaskConfig config) { - // 1. 构建请求 - HttpRequest request = buildRequest(config); - - // 2. 发送请求 - HttpResponse response = httpClient.send(request); - - // 3. 处理响应 - String body = response.body(); - - // 4. 检查状态码 - boolean success = response.statusCode() >= 200 && response.statusCode() < 300; - - return new ExecuteResult(success, body); - } -} -``` - -3. **Java任务** -```java -public class JavaTaskExecutor implements TaskExecutor { - ExecuteResult execute(TaskConfig config) { - // 1. 获取类名和方法名 - String className = config.getClassName(); - String methodName = config.getMethodName(); - - // 2. 加载类 - Class clazz = Class.forName(className); - - // 3. 获取方法 - Method method = clazz.getMethod(methodName); - - // 4. 执行方法 - Object result = method.invoke(null); - - return new ExecuteResult(true, result.toString()); - } -} -``` - -#### 8.3.4 错误处理 -1. **重试机制** -```java -public class RetryableTaskExecutor implements TaskExecutor { - ExecuteResult execute(TaskConfig config) { - int maxAttempts = config.getMaxAttempts(); - long delay = config.getDelay(); - - for (int attempt = 1; attempt <= maxAttempts; attempt++) { - try { - return delegate.execute(config); - } catch (Exception e) { - if (attempt == maxAttempts) { - throw e; - } - Thread.sleep(delay); - } - } - } -} -``` - -2. **补偿机制** -```java -public class CompensableTaskExecutor implements TaskExecutor { - ExecuteResult execute(TaskConfig config) { - try { - ExecuteResult result = delegate.execute(config); - if (!result.isSuccess()) { - compensate(config); - } - return result; - } catch (Exception e) { - compensate(config); - throw e; - } - } - - void compensate(TaskConfig config) { - // 执行补偿逻辑 - } -} -``` - -3. **超时控制** -```java -public class TimeoutTaskExecutor implements TaskExecutor { - ExecuteResult execute(TaskConfig config) { - Future future = executor.submit(() -> { - return delegate.execute(config); - }); - - try { - return future.get(config.getTimeout(), TimeUnit.SECONDS); - } catch (TimeoutException e) { - future.cancel(true); - throw new TaskTimeoutException(); - } - } -} -``` - -### 8.4 扩展点设计 - -#### 8.4.1 节点类型扩展 -1. **自定义节点** -```java -@Component -public class CustomNodeExecutor extends AbstractNodeExecutor { - @Override - protected ExecuteResult doExecute(NodeInstance node, WorkflowContext context) { - // 实现自定义节点逻辑 - } -} -``` - -2. **节点注册** -```java -public class NodeExecutorRegistry { - private Map executors = new HashMap<>(); - - public void register(String type, NodeExecutor executor) { - executors.put(type, executor); - } - - public NodeExecutor get(String type) { - return executors.get(type); - } -} -``` - -#### 8.4.2 表达式扩展 -1. **自定义函数** -```java -public class CustomFunctions { - @Function - public static boolean checkCondition(String value) { - // 实现自定义函数逻辑 - } -} -``` - -2. **函数注册** -```java -public class FunctionRegistry { - private Map functions = new HashMap<>(); - - public void register(String name, Method method) { - functions.put(name, method); - } - - public Method get(String name) { - return functions.get(name); - } -} -``` - -#### 8.4.3 变量处理扩展 -1. **自定义变量处理器** -```java -public class CustomVariableHandler implements VariableHandler { - @Override - public Object serialize(Object value) { - // 实现序列化逻辑 - } - - @Override - public Object deserialize(String value, Class type) { - // 实现反序列化逻辑 - } -} -``` - -2. **处理器注册** -```java -public class VariableHandlerRegistry { - private Map, VariableHandler> handlers = new HashMap<>(); - - public void register(Class type, VariableHandler handler) { - handlers.put(type, handler); - } - - public VariableHandler get(Class type) { - return handlers.get(type); - } -} -``` - -### 8.5 性能优化设计 - -#### 8.5.1 数据库优化 -1. **索引设计** -```sql --- 工作流定义表索引 -ALTER TABLE workflow_definition ADD INDEX idx_code (code); -ALTER TABLE workflow_definition ADD INDEX idx_status (status); - --- 工作流实例表索引 -ALTER TABLE workflow_instance ADD INDEX idx_definition_id (definition_id); -ALTER TABLE workflow_instance ADD INDEX idx_status (status); -ALTER TABLE workflow_instance ADD INDEX idx_create_time (create_time); - --- 节点实例表索引 -ALTER TABLE node_instance ADD INDEX idx_workflow_instance_id (workflow_instance_id); -ALTER TABLE node_instance ADD INDEX idx_status (status); - --- 工作流变量表索引 -ALTER TABLE workflow_variable ADD INDEX idx_workflow_instance_id (workflow_instance_id); -ALTER TABLE workflow_variable ADD INDEX idx_scope (scope); -``` - -2. **分表策略** -- 按时间分表 -- 按工作流类型分表 -- 按租户分表 - -3. **归档策略** -- 定期归档历史数据 -- 按时间维度归档 -- 支持归档数据查询 - -#### 8.5.2 缓存设计 -1. **缓存层次** -``` -应用缓存 → 分布式缓存 → 数据库 -``` - -2. **缓存对���** -- 工作流定义 -- 节点配置 -- 常用变量 -- 执行统计 - -3. **缓存策略** -- LRU淘汰 -- 定时刷新 -- 主动失效 - -#### 8.5.3 并发控制 -1. **锁设计** -```java -public interface LockManager { - // 获取锁 - boolean acquireLock(String key, long timeout); - - // 释放锁 - void releaseLock(String key); - - // 续期锁 - boolean renewLock(String key); -} -``` - -2. **隔离级别** -- 工作流实例级 -- 节点实例级 -- 变量级 - -3. **并发策略** -- 乐观锁 -- 悲观锁 -- 分布式锁 - -### 8.6 监控运维设计 - -#### 8.6.1 监控指标 -1. **性能指标** -- 工作流执行时间 -- 节点执行时间 -- 资源使用率 -- 并发数量 - -2. **业务指标** -- 工作流成功率 -- 节点成功率 -- 重试次数 -- 超次数 - -3. **系统指标** -- CPU使用率 -- 内存使用率 -- 磁盘使用率 -- 网络使用率 - -#### 8.6.2 告警设计 -1. **告警规则** -- 执行超时 -- 异常失败 -- 资源不足 -- 并发超限 - -2. **告警级别** -- 严重告警 -- 警告告警 -- 提示告警 - -3. **告警方式** -- 邮件通知 -- 短信通知 -- 钉钉通知 -- Webhook通知 - -#### 8.6.3 日志设计 -1. **日志分类** -- 系统日志 -- 业务日志 -- 审计日志 -- 性能日志 - -2. **日志格式** -```json -{ - "timestamp": "2023-12-05 10:00:00", - "level": "INFO", - "thread": "main", - "class": "WorkflowEngine", - "message": "工作流开始执行", - "context": { - "workflowInstanceId": "123", - "nodeId": "node1", - "variables": {} - } -} -``` - -3. **日志存储** -- 文件存储 -- 数据库存储 -- ES存储 - -### 8.7 安全设计 - -#### 8.7.1 认证授权 -1. **认证方式** -- 用户名密码 -- Token认证 -- OAuth2认证 -- SSO认证 - -2. **权限模型** -- 角色权限 -- 数据权限 -- 功能权限 -- 字段权限 - -3. **权限控制** -```java -@PreAuthorize("hasPermission('workflow', 'execute')") -public void executeWorkflow(Long instanceId) { - // 执行工作流 -} -``` - -#### 8.7.2 数据安全 -1. **敏感数据** -- 配置信息 -- 执行结果 -- 变量数据 -- 日志内容 - -2. **加密方案** -- 配置加密 -- 传输加密 -- 存储加密 -- 字段加密 - -3. **脱敏策略** -```java -public class DataMaskingUtils { - public static String maskSensitiveData(String data, String type) { - switch (type) { - case "mobile": - return maskMobile(data); - case "email": - return maskEmail(data); - case "idcard": - return maskIdCard(data); - default: - return data; - } - } -} -``` - -#### 8.7.3 审计日志 -1. **审计内容** -- 操作人 -- 操作时间 -- 操作类型 -- 操作内容 -- 操作结果 - -2. **审计方式** -```java -@Audit(type = "workflow", operation = "execute") -public void executeWorkflow(Long instanceId) { - // 执行工作流 -} -``` - -3. **审计存储** -```sql -CREATE TABLE audit_log ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - user_id VARCHAR(50) NOT NULL COMMENT '用户ID', - operation_type VARCHAR(50) NOT NULL COMMENT '操作类型', - operation_time DATETIME NOT NULL COMMENT '操作时间', - resource_type VARCHAR(50) NOT NULL COMMENT '资源类型', - resource_id VARCHAR(50) NOT NULL COMMENT '资源ID', - operation_content TEXT COMMENT '操作内容', - operation_result VARCHAR(20) COMMENT '操作结果', - client_ip VARCHAR(50) COMMENT '客户端IP', - user_agent VARCHAR(200) COMMENT '用户代理' -); -``` - -## 9. 接口实现状态 - -### 9.1 工作流定义服务 (IWorkflowDefinitionService) - -#### 9.1.1 已实现接口 - -1. **创建工作流定义** -```java -WorkflowDefinitionDTO create(WorkflowDefinitionDTO dto) -``` -- 功能:创建新的工作流定义 -- 参数: - - dto: 工作流定义数据,包含名称、描述、节点配置等 -- 返回:创建后的工作流定义 -- 说明: - - 自动设置为草稿状态 - - 检查编码唯一性 - - 启用状态默认为true - -2. **更新工作流定义** -```java -WorkflowDefinitionDTO update(Long id, WorkflowDefinitionDTO dto) -``` -- 功能:更新现有工作流定义 -- 参数: - - id: 工作流定义ID - - dto: 更新的数据 -- 返回:更新后的工作流定义 -- 说明: - - 仅草稿状态可更新 - - 检查编码唯一性 - - 支持更新基本信息、节点配置、流转配置等 - -3. **发布工作流定义** -```java -WorkflowDefinitionDTO publish(Long id) -``` -- 功能:发布工作流定义 -- 参数: - - id: 工作流定义ID -- 返回:发布后的工作流定义 -- 说明: - - 仅草稿状态可发布 - - 发布前进行配置验证 - - 发布后状态变更为PUBLISHED - -4. **禁用工作流定义** -```java -WorkflowDefinitionDTO disable(Long id) -``` -- 功能:禁用工作流定义 -- 参数: - - id: 工作流定义ID -- 返回:禁用后的工作流定义 -- 说明: - - 仅已发布状态可禁用 - - 禁用后状态变更为DISABLED - -5. **启用工作流定义** -```java -WorkflowDefinitionDTO enable(Long id) -``` -- 功能:启用工作流定义 -- 参数: - - id: 工作流定义ID -- 返回:启用后的工作流定义 -- 说明: - - 仅禁用状态可启用 - - 启用后状态变更为PUBLISHED - -6. **创建新版本** -```java -WorkflowDefinitionDTO createNewVersion(Long id) -``` -- 功能:基于现有工作流定义创建新版本 -- 参数: - - id: 源工作流定义ID -- 返回:新版本的工作流定义 -- 说明: - - 复制所有配置信息 - - 版本号自动递增 - - 新版本为草稿状态 - -7. **查询相关接口** -```java -WorkflowDefinitionDTO findLatestByCode(String code) -WorkflowDefinitionDTO findByCodeAndVersion(String code, Integer version) -List findAllVersions(String code) -``` -- 功能:查询工作流定义 -- 说明: - - 支持查询最新版本 - - 支持查询指定版本 - - 支持查询所有版本 - -8. **验证工作流定义** -```java -boolean validate(Long id) -``` -- 功能:验证工作流定义的完整性和正确性 -- 参数: - - id: 工作流定义ID -- 返回:验证结果 -- 说明: - - 验证节点配置完整性 - - 验证流转配置完整性 - - 验证表单定义完整性 - - 验证图形信息完整性 - -### 9.2 工作流实例服务 (IWorkflowInstanceService) - -#### 9.2.1 已实现接口 - -1. **基础CRUD接口** -- 继承自BaseService,包含基础的增删改查功能 - -#### 9.2.2 待实现接口 - -1. **创建工作流实例** -```java -WorkflowInstanceDTO createInstance(Long definitionId, Map variables) -``` -- 功能:创建工作流实例 -- 参数: - - definitionId: 工作流定义ID - - variables: 初始变量 -- 返回:创建的工作流实例 - -2. **启动工作流实例** -```java -void startInstance(Long instanceId) -``` -- 功能:启动工作流实例 -- 参数: - - instanceId: 实例ID - -3. **暂停工作流实例** -```java -void suspendInstance(Long instanceId) -``` -- 功能:暂停工作流实例 -- 参数: - - instanceId: 实例ID - -4. **恢复工作流实例** -```java -void resumeInstance(Long instanceId) -``` -- 功能:恢复工作流实例 -- 参数: - - instanceId: 实例ID - -5. **终止工作流实例** -```java -void terminateInstance(Long instanceId) -``` -- 功能:终止工作流实例 -- 参数: - - instanceId: 实例ID - -### 9.3 节点实例服务 (INodeInstanceService) - -#### 9.3.1 已实现接口 - -1. **查询节点实例** -```java -List findByWorkflowInstanceId(Long workflowInstanceId) -List findByWorkflowInstanceIdAndStatus(Long workflowInstanceId, NodeStatusEnum status) -``` -- 功能:查询节点实例列表 -- 参数: - - workflowInstanceId: 工作流实例ID - - status: 节点状态(可选) -- 返回:节点实例列表 - -2. **更新节点状态** -```java -void updateStatus(Long id, NodeStatusEnum status, String output, String error) -``` -- 功能:更新节点状态 -- 参数: - - id: 节点实例ID - - status: 新状态 - - output: 输出结果 - - error: 错误信息 -- 说明: - - 自动记录状态变更时间 - - 支持记录执行结果和错误信息 - -### 9.4 工作流变量服务 (IWorkflowVariableService) - -#### 9.4.1 已实现接口 - -1. **设置变量** -```java -void setVariable(Long workflowInstanceId, String name, Object value) -``` -- 功能:设置工作流变量 -- 参数: - - workflowInstanceId: 工作流实例ID - - name: 变量名 - - value: 变量值 -- 说明: - - 支持任意类型的变量值 - - 自动序列化和类型记录 - - 支持变量更新 - -2. **获取变量** -```java -Map getVariables(Long workflowInstanceId) -Map getVariablesByScope(Long workflowInstanceId, VariableScopeEnum scope) -``` -- 功能:获取工作流变量 -- 参数: - - workflowInstanceId: 工作流实例ID - - scope: 变量作用域(可选) -- 返回:变量Map -- 说明: - - 支持获取所有变量 - - 支持按作用域获取变量 - - 自动反序列化为原始类型 - -3. **删除变量** -```java -void deleteVariables(Long workflowInstanceId) -``` -- 功能:删除工作流变量 -- 参数: - - workflowInstanceId: 工作流实例ID -- 说明: - - 删除指定实例的所有变量 - -### 9.5 工作流日志服务 (IWorkflowLogService) - -#### 9.5.1 已实现接口 - -1. **记录日志** -```java -void log(Long workflowInstanceId, String nodeId, String message, LogLevelEnum level, String detail) -``` -- 功能:记录工作流日志 -- 参数: - - workflowInstanceId: 工作流实例ID - - nodeId: 节点ID(可选) - - message: 日志消息 - - level: 日志级别 - - detail: 详细信息 -- 说明: - - 支持不同级别的日志 - - 支持节点级别的日志 - - 支持详细信息记录 - -2. **查询日志** -```java -List getLogs(Long workflowInstanceId) -List getNodeLogs(Long workflowInstanceId, String nodeId) -``` -- 功能:查询工作流日志 -- 参数: - - workflowInstanceId: 工作流实例ID - - nodeId: 节点ID(可选) -- 返回:日志列表 -- 说明: - - 支持查询实例所有日志 - - 支持查询节点级别日志 - -3. **删除日志** -```java -void deleteLogs(Long workflowInstanceId) -``` -- 功能:删除工作流日志 -- 参数: - - workflowInstanceId: 工作流实例ID -- 说明: - - 删除指定实例的所有日志 - -### 9.6 节点类型服务 (INodeTypeService) - -#### 9.6.1 已实现接口 - -1. **查询节点类型** -```java -NodeTypeDTO findByCode(String code) -``` -- 功能:根据编码查询节点类型 -- 参数: - - code: 节点类型编码 -- 返回:节点类型信息 - -2. **获取执行器列表** -```java -List getExecutors(String code) -``` -- 功能:获取节点类型支持的执行器列表 -- 参数: - - code: 节点类型编码 -- 返回:执行器定义列表 -- 说明: - - 仅任务节点类型支持 - - 返回执行器配置模式 - - 支持多种执行器 - -3. **启用/禁用节点类型** -```java -void enable(Long id) -void disable(Long id) -``` -- 功能:启用或禁用节点类型 -- 参数: - - id: 节点类型ID -- 说明: - - 控制节点类型是否可用 - - 影响工作流设计时的节点选择 - -### 9.7 待实现功能 - -1. **工作流实例管理** -- 实例暂停/恢复机制 -- 实例强制终止 -- 实例重试机制 -- 子流程支持 - -2. **节点执行增强** -- 节点重试机制 -- 节点超时控制 -- 节点执行补偿 -- 自定义节点扩展 - -3. **变量管理增强** -- 变量作用域隔离 -- 变量类型转换 -- 变量生命周期管理 -- 变量访问控制 - -4. **监控和统计** -- 执行状态监控 -- 性能统计分析 -- 告警机制 -- 审计日志 - -5. **权限控制** -- 工作流权限管理 -- 节点权限控制 -- 变量访问权限 -- 操作审计 \ No newline at end of file diff --git a/backend/docs/backend-development-guide.md b/backend/docs/backend-development-guide.md deleted file mode 100644 index 750f325c..00000000 --- a/backend/docs/backend-development-guide.md +++ /dev/null @@ -1,176 +0,0 @@ -# 后端开发指南 - -## 待开发接口清单 - -### 1. 工作流定义管理 (WorkflowDefinitionApiController) - -#### 1.1 更新工作流定义 -```typescript -PUT /api/v1/workflow-definitions/{id} -Request: { - // 基本信息 - code: string; // 工作流编码 - name: string; // 工作流名称 - description: string; // 工作流描述 - version: number; // 版本号 - - // 设计数据 - design?: { - nodes: [{ - id: string; - type: string; - position: { x: number; y: number }; - data: { - name: string; - description?: string; - config: Record; - } - }]; - edges: [{ - id: string; - source: string; - target: string; - type: string; - data?: { - condition?: string; - } - }] - } -} -Response: { - code: number; - data: WorkflowDefinitionDTO; - message: string; -} -``` - -#### 1.2 导入工作流定义(待实现) -```typescript -POST /api/v1/workflow-definitions/import -Request: FormData { - file: File; // JSON格式的工作流定义文件 -} -Response: { - code: number; - data: WorkflowDefinitionDTO; - message: string; -} -``` - -#### 1.3 导出工作流定义(待实现) -```typescript -GET /api/v1/workflow-definitions/{id}/export -Response: File // JSON格式的工作流定义文件 -``` - -### 2. 工作流实例管理 (WorkflowInstanceApiController) - -#### 2.1 导出工作流实例 -```typescript -GET /api/v1/workflow-instances/export -Query Parameters: - - startTime?: string // 开始时间 - - endTime?: string // 结束时间 - - status?: string // 实例状态 - - keyword?: string // 关键字搜索 -Response: File // Excel格式的工作流实例数据 -``` - -### 3. 节点实例管理 (NodeInstanceApiController) - -#### 3.1 导出节点实例 -```typescript -GET /api/v1/node-instances/export -Query Parameters: - - workflowInstanceId?: string // 工作流实例ID - - nodeType?: string // 节点类型 - - status?: string // 节点状态 - - startTime?: string // 开始时间 - - endTime?: string // 结束时间 -Response: File // Excel格式的节点实例数据 -``` - -### 4. 节点类型管理 (NodeTypeApiController) - -#### 4.1 导出节点类型 -```typescript -GET /api/v1/node-types/export -Query Parameters: - - category?: string // 节点类型分类 - - enabled?: boolean // 是否启用 -Response: File // Excel格式的节点类型数据 -``` - -### 5. 工作流日志管理 (WorkflowLogApiController) - -#### 5.1 导出日志 -```typescript -GET /api/v1/workflow-logs/export -Query Parameters: - - workflowInstanceId?: string // 工作流实例ID - - nodeId?: string // 节点ID - - level?: string // 日志级别 - - startTime?: string // 开始时间 - - endTime?: string // 结束时间 -Response: File // Excel格式的日志数据 -``` - -## 开发注意事项 - -### 1. 工作流设计数据存储 -- 工作流设计数据应该存储在 `workflow_definition` 表的 `design_data` 字段中 -- 数据格式为JSON字符串 -- 需要实现JSON序列化和反序列化 - -### 2. 数据导出功能 -- 使用 Apache POI 处理Excel文件 -- 支持大数据量导出 -- 添加导出进度提示 -- 支持自定义导出字段 - -### 3. 数据验证 -- 工作流设计数据的完整性验证 -- 节点连接的有效性验证 -- 配置数据的格式验证 - -### 4. 错误处理 -- 添加详细的错误提示 -- 记录操作日志 -- 支持事务回滚 - -### 5. 性能优化 -- 大数据量导出时使用分页 -- 添加必要的索引 -- 使用缓存优化查询性能 - -## 开发优先级建议 - -1. 工作流设计数据的保存和获取 - - 这是工作流设计器的核心功能 - - 前端需要这些接口来实现基本功能 - -2. 工作流设计的验证 - - 确保工作流设计的正确性 - - 避免错误的工作流定义被发布 - -3. 工作流定义的导入导出 - - 支持工作流定义的迁移 - - 方便工作流的备份和恢复 - -4. 各个模块的数据导出 - - 用于数据分析和备份 - - 相对优先级较低 - -## 测试要求 - -1. 单元测试 - - 测试各个Service层的核心方法 - - 验证数据处理逻辑的正确性 - -2. 集成测试 - - 测试API接口的功能 - - 验证数据库操作的正确性 - -3. 性能测试 - - 测试大数据量导出的性能 - - 验证并发处理能力 \ No newline at end of file diff --git a/backend/docs/deploy-ease-platform-v2.md b/backend/docs/deploy-ease-platform-v2.md deleted file mode 100644 index 8ac7dc7f..00000000 --- a/backend/docs/deploy-ease-platform-v2.md +++ /dev/null @@ -1,347 +0,0 @@ -# Deploy Ease Platform V2 设计文档 - -## 一、系统概述 - -### 1.1 系统目标 -- 构建一个高度可扩展的自动化部署平台 -- 支持灵活的工作流配置和审批流程 -- 实现多环境、多租户的部署管理 -- 提供完整的日志追踪和监控能力 - -### 1.2 核心功能 -1. 项目管理 - - 项目集管理 - - 项目配置 - - Git仓库集成 - - Jenkins集成 - -2. 环境管理 - - 环境配置 - - 环境克隆 - - 环境变量管理 - - 权限控制 - -3. 工作流引擎 - - 流程设计 - - 节点执行 - - 状态管理 - - 日志追踪 - -4. 审批系统 - - 审批流程定义 - - 审批规则配置 - - 审批节点管理 - - 审批记录追踪 - -5. 配置中心 - - Nacos配置管理 - - 配置同步 - - 版本控制 - - 变更追踪 - -## 二、技术架构 - -### 2.1 技术选型 -- 后端框架:Spring Boot 3.2.0 -- Java版本:JDK 21 -- ORM框架:JPA + QueryDSL -- 安全框架:Spring Security + JWT -- 数据库:MySQL 8.0 -- 缓存:Redis -- 消息队列:RabbitMQ -- 服务注册:Nacos - -### 2.2 系统架构 -1. 微服务架构 - - 网关服务 - - 认证服务 - - 工作流引擎 - - 审批服务 - - 配置中心 - -2. 分层架构 - - 接口层(Controller) - - 业务层(Service) - - 数据访问层(Repository) - - 领域模型层(Domain) - -3. 插件化架构 - - 节点插件 - - 审批插件 - - 通知插件 - -## 三、数据库设计 - -### 3.1 核心表设计 -{{ ... }} - -### 3.2 工作流相关表 -{{ ... }} - -### 3.3 审批系统表 -1. 审批定义表(sys_approval_definition) - - 基础字段(id, tenant_id等) - - 流程编码(code) - - 流程名称(name) - - 流程描述(description) - - 业务类型(business_type) - - 节点配置(node_config) - - 表单配置(form_config) - -2. 审批流程表(sys_approval_flow) - - 基础字段 - - 业务类型(business_type) - - 业务键(business_key) - - 模板编码(template_code) - - 当前节点(current_node) - - 流程状态(status) - - 流程变量(variables) - -3. 审批节点表(sys_approval_node) - - 基础字段 - - 流程ID(flow_id) - - 节点编码(node_code) - - 节点名称(node_name) - - 节点类型(node_type) - - 审批人类型(approver_type) - - 审批人列表(approver_ids) - - 节点状态(status) - - 执行时间(start_time, end_time) - -4. 审批意见表(sys_approval_comment) - - 基础字段 - - 流程ID(flow_id) - - 节点ID(node_id) - - 审批人(user_id) - - 审批动作(action) - - 审批意见(comment) - - 附件信息(attachments) - -## 四、审批系统设计 - -### 4.1 审批系统架构 -1. 核心组件 - - 审批引擎(ApprovalEngine) - - 审批规则解析器(RuleParser) - - 审批节点执行器(NodeExecutor) - - 审批事件总线(EventBus) - -2. 扩展点 - - 审批规则插件 - - 审批节点类型 - - 审批表单定义 - - 审批通知方式 - -### 4.2 审批流程设计 -1. 流程定义 - ```json - { - "nodes": [ - { - "id": "start", - "type": "start", - "next": "approve1" - }, - { - "id": "approve1", - "type": "approve", - "config": { - "approverType": "ROLE", - "approverIds": ["ROLE_LEADER"], - "counterSign": false - }, - "next": "approve2" - }, - { - "id": "approve2", - "type": "approve", - "config": { - "approverType": "USER", - "approverIds": ["user1", "user2"], - "counterSign": true - }, - "next": "end" - }, - { - "id": "end", - "type": "end" - } - ] - } - ``` - -2. 审批规则 - ```json - { - "rules": [ - { - "condition": { - "field": "environment", - "operator": "equals", - "value": "PROD" - }, - "template": "PROD_DEPLOY_APPROVAL" - }, - { - "condition": { - "field": "changeType", - "operator": "in", - "value": ["CONFIG", "DATABASE"] - }, - "template": "CHANGE_APPROVAL" - } - ] - } - ``` - -### 4.3 审批流程实现 -1. 流程启动 - - 规则匹配 - - 创建流程实例 - - 初始化节点 - - 发送通知 - -2. 节点执行 - - 权限校验 - - 节点执行 - - 状态更新 - - 下一节点 - -3. 流程结束 - - 结果处理 - - 状态更新 - - 通知相关人 - -### 4.4 与工作流集成 -1. 工作流定义扩展 - ```json - { - "nodes": [ - { - "id": "deploy", - "type": "deploy", - "config": { - "approval": { - "enabled": true, - "template": "DEPLOY_APPROVAL" - } - } - } - ] - } - ``` - -2. 节点执行器实现 - ```java - public class DeployNodeExecutor implements NodeExecutor { - @Override - public void execute(NodeInstance node) { - // 1. 检查是否需要审批 - if (needApproval(node)) { - // 2. 创建审批流程 - createApprovalFlow(node); - // 3. 暂停节点执行 - node.suspend(); - return; - } - // 4. 执行部署 - executeDeploy(node); - } - } - ``` - -## 五、系统特性 - -### 5.1 多租户支持 -1. 数据隔离 - - 租户ID字段 - - 查询过滤 - - 权限控制 - -2. 资源隔离 - - 独立配置 - - 独立存储 - - 独立缓存 - -### 5.2 插件化设计 -1. 节点插件 - - Git操作 - - Jenkins构建 - - 配置同步 - - 数据库变更 - -2. 审批插件 - - 角色审批 - - 用户审批 - - 条件审批 - - 自动审批 - -### 5.3 安全特性 -1. 认证授权 - - JWT认证 - - RBAC权限 - - 租户隔离 - -2. 数据安全 - - 敏感信息加密 - - 操作审计 - - 数据备份 - -### 5.4 可观测性 -1. 日志管理 - - 操作日志 - - 审计日志 - - 性能日志 - -2. 监控告警 - - 系统监控 - - 业务监控 - - 异常告警 - -## 六、部署架构 - -### 6.1 系统部署 -1. 容器化部署 - - Docker镜像 - - K8s编排 - - 服务网格 - -2. 高可用设计 - - 多副本 - - 负载均衡 - - 故障转移 - -### 6.2 数据存储 -1. 数据库 - - 主从复制 - - 数据备份 - - 分库分表 - -2. 文件存储 - - 分布式存储 - - 文件备份 - - 访问控制 - -## 七、后续规划 - -### 7.1 功能增强 -1. 审批功能 - - 移动端审批 - - 审批委托 - - 审批催办 - -2. 部署功能 - - 灰度发布 - - 回滚机制 - - 批量部署 - -### 7.2 性能优化 -1. 系统性能 - - 缓存优化 - - SQL优化 - - 并发处理 - -2. 用户体验 - - 响应速度 - - 操作便捷 - - 界面优化 diff --git a/backend/docs/deploy-ease-workflow.md b/backend/docs/deploy-ease-workflow.md deleted file mode 100644 index 446c55b8..00000000 --- a/backend/docs/deploy-ease-workflow.md +++ /dev/null @@ -1,887 +0,0 @@ -# Deploy Ease 自动化部署平台设计文档 - -## 一、系统概述 - -### 1.1 功能模块 -1. 项目集管理 - - 项目集配置 - - 项目管理 - - Git仓库集成 - - Jenkins集成 - -2. 环境管理 - - 环境配置 - - 环境克隆 - - 环境变量管理 - - 权限控制 - -3. 工作流引擎 - - 流程设计 - - 节点执行 - - 状态管理 - - 日志追踪 - -4. 审批流程 - - 审批配置 - - 审批执行 - - 权限管理 - - 日志记录 - -5. 配置中心 - - Nacos配置管理 - - 配置同步 - - 版本控制 - - 变更追踪 - -## 二、数据库设计 - -### 2.1 项目管理相关表 -```sql --- 项目集表 -CREATE TABLE sys_project_group ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - create_by VARCHAR(255) NULL COMMENT '创建人', - create_time DATETIME(6) NULL COMMENT '创建时间', - deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除', - update_by VARCHAR(255) NULL COMMENT '更新人', - update_time DATETIME(6) NULL COMMENT '更新时间', - version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', - tenant_id BIGINT NOT NULL COMMENT '租户ID', - - code VARCHAR(50) NOT NULL COMMENT '项目集编码', - name VARCHAR(100) NOT NULL COMMENT '项目集名称', - description TEXT COMMENT '描述', - git_group_url VARCHAR(255) NOT NULL COMMENT 'Git组地址', - jenkins_folder VARCHAR(255) COMMENT 'Jenkins文件夹', - deploy_type VARCHAR(20) NOT NULL COMMENT '部署类型:JENKINS/SELF_BUILD', - status VARCHAR(20) NOT NULL DEFAULT 'ENABLED' COMMENT '状态:ENABLED/DISABLED', - - CONSTRAINT uk_group_code UNIQUE (tenant_id, code), - CONSTRAINT uk_group_name UNIQUE (tenant_id, name) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='项目集表'; - --- 项目表 -CREATE TABLE sys_project ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - create_by VARCHAR(255) NULL COMMENT '创建人', - create_time DATETIME(6) NULL COMMENT '创建时间', - deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除', - update_by VARCHAR(255) NULL COMMENT '更新人', - update_time DATETIME(6) NULL COMMENT '更新时间', - version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', - tenant_id BIGINT NOT NULL COMMENT '租户ID', - - group_id BIGINT NOT NULL COMMENT '项目集ID', - code VARCHAR(50) NOT NULL COMMENT '项目编码', - name VARCHAR(100) NOT NULL COMMENT '项目名称', - description TEXT COMMENT '描述', - git_url VARCHAR(255) NOT NULL COMMENT 'Git地址', - jenkins_job VARCHAR(255) COMMENT 'Jenkins任务名', - build_type VARCHAR(20) NOT NULL COMMENT '构建类型:MAVEN/GRADLE/NPM', - enable_build BIT NOT NULL DEFAULT 1 COMMENT '是否启用构建', - status VARCHAR(20) NOT NULL DEFAULT 'ENABLED' COMMENT '状态:ENABLED/DISABLED', - - CONSTRAINT fk_project_group FOREIGN KEY (group_id) REFERENCES sys_project_group(id), - CONSTRAINT uk_project_code UNIQUE (tenant_id, code), - CONSTRAINT uk_project_name UNIQUE (tenant_id, group_id, name) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='项目表'; -``` - -### 2.2 环境管理相关表 -```sql --- 环境表 -CREATE TABLE sys_environment ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - create_by VARCHAR(255) NULL COMMENT '创建人', - create_time DATETIME(6) NULL COMMENT '创建时间', - deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除', - update_by VARCHAR(255) NULL COMMENT '更新人', - update_time DATETIME(6) NULL COMMENT '更新时间', - version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', - tenant_id BIGINT NOT NULL COMMENT '租户ID', - - code VARCHAR(50) NOT NULL COMMENT '环境编码', - name VARCHAR(100) NOT NULL COMMENT '环境名称', - description TEXT COMMENT '描述', - type VARCHAR(20) NOT NULL COMMENT '环境类型:DEV/TEST/UAT/PROD', - need_approval BIT NOT NULL DEFAULT 0 COMMENT '是否需要审批', - status VARCHAR(20) NOT NULL DEFAULT 'ENABLED' COMMENT '状态:ENABLED/DISABLED', - - CONSTRAINT uk_env_code UNIQUE (tenant_id, code), - CONSTRAINT uk_env_name UNIQUE (tenant_id, name) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='环境表'; - --- 项目环境配置表 -CREATE TABLE sys_project_env ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - create_by VARCHAR(255) NULL COMMENT '创建人', - create_time DATETIME(6) NULL COMMENT '创建时间', - deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除', - update_by VARCHAR(255) NULL COMMENT '更新人', - update_time DATETIME(6) NULL COMMENT '更新时间', - version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', - tenant_id BIGINT NOT NULL COMMENT '租户ID', - - project_id BIGINT NOT NULL COMMENT '项目ID', - env_id BIGINT NOT NULL COMMENT '环境ID', - workflow_id BIGINT COMMENT '工作流定义ID', - config TEXT COMMENT '环境配置(JSON)', - status VARCHAR(20) NOT NULL DEFAULT 'ENABLED' COMMENT '状态:ENABLED/DISABLED', - - CONSTRAINT fk_env_project FOREIGN KEY (project_id) REFERENCES sys_project(id), - CONSTRAINT fk_env_environment FOREIGN KEY (env_id) REFERENCES sys_environment(id), - CONSTRAINT uk_project_env UNIQUE (tenant_id, project_id, env_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='项目环境配置表'; -``` - -### 2.3 工作流相关表 -```sql --- 工作流定义表 -CREATE TABLE sys_workflow_definition ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - create_by VARCHAR(255) NULL COMMENT '创建人', - create_time DATETIME(6) NULL COMMENT '创建时间', - deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除', - update_by VARCHAR(255) NULL COMMENT '更新人', - update_time DATETIME(6) NULL COMMENT '更新时间', - version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', - tenant_id BIGINT NOT NULL COMMENT '租户ID', - - code VARCHAR(50) NOT NULL COMMENT '工作流编码', - name VARCHAR(100) NOT NULL COMMENT '工作流名称', - description TEXT COMMENT '描述', - content TEXT NOT NULL COMMENT '工作流定义(JSON)', - type VARCHAR(20) NOT NULL COMMENT '类型:DEPLOY/CONFIG_SYNC', - status VARCHAR(20) NOT NULL DEFAULT 'DRAFT' COMMENT '状态:DRAFT/PUBLISHED/DISABLED', - - CONSTRAINT uk_workflow_code UNIQUE (tenant_id, code) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工作流定义表'; - --- 工作流实例表 -CREATE TABLE sys_workflow_instance ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - create_by VARCHAR(255) NULL COMMENT '创建人', - create_time DATETIME(6) NULL COMMENT '创建时间', - deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除', - update_by VARCHAR(255) NULL COMMENT '更新人', - update_time DATETIME(6) NULL COMMENT '更新时间', - version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', - tenant_id BIGINT NOT NULL COMMENT '租户ID', - - definition_id BIGINT NOT NULL COMMENT '工作流定义ID', - project_env_id BIGINT NOT NULL COMMENT '项目环境ID', - status VARCHAR(20) NOT NULL COMMENT '状态:RUNNING/COMPLETED/FAILED/CANCELED', - start_time DATETIME NOT NULL COMMENT '开始时间', - end_time DATETIME COMMENT '结束时间', - variables TEXT COMMENT '工作流变量(JSON)', - error TEXT COMMENT '错误信息', - - CONSTRAINT fk_instance_definition FOREIGN KEY (definition_id) REFERENCES sys_workflow_definition(id), - CONSTRAINT fk_instance_project_env FOREIGN KEY (project_env_id) REFERENCES sys_project_env(id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工作流实例表'; - --- 节点实例表 -CREATE TABLE sys_node_instance ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - create_by VARCHAR(255) NULL COMMENT '创建人', - create_time DATETIME(6) NULL COMMENT '创建时间', - deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除', - update_by VARCHAR(255) NULL COMMENT '更新人', - update_time DATETIME(6) NULL COMMENT '更新时间', - version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', - tenant_id BIGINT NOT NULL COMMENT '租户ID', - - workflow_instance_id BIGINT NOT NULL COMMENT '工作流实例ID', - node_id VARCHAR(50) NOT NULL COMMENT '节点ID', - node_type VARCHAR(50) NOT NULL COMMENT '节点类型', - name VARCHAR(100) NOT NULL COMMENT '节点名称', - status VARCHAR(20) NOT NULL COMMENT '状态:PENDING/RUNNING/COMPLETED/FAILED/CANCELED', - start_time DATETIME COMMENT '开始时间', - end_time DATETIME COMMENT '结束时间', - input TEXT COMMENT '输入参数(JSON)', - output TEXT COMMENT '输出结果(JSON)', - error TEXT COMMENT '错误信息', - - CONSTRAINT fk_node_workflow_instance FOREIGN KEY (workflow_instance_id) REFERENCES sys_workflow_instance(id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='节点实例表'; -``` - -### 2.4 审批相关表 -```sql --- 审批流程定义表 -CREATE TABLE sys_approval_flow ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - create_by VARCHAR(255) NULL COMMENT '创建人', - create_time DATETIME(6) NULL COMMENT '创建时间', - deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除', - update_by VARCHAR(255) NULL COMMENT '更新人', - update_time DATETIME(6) NULL COMMENT '更新时间', - version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', - tenant_id BIGINT NOT NULL COMMENT '租户ID', - - code VARCHAR(50) NOT NULL COMMENT '流程编码', - name VARCHAR(100) NOT NULL COMMENT '流程名称', - description TEXT COMMENT '描述', - type VARCHAR(20) NOT NULL COMMENT '流程类型:DEPLOY/LEAVE/NACOS_CONFIG', - content TEXT NOT NULL COMMENT '流程定义(JSON)', - status VARCHAR(20) NOT NULL DEFAULT 'ENABLED' COMMENT '状态:ENABLED/DISABLED', - - CONSTRAINT uk_flow_code UNIQUE (tenant_id, code) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='审批流程定义表'; - --- 审批任务表 -CREATE TABLE sys_approval_task ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - create_by VARCHAR(255) NULL COMMENT '创建人', - create_time DATETIME(6) NULL COMMENT '创建时间', - deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除', - update_by VARCHAR(255) NULL COMMENT '更新人', - update_time DATETIME(6) NULL COMMENT '更新时间', - version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', - tenant_id BIGINT NOT NULL COMMENT '租户ID', - - flow_id BIGINT NOT NULL COMMENT '流程定义ID', - node_id BIGINT NOT NULL COMMENT '当前节点ID', - title VARCHAR(200) NOT NULL COMMENT '审批标题', - status VARCHAR(20) NOT NULL COMMENT '状态:PENDING/APPROVED/REJECTED/CANCELED', - start_time DATETIME NOT NULL COMMENT '开始时间', - end_time DATETIME COMMENT '结束时间', - variables TEXT COMMENT '审批变量(JSON)', - - CONSTRAINT fk_task_flow FOREIGN KEY (flow_id) REFERENCES sys_approval_flow(id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='审批任务表'; - --- 审批记录表 -CREATE TABLE sys_approval_record ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - create_by VARCHAR(255) NULL COMMENT '创建人', - create_time DATETIME(6) NULL COMMENT '创建时间', - deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除', - update_by VARCHAR(255) NULL COMMENT '更新人', - update_time DATETIME(6) NULL COMMENT '更新时间', - version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', - tenant_id BIGINT NOT NULL COMMENT '租户ID', - - task_id BIGINT NOT NULL COMMENT '审批任务ID', - node_id BIGINT NOT NULL COMMENT '审批节点ID', - approver VARCHAR(100) NOT NULL COMMENT '审批人', - action VARCHAR(20) NOT NULL COMMENT '操作:APPROVE/REJECT', - comment TEXT COMMENT '审批意见', - - CONSTRAINT fk_record_task FOREIGN KEY (task_id) REFERENCES sys_approval_task(id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='审批记录表'; -``` - -## 三、API接口设计 - -### 3.1 项目管理接口 - -#### 3.1.1 项目集管理 -```yaml -/api/v1/projectGroup-group: - post: - summary: 创建项目集 - description: | - 创建新的项目集,需要提供以下信息: - 1. 项目集基本信息(编码、名称、描述) - 2. Git仓库信息 - 3. Jenkins配置(可选) - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/ProjectGroupDTO' - responses: - 200: - description: 成功创建项目集 - content: - application/json: - schema: - $ref: '#/components/schemas/Response' - - get: - summary: 查询项目集列表 - description: | - 支持以下查询条件: - 1. 名称模糊搜索 - 2. 状态过滤 - 3. 分页参数 - parameters: - - name: name - in: query - description: 项目集名称 - schema: - type: string - - name: status - in: query - description: 状态 - schema: - type: string - enum: [ENABLED, DISABLED] - responses: - 200: - description: 项目集列表 - content: - application/json: - schema: - $ref: '#/components/schemas/PageResponse' -``` - -#### 3.1.2 项目管理 -```yaml -/api/v1/projectGroup: - post: - summary: 创建项目 - description: | - 创建新项目,需要提供: - 1. 项目基本信息 - 2. 所属项目集 - 3. Git仓库信息 - 4. 构建配置 - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/ProjectDTO' - responses: - 200: - description: 成功创建项目 - - get: - summary: 查询项目列表 - description: | - 支持条件: - 1. 项目集ID - 2. 名称搜索 - 3. 状态过滤 - parameters: - - name: groupId - in: query - required: true - schema: - type: integer - - name: name - in: query - schema: - type: string - responses: - 200: - description: 项目列表 -``` - -### 3.2 工作流管理接口 - -#### 3.2.1 工作流设计 -```yaml -/api/v1/workflow/definition: - post: - summary: 创建工作流 - description: | - 创建工作流定义,支持: - 1. 节点配置 - 2. 连线规则 - 3. 变量定义 - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/WorkflowDefinitionDTO' - - get: - summary: 获取工作流定义 - description: | - 查询工作流定义,支持: - 1. 基本信息 - 2. 节点信息 - 3. 连线信息 - parameters: - - name: id - in: path - required: true - schema: - type: integer -``` - -#### 3.2.2 工作流执行 -```yaml -/api/v1/workflow/instance: - post: - summary: 启动工作流 - description: | - 启动工作流实例,需要: - 1. 工作流定义ID - 2. 项目环境ID - 3. 执行参数 - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/WorkflowStartDTO' - - get: - summary: 查询执行状态 - description: | - 查询工作流执行状态,包括: - 1. 整体状态 - 2. 节点状态 - 3. 执行日志 - parameters: - - name: id - in: path - required: true - schema: - type: integer -``` - -## 四、前端集成指南 - -### 4.1 工作流设计器 - -#### 4.1.1 组件结构 -```typescript -// 工作流设计器组件 -const WorkflowDesigner: React.FC = () => { - // 1. 画布配置 - const graphConfig = { - container: 'workflow-container', - grid: true, - history: true, - connecting: { - snap: true, - allowBlank: false, - allowLoop: false, - highlight: true, - }, - }; - - // 2. 节点配置 - const nodeConfig = { - // Git同步节点 - gitSync: { - width: 120, - height: 60, - attrs: { - body: { - fill: '#E7F7FF', - stroke: '#1890FF', - }, - label: { - text: 'Git同步', - }, - }, - }, - // 其他节点配置... - }; - - return ( -
- - -
- - -
-
- -
-
- -
- - -
- ); -}; -``` - -#### 4.1.2 节点配置 -```typescript -// 节点配置组件 -const NodeConfig: React.FC<{node: Node}> = ({ node }) => { - // 1. 动态加载配置组件 - const ConfigComponent = nodeConfigComponents[node.type]; - - // 2. 处理配置变更 - const handleConfigChange = (config: any) => { - // 更新节点配置 - node.updateConfig(config); - }; - - return ( -
- -
- ); -}; - -// Git同步节点配置 -const GitSyncConfig: React.FC<{value: any, onChange: (config: any) => void}> = ({ - value, - onChange, -}) => { - return ( -
- - onChange({ ...value, branch: e.target.value })} - /> - - - onChange({ ...value, localPath: e.target.value })} - /> - -
- ); -}; -``` - -#### 4.1.3 日志查看 -```typescript -// 日志查看器组件 -const LogViewer: React.FC<{instance: WorkflowInstance}> = ({ instance }) => { - const [logs, setLogs] = useState([]); - - // 1. 建立WebSocket连接 - useEffect(() => { - const ws = new WebSocket(`ws://localhost:8080/api/logs/${instance.id}`); - - ws.onmessage = (event) => { - const log = JSON.parse(event.data); - setLogs(prev => [...prev, log]); - }; - - return () => ws.close(); - }, [instance]); - - // 2. 渲染日志 - return ( -
- - - log.type === 'workflow')} /> - - - log.type === 'node')} /> - - -
- ); -}; -``` - -### 4.2 状态管理 - -#### 4.2.1 工作流状态 -```typescript -// 工作流状态管理 -const useWorkflowStore = create((set) => ({ - definitions: [], - instances: [], - - // 加载工作流定义 - loadDefinitions: async () => { - const response = await api.get('/api/v1/workflow/definition'); - set({ definitions: response.data }); - }, - - // 创建工作流实例 - createInstance: async (params) => { - const response = await api.post('/api/v1/workflow/instance', params); - set(state => ({ - instances: [...state.instances, response.data], - })); - }, - - // 更新实例状态 - updateInstanceStatus: (id, status) => { - set(state => ({ - instances: state.instances.map(instance => - instance.id === id - ? { ...instance, status } - : instance - ), - })); - }, -})); -``` - -#### 4.2.2 审批状态 -```typescript -// 审批状态管理 -const useApprovalStore = create((set) => ({ - tasks: [], - records: [], - - // 加载待办任务 - loadTasks: async () => { - const response = await api.get('/api/v1/approval/task'); - set({ tasks: response.data }); - }, - - // 审批通过 - approve: async (taskId, comment) => { - await api.post(`/api/v1/approval/task/${taskId}/approve`, { comment }); - set(state => ({ - tasks: state.tasks.filter(task => task.id !== taskId), - })); - }, - - // 审批拒绝 - reject: async (taskId, comment) => { - await api.post(`/api/v1/approval/task/${taskId}/reject`, { comment }); - set(state => ({ - tasks: state.tasks.filter(task => task.id !== taskId), - })); - }, -})); -``` - -## 五、部署指南 - -### 5.1 环境要求 -1. Java 21 -2. MySQL 8.0 -3. Redis 7.x -4. Node.js 18+ - -### 5.2 配置说明 -```yaml -# application.yml -spring: - datasource: - url: jdbc:mysql://localhost:3306/deploy_ease - username: root - password: root - - redis: - host: localhost - port: 6379 - - rabbitmq: - host: localhost - port: 5672 - -workflow: - executor: - core-pool-size: 10 - max-pool-size: 20 - queue-capacity: 100 - - node: - timeout: 3600 # 节点执行超时时间(秒) - retry: - max-attempts: 3 - delay: 5000 -``` - -### 5.3 部署步骤 -1. 数据库初始化 - ```bash - mysql -u root -p deploy_ease < schema.sql - mysql -u root -p deploy_ease < data.sql - ``` - -2. 后端部署 - ```bash - mvn clean package - java -jar deploy-ease.jar - ``` - -3. 前端部署 - ```bash - npm install - npm run build - ``` - -### 5.4 监控配置 -1. Prometheus监控 - ```yaml - scrape_configs: - - job_name: 'deploy-ease' - metrics_path: '/actuator/prometheus' - static_configs: - - targets: ['localhost:8080'] - ``` - -2. Grafana面板 - - JVM监控 - - 接口监控 - - 业务监控 - -## 六、系统优化增强 - -### 6.1 分布式工作流支持 -```java -/** - * 分布式工作流引擎增强 - */ -public interface DistributedWorkflowEngine extends WorkflowEngine { - // 分布式锁支持 - void acquireWorkflowLock(String lockKey, Duration timeout); - void releaseWorkflowLock(String lockKey); - - // 集群协调 - void registerWorker(WorkerInfo workerInfo); - void updateWorkerStatus(String workerId, WorkerStatus status); - - // 任务分发 - void assignTask(String workerId, WorkflowTask task); - void reassignTask(String fromWorkerId, String toWorkerId, WorkflowTask task); - - // 故障转移 - void handleWorkerFailure(String workerId); - void recoverTasks(String failedWorkerId); -} -``` - -### 6.2 工作流版本控制 -```java -/** - * 工作流版本管理 - */ -public interface WorkflowVersionManager { - // 版本控制 - String createVersion(WorkflowDefinition definition); - WorkflowDefinition getVersion(String versionId); - List getVersionHistory(String code); - - // 版本迁移 - void migrateInstances(String fromVersion, String toVersion); - void rollbackVersion(String versionId); - - // 版本比较 - WorkflowDiff compareVersions(String version1, String version2); -} -``` - -### 6.3 动态节点支持 -```java -/** - * 动态节点管理器 - */ -public interface DynamicNodeManager { - // 动态节点注册 - void registerNodeType(String type, NodeExecutor executor); - void unregisterNodeType(String type); - - // 动态配置 - void updateNodeConfig(String type, NodeConfig config); - NodeConfig getNodeConfig(String type); - - // 节点发现 - List discoverNodes(); - boolean isNodeTypeAvailable(String type); -} -``` - -### 6.4 工作流监控增强 -```java -/** - * 增强的工作流监控 - */ -@Component -@Slf4j -public class EnhancedWorkflowMonitor extends PerformanceMonitor { - // 业务监控指标 - private final Counter businessErrorCounter; - private final Counter approvalTimeoutCounter; - private final Timer approvalDurationTimer; - - // 资源监控 - private final Gauge databaseConnectionGauge; - private final Gauge cacheUsageGauge; - private final Gauge messageQueueDepthGauge; - - // SLA监控 - private final Timer workflowSLATimer; - private final Counter slaViolationCounter; - - public void recordBusinessError(String errorType, String errorCode) { - businessErrorCounter.increment(); - // 记录详细错误信息 - } - - public void checkSLA(WorkflowInstance instance) { - Duration duration = Duration.between(instance.getStartTime(), LocalDateTime.now()); - workflowSLATimer.record(duration); - - if (duration.compareTo(instance.getSLADuration()) > 0) { - slaViolationCounter.increment(); - // 触发SLA违规告警 - } - } -} -``` - -### 6.5 工作流数据分析 -```java -/** - * 工作流分析服务 - */ -@Service -@Slf4j -public class WorkflowAnalyticsService { - private final WorkflowInstanceRepository instanceRepository; - private final NodeInstanceRepository nodeRepository; - private final AnalyticsEngine analyticsEngine; - - // 性能分析 - public PerformanceReport analyzePerformance(String workflowType, DateRange range) { - // 分析工作流执行性能 - return analyticsEngine.analyzePerformance(workflowType, range); - } - - // 瓶颈分析 - public List findBottlenecks(String workflowType) { - // 识别执行瓶颈 - return analyticsEngine.findBottlenecks(workflowType); - } - - // 趋势分析 - public TrendReport analyzeTrend(String metric, DateRange range) { - // 分析指标趋势 - return analyticsEngine.analyzeTrend(metric, range); - } -} -``` - -### 6.6 工作流调度优化 -```java -/** - * 优化的工作流调度器 - */ -@Component -@Slf4j -public class OptimizedWorkflowScheduler { - private final WorkloadBalancer workloadBalancer; - private final PriorityQueue taskQueue; - - // 优先级调度 - public void schedulePriorityTask(WorkflowTask task) { - task.setPriority(calculatePriority(task)); - taskQueue.offer(task); - } - - // 负载均衡 - public void balanceWorkload() { - List workers = workloadBalancer.getActiveWorkers(); - for (WorkerNode worker : workers) { - if (worker.isOverloaded()) { - redistributeTasks(worker); - } - } - } - - // 资源预留 - public void reserveResources(WorkflowTask task) { - ResourceRequirements requirements = task.getResourceRequirements(); - resourceManager.reserve(requirements); - } -} -``` \ No newline at end of file diff --git a/backend/docs/frontend-development-guide.md b/backend/docs/frontend-development-guide.md deleted file mode 100644 index 0519ecba..00000000 --- a/backend/docs/frontend-development-guide.md +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/backend/docs/frontend-guide.md b/backend/docs/frontend-guide.md deleted file mode 100644 index 8bb3ba45..00000000 --- a/backend/docs/frontend-guide.md +++ /dev/null @@ -1,869 +0,0 @@ -# 工作流前端对接文档 - -## 一、系统架构 - -### 1.1 整体架构 -```typescript -// 系统分层 -interface SystemArchitecture { - presentation: { - views: '页面组件', // 负责页面展示和用户交互 - components: '通用组件', // 可复用的UI组件 - hooks: '业务钩子', // 封装业务逻辑的Hooks - }; - domain: { - models: '数据模型', // 核心业务模型定义 - services: '领域服务', // 业务逻辑封装 - stores: '状态管理', // 全局状态管理 - }; - infrastructure: { - api: 'API封装', // 后端接口调用 - utils: '工具函数', // 通用工具方法 - constants: '常量定义', // 系统常量 - }; -} -``` - -### 1.2 核心模块 -1. **工作流设计器** - - 负责工作流的可视化设计 - - 节点拖拽和连线 - - 属性配置面板 - - 数据验证和保存 - -2. **表单设计器** - - 工作流表单的可视化设计 - - 字段配置和验证规则 - - 布局设计 - - 表单预览 - -3. **工作流引擎** - - 工作流实例管理 - - 节点状态控制 - - 变量管理 - - 日志记录 - -## 二、数据模型 - -### 2.1 工作流定义 -```typescript -interface WorkflowDefinition { - // 基本信息 - id: number; // 工作流定义ID - code: string; // 工作流编码(唯一标识) - name: string; // 工作流名称 - description: string; // 工作流描述 - version: number; // 版本号 - status: WorkflowStatus; // 状态(DRAFT/PUBLISHED/DISABLED) - enabled: boolean; // 是否启用 - - // 节点定义 - nodes: Array<{ - id: number; // 节点定义ID - nodeId: string; // 节点标识(在图中的唯一标识) - name: string; // 节点名称 - type: NodeType; // 节点类型(START/END/TASK/GATEWAY) - config: string; // 节点配置(JSON字符串) - description: string; // 节点描述 - orderNum: number; // 排序号 - }>; - - // 配置数据 - nodeConfig: string; // 节点关系配置(JSON字符串) - transitionConfig: string; // 流转配置(JSON字符串) - formDefinition: string; // 表单定义(JSON字符串) - graphDefinition: string; // 图形布局(JSON字符串) -} - -// 节点配置示例(JSON格式) -interface NodeConfig { - // 开始节点配置 - startNode: { - type: 'START'; - name: string; - config?: Record; - }; - - // 任务节点配置 - taskNodes: Array<{ - id: string; - type: 'TASK'; - name: string; - executor?: string; // 执行器类型 - config: { - // Shell执行器配置 - script?: string; // 脚本内容 - timeout?: number; // 超时时间 - workingDir?: string; // 工作目录 - - // Jenkins执行器配置 - jenkinsJob?: string; // Jenkins任务名 - parameters?: Record; // 构建参数 - }; - }>; - - // 网关节点配置 - gatewayNodes: Array<{ - id: string; - type: 'GATEWAY'; - name: string; - gatewayType: 'EXCLUSIVE' | 'PARALLEL' | 'INCLUSIVE'; // 网关类型 - conditions?: Array<{ - expression: string; // 条件表达式 - description: string; // 条件描述 - }>; - }>; - - // 结束节点配置 - endNode: { - type: 'END'; - name: string; - config?: Record; - }; -} - -// 流转配置示例 -interface TransitionConfig { - transitions: Array<{ - from: string; // 源节点ID - to: string; // 目标节点ID - condition?: string; // 流转条件(网关节点使用) - priority?: number; // 优先级(条件分支使用) - }>; -} - -// 图形布局示例 -interface GraphDefinition { - nodes: Array<{ - id: string; // 节点ID - type: string; // 节点类型 - x: number; // X坐标 - y: number; // Y坐标 - properties?: { // 节点属性 - width?: number; - height?: number; - color?: string; - icon?: string; - }; - }>; - edges: Array<{ - source: string; // 源节点ID - target: string; // 目标节点ID - points?: Array<{ // 连线控制点 - x: number; - y: number; - }>; - properties?: { // 连线属性 - style?: string; - arrow?: string; - label?: string; - }; - }>; -} -``` - -### 2.2 工作流实例 -```typescript -interface WorkflowInstance { - id: number; // 实例ID - definitionId: number; // 工作流定义ID - businessKey: string; // 业务标识 - status: InstanceStatus; // 状态 - startTime: string; // 开始时间 - endTime: string; // 结束时间 - variables: string; // 工作流变量(JSON字符串) - error: string; // 错误信息 -} - -interface NodeInstance { - id: number; // 实例ID - workflowInstanceId: number; // 工作流实例ID - nodeId: string; // 节点ID - nodeType: NodeType; // 节点类型 - status: NodeStatus; // 节点状态 - startTime: string; // 开始时间 - endTime: string; // 结束时间 - input: string; // 输入参数(JSON字符串) - output: string; // 输出结果(JSON字符串) - error: string; // 错误信息 -} -``` - -## 三、接口定义 - -### 3.1 工作流定义接口 -```typescript -interface WorkflowDefinitionAPI { - // 基础CRUD - create: { - url: '/api/v1/workflow-definitions', - method: 'POST', - data: WorkflowDefinitionDTO, - response: Response - }; - - update: { - url: '/api/v1/workflow-definitions/{id}', - method: 'PUT', - data: WorkflowDefinitionDTO, - response: Response - }; - - delete: { - url: '/api/v1/workflow-definitions/{id}', - method: 'DELETE', - response: Response - }; - - getById: { - url: '/api/v1/workflow-definitions/{id}', - method: 'GET', - response: Response - }; - - page: { - url: '/api/v1/workflow-definitions/page', - method: 'GET', - params: PageQuery, - response: Response> - }; - - // 特殊操作 - publish: { - url: '/api/v1/workflow-definitions/{id}/publish', - method: 'POST', - response: Response - }; - - disable: { - url: '/api/v1/workflow-definitions/{id}/disable', - method: 'POST', - response: Response - }; - - enable: { - url: '/api/v1/workflow-definitions/{id}/enable', - method: 'POST', - response: Response - }; -} -``` - -### 3.2 工作流实例接口 -```typescript -interface WorkflowInstanceAPI { - // 实例操作 - start: { - url: '/api/v1/workflow-instances/start', - method: 'POST', - data: { - workflowCode: string; // 工作流编码 - businessKey: string; // 业务标识 - variables?: Record; // 工作流变量 - }, - response: Response - }; - - terminate: { - url: '/api/v1/workflow-instances/{id}/terminate', - method: 'POST', - data: { - reason?: string; // 终止原因 - }, - response: Response - }; - - pause: { - url: '/api/v1/workflow-instances/{id}/pause', - method: 'POST', - response: Response - }; - - resume: { - url: '/api/v1/workflow-instances/{id}/resume', - method: 'POST', - response: Response - }; - - // 查询接口 - getById: { - url: '/api/v1/workflow-instances/{id}', - method: 'GET', - response: Response - }; - - page: { - url: '/api/v1/workflow-instances/page', - method: 'GET', - params: PageQuery & { - status?: InstanceStatus; - startTime?: string; - endTime?: string; - keyword?: string; - }, - response: Response> - }; -} -``` - -## 四、组件设计 - -### 4.1 工作流设计器 -```typescript -// 1. 设计器组件 -interface WorkflowDesigner { - // 属性定义 - props: { - value?: WorkflowDefinition; // 工作流定义数据 - readOnly?: boolean; // 是否只读 - onChange?: (value: WorkflowDefinition) => void; // 数据变更回调 - }; - - // 子组件 - components: { - ToolBar: '工具栏', // 撤销、重做、缩放等操作 - NodePanel: '节点面板', // 可用节点列表 - Canvas: '画布', // 节点拖拽和连线 - ConfigPanel: '配置面板', // 节点和连线配置 - MiniMap: '小地图' // 导航缩略图 - }; - - // 核心方法 - methods: { - // 初始化设计器 - initialize(definition: WorkflowDefinition): void; - - // 添加节点 - addNode(nodeData: NodeData): void; - - // 连接节点 - connect(source: string, target: string): void; - - // 更新节点配置 - updateNodeConfig(nodeId: string, config: any): void; - - // 更新连线配置 - updateEdgeConfig(edgeId: string, config: any): void; - - // 验证数据 - validate(): ValidationResult; - - // 导出数据 - exportData(): WorkflowDefinition; - }; -} - -// 2. 节点配置面板 -interface NodeConfigPanel { - props: { - node: NodeData; // 节点数据 - nodeType: NodeType; // 节点类型 - onChange: (config: any) => void; // 配置变更回调 - }; - - // 配置表单定义 - forms: { - // 基础配置(所有节点都有) - BaseConfig: { - name: string; // 节点名称 - description?: string; // 节点描述 - }; - - // 任务节点配置 - TaskConfig: { - executor: string; // 执行器类型 - config: Record; // 执行器配置 - }; - - // 网关节点配置 - GatewayConfig: { - type: 'EXCLUSIVE' | 'PARALLEL' | 'INCLUSIVE'; - conditions?: Array<{ - expression: string; - description: string; - }>; - }; - }; -} - -// 3. 连线配置面板 -interface EdgeConfigPanel { - props: { - edge: EdgeData; // 连线数据 - sourceNode: NodeData; // 源节点 - targetNode: NodeData; // 目标节点 - onChange: (config: any) => void; // 配置变更回调 - }; - - // 配置表单定义 - forms: { - condition?: string; // 流转条件 - priority?: number; // 优先级 - description?: string; // 说明 - }; -} -``` - -### 4.2 表单设计器 -```typescript -interface FormDesigner { - props: { - value?: FormDefinition; // 表单定义数据 - onChange?: (value: FormDefinition) => void; // 数据变更回调 - }; - - // 可用的字段类型 - fieldTypes: { - input: '单行文本', - textarea: '多行文本', - number: '数字', - select: '下拉选择', - radio: '单选', - checkbox: '多选', - date: '日期', - datetime: '日期时间', - file: '文件上传' - }; - - // 字段属性定义 - fieldProperties: { - name: string; // 字段名 - label: string; // 字段标签 - type: string; // 字段类型 - required?: boolean; // 是否必填 - defaultValue?: any; // 默认值 - placeholder?: string; // 占位提示 - description?: string; // 字段描述 - options?: Array<{ // 选项(用于select/radio/checkbox) - label: string; - value: any; - }>; - validation?: Array<{ // 验证规则 - type: string; // 规则类型 - message: string; // 错误消息 - params?: any; // 规则参数 - }>; - }; -} -``` - -## 五、状态管理 - -### 5.1 工作流设计器状态 -```typescript -interface DesignerState { - // 画布状态 - canvas: { - scale: number; // 缩放比例 - position: { // 画布位置 - x: number; - y: number; - }; - selectedElements: { // 选中的元素 - nodes: string[]; // 节点ID列表 - edges: string[]; // 连线ID列表 - }; - }; - - // 历史记录 - history: { - undoStack: HistoryItem[]; // 撤销栈 - redoStack: HistoryItem[]; // 重做栈 - canUndo: boolean; // 是否可撤销 - canRedo: boolean; // 是否可重做 - }; - - // 节点数据 - nodes: Record; // 节点数据映射 - edges: Record; // 连线数据映射 - - // 配置面板 - configPanel: { - visible: boolean; // 是否显示 - type: 'node' | 'edge'; // 配置类型 - elementId?: string; // 当前配置的元素ID - }; -} -``` - -### 5.2 工作流实例状态 -```typescript -interface InstanceState { - // 实例数据 - instance: { - current?: WorkflowInstance; // 当前实例 - nodes: NodeInstance[]; // 节点实例列表 - variables: Record; // 工作流变量 - }; - - // 操作权限 - permissions: { - canTerminate: boolean; // 是否可终止 - canPause: boolean; // 是否可暂停 - canResume: boolean; // 是否可恢复 - }; - - // 日志数据 - logs: { - workflow: WorkflowLog[]; // 工作流日志 - nodes: Record; // 节点日志 - }; -} -``` - -## 六、工作流设计器使用示例 - -### 6.1 基础使用 -```typescript -// 1. 创建工作流 -const createWorkflow = async (definition: WorkflowDefinition) => { - try { - // 验证数据 - const validationResult = workflowDesigner.validate(); - if (!validationResult.valid) { - message.error('工作流数据验证失败:' + validationResult.errors.join(', ')); - return; - } - - // 提交数据 - const response = await WorkflowDefinitionAPI.create(definition); - if (response.code === 200) { - message.success('工作流创建成功'); - return response.data; - } else { - message.error('工作流创建失败:' + response.message); - } - } catch (error) { - message.error('系统错误:' + error.message); - } -}; - -// 2. 加载工作流 -const loadWorkflow = async (id: number) => { - try { - const response = await WorkflowDefinitionAPI.getById(id); - if (response.code === 200) { - // 初始化设计器 - workflowDesigner.initialize(response.data); - return response.data; - } else { - message.error('加载工作流失败:' + response.message); - } - } catch (error) { - message.error('系统错误:' + error.message); - } -}; - -// 3. 保存工作流 -const saveWorkflow = async (id: number) => { - try { - // 获取设计器数据 - const definition = workflowDesigner.exportData(); - - // 验证数据 - const validationResult = workflowDesigner.validate(); - if (!validationResult.valid) { - message.error('工作流数据验证失败:' + validationResult.errors.join(', ')); - return; - } - - // 提交数据 - const response = await WorkflowDefinitionAPI.update(id, definition); - if (response.code === 200) { - message.success('工作流保存成功'); - return response.data; - } else { - message.error('工作流保存失败:' + response.message); - } - } catch (error) { - message.error('系统错误:' + error.message); - } -}; -``` - -### 6.2 节点配置示例 -```typescript -// 1. Shell节点配置 -const ShellNodeConfig = { - type: 'TASK', - name: 'Shell脚本', - executor: 'SHELL', - config: { - script: '#!/bin/bash\necho "Hello World"', - timeout: 300, - workingDir: '/tmp' - } -}; - -// 2. Jenkins节点配置 -const JenkinsNodeConfig = { - type: 'TASK', - name: 'Jenkins构建', - executor: 'JENKINS', - config: { - jenkinsJob: 'my-projectGroup-build', - parameters: { - BRANCH: 'master', - ENV: 'prod' - } - } -}; - -// 3. 网关节点配置 -const GatewayNodeConfig = { - type: 'GATEWAY', - name: '条件分支', - gatewayType: 'EXCLUSIVE', - conditions: [ - { - expression: '${status} == "SUCCESS"', - description: '执行成功' - }, - { - expression: '${status} == "FAILED"', - description: '执行失败' - } - ] -}; -``` - -### 6.3 数据验证示例 -```typescript -// 1. 节点配置验证 -const validateNodeConfig = (node: NodeData): ValidationResult => { - const errors: string[] = []; - - // 基础验证 - if (!node.name) { - errors.push('节点名称不能为空'); - } - - // 任务节点特殊验证 - if (node.type === 'TASK') { - if (!node.executor) { - errors.push('请选择执行器'); - } - - // Shell执行器验证 - if (node.executor === 'SHELL') { - if (!node.config.script) { - errors.push('脚本内容不能为空'); - } - } - - // Jenkins执行器验证 - if (node.executor === 'JENKINS') { - if (!node.config.jenkinsJob) { - errors.push('Jenkins任务名不能为空'); - } - } - } - - // 网关节点特殊验证 - if (node.type === 'GATEWAY') { - if (!node.gatewayType) { - errors.push('请选择网关类型'); - } - if (node.gatewayType === 'EXCLUSIVE' && (!node.conditions || node.conditions.length === 0)) { - errors.push('请配置分支条件'); - } - } - - return { - valid: errors.length === 0, - errors - }; -}; - -// 2. 流程验证 -const validateWorkflow = (definition: WorkflowDefinition): ValidationResult => { - const errors: string[] = []; - - // 1. 基础信息验证 - if (!definition.name) { - errors.push('工作流名称不能为空'); - } - if (!definition.code) { - errors.push('工作流编码不能为空'); - } - - // 2. 节点验证 - const nodes = JSON.parse(definition.nodeConfig); - - // 验证开始节点 - if (!nodes.startNode) { - errors.push('必须有一个开始节点'); - } - - // 验证结束节点 - if (!nodes.endNode) { - errors.push('必须有一个结束节点'); - } - - // 验证任务节点 - if (!nodes.taskNodes || nodes.taskNodes.length === 0) { - errors.push('至少需要一个任务节点'); - } - - // 3. 连线验证 - const transitions = JSON.parse(definition.transitionConfig).transitions; - - // 验证是否有孤立节点 - const connectedNodes = new Set(); - transitions.forEach(t => { - connectedNodes.add(t.from); - connectedNodes.add(t.to); - }); - - const allNodes = [ - nodes.startNode, - ...nodes.taskNodes, - ...(nodes.gatewayNodes || []), - nodes.endNode - ]; - - allNodes.forEach(node => { - if (!connectedNodes.has(node.id)) { - errors.push(`节点 ${node.name}(${node.id}) 未连接`); - } - }); - - // 4. 验证是否有环 - if (hasCircle(transitions)) { - errors.push('流程中存在循环'); - } - - return { - valid: errors.length === 0, - errors - }; -}; -``` - -## 七、最佳实践 - -### 7.1 性能优化 -1. **大数据量处理** - - 使用虚拟滚动 - - 分页加载数据 - - 按需渲染节点 - -2. **状态管理** - - 合理使用缓存 - - 避免频繁更新 - - 使用不可变数据 - -3. **渲染优化** - - 组件懒加载 - - 合理使用memo - - 避免不必要的重渲染 - -### 7.2 错误处理 -1. **前端验证** - - 实时验证 - - 提供错误提示 - - 防止无效操作 - -2. **异常捕获** - - 全局错误处理 - - 友好的错误提示 - - 错误日志记录 - -3. **数据恢复** - - 自动保存 - - 本地缓存 - - 操作回滚 - -### 7.3 用户体验 -1. **交互设计** - - 拖拽操作流畅 - - 实时预览效果 - - 快捷键支持 - -2. **反馈机制** - - 操作提示 - - 加载状态 - - 成功/失败反馈 - -3. **辅助功能** - - 自动布局 - - 查找/替换 - - 导入/导出 - -## 八、常见问题 - -### 8.1 配置相关 -1. **节点配置问题** - - Q: 如何处理不同类型节点的配置? - - A: 使用统一的配置接口,通过类型区分不同的配置表单 - -2. **数据同步问题** - - Q: 如何保持前端展示数据与后端数据一致? - - A: 实现定期自动保存和加载机制 - -3. **验证问题** - - Q: 如何确保工作流数据的正确性? - - A: 实现多层次的验证机制,包括实时验证和提交验证 - -### 8.2 性能相关 -1. **大型工作流** - - Q: 如何处理节点数量很多的工作流? - - A: 实现分区渲染和虚拟滚动 - -2. **频繁操作** - - Q: 如何处理频繁的拖拽和连线操作? - - A: 使用节流和防抖优化性能 - -3. **数据量大** - - Q: 如何处理大量的历史数据? - - A: 实现分页加载和懒加载机制 - -## 九、开发规范 - -### 9.1 代码规范 -1. **命名规范** - - 组件使用大驼峰命名 - - 方法使用小驼峰命名 - - 常量使用大写下划线 - -2. **注释规范** - - 组件必须有文档注释 - - 复杂逻辑需要注释 - - 保持注释的及时更新 - -3. **类型规范** - - 使用TypeScript - - 定义清晰的接口 - - 避免any类型 - -### 9.2 提交规范 -1. **提交信息** - - feat: 新功能 - - fix: 修复bug - - docs: 文档更新 - - style: 代码格式 - - refactor: 重构 - - test: 测试 - - chore: 构建过程或辅助工具的变动 - -2. **分支管理** - - master: 主分支 - - develop: 开发分支 - - feature: 功能分支 - - hotfix: 紧急修复分支 - -### 9.3 测试规范 -1. **单元测试** - - 组件测试 - - 方法测试 - - 工具函数测试 - -2. **集成测试** - - 流程测试 - - 接口测试 - - 兼容性测试 - -3. **E2E测试** - - 用户操作测试 - - 场景测试 - - 性能测试 \ No newline at end of file diff --git a/backend/docs/workflow-data-format.md b/backend/docs/workflow-data-format.md deleted file mode 100644 index 6baf8cf6..00000000 --- a/backend/docs/workflow-data-format.md +++ /dev/null @@ -1,332 +0,0 @@ -# 工作流数据格式说明 - -## 1. 节点类型数据示例 - -```json -{ - "id": 2003, - "createTime": "2024-12-05 12:40:03", - "createBy": "system", - "updateTime": "2024-12-05 12:40:03", - "updateBy": "system", - "version": 1, - "deleted": false, - "extraData": null, - "code": "SHELL", - "name": "Shell脚本节点", - "description": "执行Shell脚本的任务节点", - "category": "TASK", - "icon": "code", - "color": "#1890ff", - "executors": [ - { - "code": "SHELL", - "name": "Shell脚本执行器", - "description": "执行Shell脚本,支持配置超时时间和工作目录", - "configSchema": "{\"type\":\"object\",\"required\":[\"script\"],\"properties\":{\"script\":{\"type\":\"string\",\"title\":\"脚本内容\",\"format\":\"shell\",\"description\":\"需要执行的Shell脚本内容\"},\"timeout\":{\"type\":\"number\",\"title\":\"超时时间\",\"description\":\"脚本执行的最大时间(秒)\",\"minimum\":1,\"maximum\":3600,\"default\":300},\"workingDir\":{\"type\":\"string\",\"title\":\"工作目录\",\"description\":\"脚本执行的工作目录\",\"default\":\"/tmp\"}}}", - "defaultConfig": null - } - ], - "configSchema": "{\n \"type\": \"object\",\n \"properties\": {\n \"name\": {\n \"type\": \"string\",\n \"title\": \"节点名称\",\n \"minLength\": 1,\n \"maxLength\": 50\n },\n \"description\": {\n \"type\": \"string\",\n \"title\": \"节点描述\",\n \"maxLength\": 200\n },\n \"executor\": {\n \"type\": \"string\",\n \"title\": \"执行器\",\n \"enum\": [\"SHELL\"],\n \"enumNames\": [\"Shell脚本执行器\"]\n },\n \"retryTimes\": {\n \"type\": \"number\",\n \"title\": \"重试次数\",\n \"minimum\": 0,\n \"maximum\": 3,\n \"default\": 0\n },\n \"retryInterval\": {\n \"type\": \"number\",\n \"title\": \"重试间隔(秒)\",\n \"minimum\": 1,\n \"maximum\": 300,\n \"default\": 60\n }\n },\n \"required\": [\"name\", \"executor\"]\n}", - "defaultConfig": "{\"name\": \"Shell脚本\", \"executor\": \"SHELL\", \"retryTimes\": 0, \"retryInterval\": 60}", - "enabled": true -} -``` - -## 2. 流程设计数据示例 - -```json -{ - "nodes": [ - { - "id": "node_1", - "type": "START", - "position": { "x": 100, "y": 100 }, - "data": { - "name": "开始", - "description": "流程开始节点", - "config": { - "name": "开始", - "description": "这是一个开始节点" - } - } - } - ], - "edges": [ - { - "id": "edge_1", - "source": "node_1", - "target": "node_2", - "type": "default", - "data": { - "condition": null - } - } - ] -} -``` - -## 3. 关键字段说明 - -### configSchema(节点配置模式) -**用途**: -1. 前端动态生成配置表单 -2. 后端验证配置数据的合法性 -3. 提供配置项的约束和验证规则 - -### defaultConfig(默认配置) -**用途**: -1. 新建节点时的默认值 -2. 重置配置时的参考值 -3. 必须符合configSchema定义的格式 - -### executors(执行器定义) -**用途**: -1. 定义节点支持的执行器类型 -2. 每个执行器的配置要求 -3. 用于任务节点的具体执行逻辑 - -## 4. 字段关系说明 - -1. configSchema定义节点的整体配置结构 -2. defaultConfig提供符合configSchema的默认值 -3. executors中的configSchema定义具体执行器的配置结构 -4. 实际节点配置时,executorConfig需要符合选定执行器的configSchema - -## 5. 前端实现指南 - -### 5.1 工作流设计器组件架构 - -推荐使用组件化设计,主要包含以下组件: - -1. **WorkflowDesigner(工作流设计器)** - - 整体容器组件 - - 负责状态管理 - - 处理快捷键 - - 工具栏集成 - -2. **NodePanel(节点面板)** - - 显示可用节点类型 - - 支持拖拽创建节点 - - 节点分类展示 - -3. **Canvas(画布)** - - 节点和连线的可视化 - - 处理拖拽和连线 - - 网格背景 - - 缩放和平移 - -4. **NodeConfig(节点配置)** - - 动态表单生成 - - 配置验证 - - 实时预览 - -### 5.2 接口说明 - -#### 5.2.1 节点类型接口 -```typescript -// 获取节点类型列表 -GET /api/v1/node-types -Response: { - code: number; - data: NodeType[]; - message: string; -} - -// 获取单个节点类型详情 -GET /api/v1/node-types/{id} -Response: { - code: number; - data: NodeType; - message: string; -} -``` - -#### 5.2.2 工作流设计接口 -```typescript -// 保存工作流设计 -POST /api/v1/workflows/{id}/design -Request: { - nodes: Node[]; - edges: Edge[]; -} -Response: { - code: number; - data: boolean; - message: string; -} - -// 获取工作流设计 -GET /api/v1/workflows/{id}/design -Response: { - code: number; - data: { - nodes: Node[]; - edges: Edge[]; - }; - message: string; -} -``` - -### 5.3 节点连线实现 - -#### 5.3.1 连接点(Anchors) -每种类型节点的连接点定义: -```typescript -interface NodeAnchor { - id: string; - type: 'input' | 'output'; - position: 'top' | 'right' | 'bottom' | 'left'; - allowMultiple?: boolean; // 是否允许多条连线 -} - -const nodeAnchors = { - START: [ - { id: 'output', type: 'output', position: 'bottom', allowMultiple: true } - ], - END: [ - { id: 'input', type: 'input', position: 'top', allowMultiple: false } - ], - TASK: [ - { id: 'input', type: 'input', position: 'top', allowMultiple: false }, - { id: 'output', type: 'output', position: 'bottom', allowMultiple: true } - ], - GATEWAY: [ - { id: 'input', type: 'input', position: 'top', allowMultiple: false }, - { id: 'output', type: 'output', position: 'bottom', allowMultiple: true } - ] -}; -``` - -#### 5.3.2 连线验证规则 -```typescript -interface ConnectionValidation { - sourceNode: Node; - targetNode: Node; - sourceAnchor: NodeAnchor; - targetAnchor: NodeAnchor; -} - -function validateConnection({ - sourceNode, - targetNode, - sourceAnchor, - targetAnchor -}: ConnectionValidation): boolean { - // 1. 检查源节点和目标节点是否有效 - if (!sourceNode || !targetNode) return false; - - // 2. 检查是否形成循环 - if (wouldCreateCycle(sourceNode, targetNode)) return false; - - // 3. 检查锚点类型匹配 - if (sourceAnchor.type !== 'output' || targetAnchor.type !== 'input') return false; - - // 4. 检查目标锚点是否已被占用(如果不允许多重连接) - if (!targetAnchor.allowMultiple && hasExistingConnection(targetNode, targetAnchor)) { - return false; - } - - return true; -} -``` - -#### 5.3.3 连线样式配置 -```typescript -const edgeStyles = { - default: { - type: 'smoothstep', // 平滑阶梯线 - animated: false, - style: { - stroke: '#b1b1b7', - strokeWidth: 2 - } - }, - selected: { - style: { - stroke: '#1890ff', - strokeWidth: 2 - } - }, - conditional: { - type: 'smoothstep', - animated: true, - style: { - stroke: '#722ed1', - strokeWidth: 2, - strokeDasharray: '5,5' - } - } -}; -``` - -### 5.4 状态管理建议 - -推荐使用状态管理库(如Redux或MobX)管理以下状态: - -1. **全局状态** - - 当前工作流设计 - - 可用节点类型 - - 画布缩放级别 - - 选中的节点/连线 - -2. **节点状态** - - 位置信息 - - 配置数据 - - 验证状态 - -3. **连线状态** - - 连接关系 - - 条件配置 - - 样式信息 - -### 5.5 性能优化建议 - -1. **渲染优化** - - 使用React.memo()优化节点渲染 - - 实现虚拟滚动 - - 大量节点时使用分层渲染 - -2. **状态更新优化** - - 批量更新状态 - - 使用不可变数据结构 - - 实现节点位置的防抖 - -3. **交互优化** - - 拖拽时使用节点预览 - - 连线时显示对齐参考线 - - 支持快捷键操作 - -### 5.6 错误处理 - -1. **前端验证** - - 节点配置验证 - - 连线规则验证 - - 数据完整性检查 - -2. **错误提示** - - 友好的错误信息 - - 错误定位高亮 - - 操作建议提示 - -3. **异常恢复** - - 自动保存 - - 操作撤销/重做 - - 状态恢复机制 - -## 6. 最佳实践建议 - -1. **代码组织** - - 使用TypeScript确保类型安全 - - 遵循组件设计原则 - - 实现完整的测试覆盖 - -2. **用户体验** - - 实现撤销/重做功能 - - 支持键盘快捷键 - - 添加操作引导 - -3. **可扩展性** - - 支持自定义节点 - - 支持自定义连线样式 - - 预留扩展接口 \ No newline at end of file diff --git a/backend/docs/workflow-development.md b/backend/docs/workflow-development.md deleted file mode 100644 index 0823132e..00000000 --- a/backend/docs/workflow-development.md +++ /dev/null @@ -1,494 +0,0 @@ -# 工作流引擎开发文档 - -## 一、已完成工作 - -### 1. 核心实体设计 -- WorkflowDefinition: 工作流定义 - - 包含基本信息(编码、名称、描述等) - - 包含流程定义内容(JSON格式) - - 支持版本控制和状态管理 - -- WorkflowInstance: 工作流实例 - - 关联工作流定义 - - 记录执行状态和进度 - - 支持暂停、恢复、取消等操作 - -- NodeInstance: 节点实例 - - 关联工作流实例 - - 记录节点执行状态和结果 - - 支持重试和跳过等操作 - -### 2. 节点类型体系 -- 基础节点类型: - - START: 开始节点 - - END: 结束节点 - - CONDITION: 条件节点 - - PARALLEL: 并行节点 - -- 功能节点类型: - - APPROVAL: 审批节点 - - JENKINS: Jenkins构建节点 - - SCRIPT: 脚本执行节点 - - GIT: Git操作节点 - - NACOS: 配置中心节点 - - HTTP: HTTP请求节点 - - NOTIFY: 通知节点 - -### 3. 工作流引擎实现 -- 核心接口设计 - - WorkflowEngine: 工作流引擎接口 - - NodeExecutor: 节点执行器接口 - -- 默认实现 - - DefaultWorkflowEngine: 默认工作流引擎实现 - - AbstractNodeExecutor: 抽象节点执行器 - - ShellNodeExecutor等具体执行器 - -### 4. 变量与日志管理 -- WorkflowVariable: 工作流变量 - - 支持全局变量和节点变量 - - 支持变量引用和替换 - -- WorkflowLog: 工作流日志 - - 支持不同日志类型和级别 - - 支持详细的执行记录 - -### 5. 权限管理 -- WorkflowPermission: 工作流权限 - - 支持角色和用户级别的权限控制 - - 支持操作级别的权限控制 - -### 6. API接口设计 -- 工作流定义管理 -- 工作流实例操作 -- 节点实例管理 -- 日志查询等功能 - -### 7. 错误码和消息 -- 工作流相关错误码(2700-2799) -- 详细的错误消息定义 - -## 二、待完成工作 - -### 1. 高级功能实现 - -#### 1.1 节点执行引擎增强 -```java -public interface NodeExecutor { - // 新增预执行检查 - boolean preCheck(NodeInstance node, WorkflowContext context); - - // 新增补偿操作 - void compensate(NodeInstance node, WorkflowContext context); - - // 新增超时处理 - void handleTimeout(NodeInstance node, WorkflowContext context); -} - -// 节点执行状态追踪 -public class NodeExecutionTracker { - private Long nodeInstanceId; - private Date startTime; - private Date endTime; - private String status; - private Map metrics; - private List logs; -} -``` - -#### 1.2 工作流调度管理 -```java -@Service -public class WorkflowScheduleService { - // 创建调度任务 - public void createSchedule(WorkflowSchedule schedule); - - // 修改调度配置 - public void updateSchedule(WorkflowSchedule schedule); - - // 启用/禁用调度 - public void toggleSchedule(Long scheduleId, boolean enabled); - - // 手动触发 - public void triggerSchedule(Long scheduleId); -} - -// 调度配置表 -CREATE TABLE wf_workflow_schedule ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - workflow_definition_id BIGINT NOT NULL COMMENT '工作流定义ID', - name VARCHAR(100) NOT NULL COMMENT '调度名称', - cron VARCHAR(100) NOT NULL COMMENT 'cron表达式', - variables JSON COMMENT '工作流变量', - enabled BIT NOT NULL DEFAULT 1 COMMENT '是否启用', - last_fire_time DATETIME COMMENT '上次触发时间', - next_fire_time DATETIME COMMENT '下次触发时间', - -- 其他基础字段 -); -``` - -#### 1.3 工作流监控告警 -```java -public interface WorkflowMonitor { - // 收集性能指标 - void collectMetrics(WorkflowInstance instance); - - // 检查健康状态 - HealthStatus checkHealth(); - - // 触发告警 - void triggerAlert(AlertType type, String message); -} - -// 监控指标表 -CREATE TABLE wf_workflow_metrics ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - workflow_instance_id BIGINT NOT NULL, - metric_name VARCHAR(100) NOT NULL, - metric_value DECIMAL(19,2) NOT NULL, - collect_time DATETIME NOT NULL, - -- 其他基础字段 -); - -// 告警记录表 -CREATE TABLE wf_workflow_alert ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - alert_type VARCHAR(50) NOT NULL, - target_type VARCHAR(50) NOT NULL, - target_id BIGINT NOT NULL, - level VARCHAR(20) NOT NULL, - message TEXT NOT NULL, - status VARCHAR(20) NOT NULL, - -- 其他基础字段 -); -``` - -#### 1.4 工作流分析统计 -```java -public interface WorkflowAnalytics { - // 执行时长分析 - Map analyzeExecutionTime(Long workflowId); - - // 成功率分析 - Map analyzeSuccessRate(Long workflowId); - - // 节点耗时分析 - List analyzeNodeTime(Long workflowId); -} - -// 分析结果表 -CREATE TABLE wf_workflow_analytics ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - workflow_definition_id BIGINT NOT NULL, - analysis_type VARCHAR(50) NOT NULL, - time_range VARCHAR(50) NOT NULL, - metrics JSON NOT NULL, - -- 其他基础字段 -); -``` - -### 2. 功能优化计划 - -#### 2.1 性能优化 -- 引入本地缓存和分布式缓存 -- 实现工作流实例分片执行 -- 优化日志存储和查询 -```java -@Configuration -public class WorkflowCacheConfig { - @Bean - public Cache definitionCache() { - return CacheBuilder.newBuilder() - .maximumSize(1000) - .expireAfterWrite(1, TimeUnit.HOURS) - .build(); - } -} - -public class ShardingWorkflowEngine implements WorkflowEngine { - // 分片执行实现 - public void executeWithSharding(WorkflowInstance instance) { - String shardingKey = calculateShardingKey(instance); - ShardingContext context = createShardingContext(shardingKey); - executeInShard(instance, context); - } -} -``` - -#### 2.2 可靠性增强 -- 实现节点执行幂等性 -- 增加全局事务控制 -- 完善补偿机制 -```java -public abstract class IdempotentNodeExecutor implements NodeExecutor { - // 幂等性检查 - protected boolean checkIdempotent(String executionId) { - return redisTemplate.opsForValue() - .setIfAbsent("node:execution:" + executionId, "1", 24, TimeUnit.HOURS); - } -} - -@Service -public class CompensationService { - // 注册补偿操作 - public void registerCompensation(NodeInstance node, Runnable compensation); - - // 执行补偿 - public void executeCompensation(WorkflowInstance instance); -} -``` - -#### 2.3 扩展性优化 -- 支持自定义节点类型 -- 支持插件化扩展 -- 提供更多扩展点 -```java -public interface WorkflowPlugin { - // 插件初始化 - void init(WorkflowEngine engine); - - // 注册扩展点 - void registerExtensions(); - - // 清理资源 - void destroy(); -} - -public class PluginManager { - // 加载插件 - public void loadPlugins(); - - // 启用插件 - public void enablePlugin(String pluginId); - - // 禁用插件 - public void disablePlugin(String pluginId); -} -``` - -### 3. 新特性规划 - -#### 3.1 工作流模板功能 -```java -// 模板定义表 -CREATE TABLE wf_workflow_template ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - code VARCHAR(100) NOT NULL UNIQUE, - name VARCHAR(100) NOT NULL, - description TEXT, - content JSON NOT NULL, - category VARCHAR(50), - tags JSON, - -- 其他基础字段 -); - -// 模板参数表 -CREATE TABLE wf_template_parameter ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - template_id BIGINT NOT NULL, - name VARCHAR(100) NOT NULL, - type VARCHAR(50) NOT NULL, - required BIT NOT NULL DEFAULT 0, - default_value VARCHAR(255), - validation_rule VARCHAR(255), - -- 其他基础字段 -); -``` - -#### 3.2 工作流版本管理 -```java -// 版本管理表 -CREATE TABLE wf_workflow_version ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - workflow_definition_id BIGINT NOT NULL, - version VARCHAR(20) NOT NULL, - content JSON NOT NULL, - change_log TEXT, - status VARCHAR(20) NOT NULL, - -- 其他基础字段 -); - -public interface VersionManager { - // 创建新版本 - String createVersion(Long workflowId, String content); - - // 发布版本 - void publishVersion(Long workflowId, String version); - - // 回滚版本 - void rollbackVersion(Long workflowId, String version); -} -``` - -#### 3.3 工作流迁移功能 -```java -public interface WorkflowMigration { - // 导出工作流 - byte[] exportWorkflow(Long workflowId); - - // 导入工作流 - Long importWorkflow(byte[] content); - - // 迁移实例到新版本 - void migrateInstance(Long instanceId, String targetVersion); -} - -// 迁移记录表 -CREATE TABLE wf_workflow_migration ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - source_workflow_id BIGINT NOT NULL, - target_workflow_id BIGINT NOT NULL, - migration_type VARCHAR(50) NOT NULL, - status VARCHAR(20) NOT NULL, - error_message TEXT, - -- 其他基础字段 -); -``` - -#### 3.4 工作流测试功能 -```java -public interface WorkflowTesting { - // 模拟执行 - TestResult simulateExecution(Long workflowId, Map variables); - - // 节点单元测试 - TestResult testNode(NodeConfig config, Map inputs); - - // 生成测试报告 - TestReport generateReport(Long testExecutionId); -} - -// 测试用例表 -CREATE TABLE wf_workflow_test_case ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - workflow_definition_id BIGINT NOT NULL, - name VARCHAR(100) NOT NULL, - description TEXT, - variables JSON, - expected_result JSON, - -- 其他基础字段 -); - -// 测试执行记录表 -CREATE TABLE wf_workflow_test_execution ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - test_case_id BIGINT NOT NULL, - execution_time DATETIME NOT NULL, - status VARCHAR(20) NOT NULL, - actual_result JSON, - error_message TEXT, - -- 其他基础字段 -); -``` - -## 三、技术架构 - -### 1. 整体架构 -``` -+------------------+ -| API Layer | -+------------------+ -| Service Layer | -+------------------+ -| Engine Core | -+------------------+ -| Storage Layer | -+------------------+ -``` - -### 2. 关键组件 -- 工作流引擎核心 -- 节点执行引擎 -- 变量管理器 -- 日志管理器 -- 权限管理器 -- 调度管理器 -- 监控告警组件 -- 分析统计组件 - -### 3. 存储设计 -- 核心业务表 -- 执行记录表 -- 监控指标表 -- 分析统计表 -- 日志记录表 - -### 4. 缓存设计 -- 本地缓存 - - 工作流定义缓存 - - 节点配置缓存 - -- 分布式缓存 - - 执行状态缓存 - - 变量数据缓存 - -## 四、部署运维 - -### 1. 部署架构 -``` -+---------------+ +---------------+ -| API Server | | API Server | -+---------------+ +---------------+ - | | -+---------------+ +---------------+ -| Engine Worker | | Engine Worker | -+---------------+ +---------------+ - | | -+---------------+ +---------------+ -| Node Worker | | Node Worker | -+---------------+ +---------------+ -``` - -### 2. 监控方案 -- 系统监控 - - JVM指标 - - 线程池状态 - - 数据库连接池 - -- 业务监控 - - 工作流执行状态 - - 节点执行性能 - - 错误率统计 - -### 3. 告警方案 -- 系统告警 - - 资源使用率 - - 错误率阈值 - - 响应时间 - -- 业务告警 - - 执行超时 - - 节点失败 - - 异常终止 - -### 4. 运维工具 -- 管理控制台 -- 监控面板 -- 运维脚本 -- 诊断工具 - -## 五、项目管理 - -### 1. 开发计划 -- Phase 1: 核心功能实现 (已完成) -- Phase 2: 高级特性开发 (进行中) -- Phase 3: 性能优化和稳定性提升 -- Phase 4: 运维工具和监控体系建设 - -### 2. 测试策略 -- 单元测试 -- 集成测试 -- 性能测试 -- 稳定性测试 - -### 3. 文档规划 -- 设计文档 -- API文档 -- 使用手册 -- 运维手册 - -### 4. 版本规划 -- v1.0: 基础功能版本 -- v1.1: 高级特性版本 -- v1.2: 性能优化版本 -- v2.0: 企业版本 \ No newline at end of file diff --git a/backend/docs/workflow-engine-design.md b/backend/docs/workflow-engine-design.md deleted file mode 100644 index c94b42e3..00000000 --- a/backend/docs/workflow-engine-design.md +++ /dev/null @@ -1,670 +0,0 @@ -# 工作流引擎改造方案设计文档 - -## 1. 现状分析 - -### 1.1 现有组件 - -1. 工作流引擎核心组件 -- `AbstractNodeExecutor`: 节点执行器抽象类,提供基础的节点执行逻辑 -- `WorkflowGraph`: 工作流图模型,管理节点和转换关系 -- `INodeExecutor`: 节点执行器接口 -- `WorkflowInstance`: 工作流实例实体 -- `NodeInstance`: 节点实例实体 - -2. 数据模型 -```sql --- 工作流定义表 -CREATE TABLE wf_workflow_definition ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - create_by VARCHAR(255) NULL COMMENT '创建人', - create_time DATETIME(6) NULL COMMENT '创建时间', - deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除(0:未删除,1:已删除)', - update_by VARCHAR(255) NULL COMMENT '更新人', - update_time DATETIME(6) NULL COMMENT '更新时间', - version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', - - code VARCHAR(100) NOT NULL COMMENT '工作流编码', - name VARCHAR(100) NOT NULL COMMENT '工作流名称', - description VARCHAR(255) NULL COMMENT '工作流描述', - status TINYINT NOT NULL COMMENT '工作流状态(0:草稿,1:已发布,2:已禁用)', - version_no INT NOT NULL DEFAULT 1 COMMENT '版本号', - node_config TEXT NULL COMMENT '节点配置(JSON)', - transition_config TEXT NULL COMMENT '流转配置(JSON)', - form_definition TEXT NULL COMMENT '表单定义(JSON)', - graph_definition TEXT NULL COMMENT '图形信息(JSON)', - enabled BIT NOT NULL DEFAULT 1 COMMENT '是否启用', - - CONSTRAINT UK_workflow_definition_code_version UNIQUE (code, version_no) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工作流定义表'; - --- 工作流实例表 -CREATE TABLE wf_workflow_instance ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - create_by VARCHAR(255) NULL COMMENT '创建人', - create_time DATETIME(6) NULL COMMENT '创建时间', - deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除(0:未删除,1:已删除)', - update_by VARCHAR(255) NULL COMMENT '更新人', - update_time DATETIME(6) NULL COMMENT '更新时间', - version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', - - workflow_definition_id BIGINT NOT NULL COMMENT '工作流定义ID', - business_key VARCHAR(100) NOT NULL COMMENT '业务标识', - status TINYINT NOT NULL COMMENT '状态(0:已创建,1:等待执行,2:执行中,3:已完成,4:执行失败,5:已取消,6:已暂停,7:已终止)', - start_time DATETIME(6) NULL COMMENT '开始时间', - end_time DATETIME(6) NULL COMMENT '结束时间', - error TEXT NULL COMMENT '错误信息', - - CONSTRAINT FK_workflow_instance_definition FOREIGN KEY (workflow_definition_id) REFERENCES wf_workflow_definition (id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工作流实例表'; - --- 节点实例表 -CREATE TABLE wf_node_instance ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - create_by VARCHAR(255) NULL COMMENT '创建人', - create_time DATETIME(6) NULL COMMENT '创建时间', - deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除(0:未删除,1:已删除)', - update_by VARCHAR(255) NULL COMMENT '更新人', - update_time DATETIME(6) NULL COMMENT '更新时间', - version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', - - workflow_instance_id BIGINT NOT NULL COMMENT '工作流实例ID', - node_id VARCHAR(100) NOT NULL COMMENT '节点ID', - node_type TINYINT NOT NULL COMMENT '节点类型(0:开始节点,1:结束节点,2:任务节点,3:网关节点,4:子流程节点,5:Shell脚本节点,6:审批节点,7:Jenkins任务节点,8:Git操作节点)', - name VARCHAR(100) NOT NULL COMMENT '节点名称', - status TINYINT NOT NULL COMMENT '状态(0:已创建,1:等待执行,2:执行中,3:已完成,4:执行失败,5:已取消,6:已暂停,7:已终止)', - start_time DATETIME(6) NULL COMMENT '开始时间', - end_time DATETIME(6) NULL COMMENT '结束时间', - config TEXT NULL COMMENT '节点配置(JSON)', - description TEXT NULL COMMENT '节点描述', - input TEXT NULL COMMENT '输入参数(JSON)', - output TEXT NULL COMMENT '输出结果(JSON)', - error TEXT NULL COMMENT '错误信息', - pre_node_id VARCHAR(100) NULL COMMENT '前置节点ID', - - CONSTRAINT FK_node_instance_workflow FOREIGN KEY (workflow_instance_id) REFERENCES wf_workflow_instance (id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='节点实例表'; - --- 工作流变量表 -CREATE TABLE wf_workflow_variable ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - create_by VARCHAR(255) NULL COMMENT '创建人', - create_time DATETIME(6) NULL COMMENT '创建时间', - deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除(0:未删除,1:已删除)', - update_by VARCHAR(255) NULL COMMENT '更新人', - update_time DATETIME(6) NULL COMMENT '更新时间', - version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', - - workflow_instance_id BIGINT NOT NULL COMMENT '工作流实例ID', - name VARCHAR(100) NOT NULL COMMENT '变量名', - value TEXT NULL COMMENT '变量值', - type VARCHAR(20) NOT NULL COMMENT '变量类型', - - CONSTRAINT FK_workflow_variable_instance FOREIGN KEY (workflow_instance_id) REFERENCES wf_workflow_instance (id), - CONSTRAINT UK_workflow_variable_instance_name UNIQUE (workflow_instance_id, name) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工作流变量表'; - --- 工作流日志表 -CREATE TABLE wf_log ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - workflow_instance_id BIGINT NOT NULL COMMENT '工作流实例ID', - node_id VARCHAR(50) NULL COMMENT '节点ID', - level VARCHAR(10) NOT NULL COMMENT '日志级别', - message TEXT NOT NULL COMMENT '日志内容', - detail TEXT, - create_time DATETIME NOT NULL, - create_by VARCHAR(50), - update_time DATETIME, - update_by VARCHAR(50), - version INT DEFAULT 0, - deleted BOOLEAN DEFAULT FALSE, - CONSTRAINT FK_workflow_log_instance FOREIGN KEY (workflow_instance_id) REFERENCES wf_workflow_instance (id) -); - --- 工作流权限表 -CREATE TABLE wf_workflow_permission ( - id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键', - workflow_definition_id BIGINT NOT NULL COMMENT '工作流定义ID', - type VARCHAR(50) NOT NULL COMMENT '权限类型', - user_id BIGINT COMMENT '用户ID', - role_id BIGINT COMMENT '角色ID', - department_id BIGINT COMMENT '部门ID', - create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - create_by BIGINT NOT NULL COMMENT '创建人', - update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - update_by BIGINT NOT NULL COMMENT '更新人', - version INT NOT NULL DEFAULT 0 COMMENT '版本号', - deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否删除', - INDEX idx_workflow_definition_id (workflow_definition_id), - INDEX idx_user_id (user_id), - INDEX idx_role_id (role_id), - INDEX idx_department_id (department_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='工作流权限'; - --- 节点类型表 -CREATE TABLE wf_node_type ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - create_by VARCHAR(255) NULL COMMENT '创建人', - create_time DATETIME(6) NULL COMMENT '创建时间', - deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除(0:未删除,1:已删除)', - update_by VARCHAR(255) NULL COMMENT '更新人', - update_time DATETIME(6) NULL COMMENT '更新时间', - version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', - - code VARCHAR(100) NOT NULL COMMENT '节点类型编码', - name VARCHAR(100) NOT NULL COMMENT '节点类型名称', - description TEXT NULL COMMENT '节点类型描述', - category VARCHAR(50) NOT NULL COMMENT '节点类型分类', - icon VARCHAR(100) NULL COMMENT '节点图标', - color VARCHAR(20) NULL COMMENT '节点颜色', - executors TEXT NULL COMMENT '执行器列表(JSON)', - config_schema TEXT NULL COMMENT '节点配置模式(JSON)', - default_config TEXT NULL COMMENT '默认配置(JSON)', - enabled BIT NOT NULL DEFAULT 1 COMMENT '是否启用', - - CONSTRAINT UK_node_type_code UNIQUE (code) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='节点类型表'; - -## 2. 改造目标 - -### 2.1 核心需求 -1. 支持审批节点 -2. 实现实时日志 -3. 工作流状态持久化 -4. 支持程序重启后恢复执行 - -### 2.2 技术要求 -1. 遵循现有代码规范 -2. 保持向后兼容 -3. 确保高可用性 -4. 支持并发执行 - -## 3. 详细设计 - -### 3.1 数据库改造 - -#### 3.1.1 修改现有表 -```sql --- 修改工作流实例表,添加执行状态字段 -ALTER TABLE wf_workflow_instance -ADD COLUMN current_node_id varchar(100) COMMENT '当前执行的节点ID', -ADD COLUMN execution_status varchar(32) COMMENT '执行状态'; - --- 修改节点实例表,添加重试相关字段 -ALTER TABLE wf_node_instance -ADD COLUMN retry_count INT DEFAULT 0 COMMENT '重试次数', -ADD COLUMN max_retry_count INT DEFAULT 0 COMMENT '最大重试次数', -ADD COLUMN retry_interval INT DEFAULT 0 COMMENT '重试间隔(秒)', -ADD COLUMN last_retry_time TIMESTAMP COMMENT '最后重试时间'; -``` - -#### 3.1.2 新增审批相关表 -```sql --- 审批配置表 -CREATE TABLE wf_approval_config ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - create_by VARCHAR(255) NULL COMMENT '创建人', - create_time DATETIME(6) NULL COMMENT '创建时间', - deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除(0:未删除,1:已删除)', - update_by VARCHAR(255) NULL COMMENT '更新人', - update_time DATETIME(6) NULL COMMENT '更新时间', - version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', - - project_id VARCHAR(64) NOT NULL COMMENT '项目ID', - env_type VARCHAR(20) NOT NULL COMMENT '环境类型', - approval_type VARCHAR(20) NOT NULL COMMENT '审批类型(SINGLE/MULTI/ANY)', - approver_ids TEXT NOT NULL COMMENT '审批人ID列表(JSON)', - required_count INT NOT NULL COMMENT '需要的审批人数', - timeout_hours INT NOT NULL COMMENT '审批超时时间(小时)', - enabled BIT NOT NULL DEFAULT 1 COMMENT '是否启用', - - CONSTRAINT UK_approval_config_project_env UNIQUE (project_id, env_type) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='审批配置表'; - --- 审批实例表 -CREATE TABLE wf_approval_instance ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - create_by VARCHAR(255) NULL COMMENT '创建人', - create_time DATETIME(6) NULL COMMENT '创建时间', - deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除(0:未删除,1:已删除)', - update_by VARCHAR(255) NULL COMMENT '更新人', - update_time DATETIME(6) NULL COMMENT '更新时间', - version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', - - node_instance_id VARCHAR(64) NOT NULL COMMENT '节点实例ID', - workflow_instance_id VARCHAR(64) NOT NULL COMMENT '工作流实例ID', - project_id VARCHAR(64) NOT NULL COMMENT '项目ID', - env_type VARCHAR(20) NOT NULL COMMENT '环境类型', - status VARCHAR(20) NOT NULL COMMENT '状态(PENDING/APPROVED/REJECTED/TIMEOUT)', - initiator_id VARCHAR(64) NOT NULL COMMENT '发起人ID', - approved_time DATETIME(6) NULL COMMENT '审批通过时间', - approver_id VARCHAR(64) NULL COMMENT '审批人ID', - comment TEXT NULL COMMENT '审批意见', - - INDEX idx_workflow_instance (workflow_instance_id), - INDEX idx_node_instance (node_instance_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='审批实例表'; - --- 审批记录表 -CREATE TABLE wf_approval_record ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - create_by VARCHAR(255) NULL COMMENT '创建人', - create_time DATETIME(6) NULL COMMENT '创建时间', - deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除(0:未删除,1:已删除)', - update_by VARCHAR(255) NULL COMMENT '更新人', - update_time DATETIME(6) NULL COMMENT '更新时间', - version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', - - approval_instance_id BIGINT NOT NULL COMMENT '审批实例ID', - approver_id VARCHAR(64) NOT NULL COMMENT '审批人ID', - action VARCHAR(20) NOT NULL COMMENT '审批动作(APPROVE/REJECT)', - comment TEXT NULL COMMENT '审批意见', - - CONSTRAINT FK_approval_record_instance FOREIGN KEY (approval_instance_id) - REFERENCES wf_approval_instance (id), - INDEX idx_approval_instance (approval_instance_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='审批记录表'; - -### 3.2 代码改造 - -#### 3.2.1 新增枚举 -```java -/** - * 节点类型枚举扩展 - */ -public enum NodeTypeEnum { - SCRIPT, // 脚本节点 - HTTP, // HTTP请求节点 - APPROVAL, // 审批节点 - // ... 其他节点类型 -} - -/** - * 审批类型枚举 - */ -public enum ApprovalTypeEnum { - SINGLE, // 单人审批 - MULTI, // 多人会签 - ANY // 任意人审批 -} - -/** - * 审批状态枚举 - */ -public enum ApprovalStatusEnum { - PENDING, // 待审批 - APPROVED, // 已通过 - REJECTED, // 已拒绝 - TIMEOUT // 已超时 -} -``` - -#### 3.2.2 实体类 -```java -/** - * 审批配置实体 - */ -@Data -@EqualsAndHashCode(callSuper = true) -@Entity -@Table(name = "wf_approval_config") -@LogicDelete -public class ApprovalConfig extends BaseEntity { - @Column(nullable = false) - private String projectId; - - @Column(nullable = false) - @Enumerated(EnumType.STRING) - private String envType; - - @Column(nullable = false) - @Enumerated(EnumType.STRING) - private ApprovalTypeEnum approvalType; - - @Column(nullable = false) - private String approverIds; - - @Column(nullable = false) - private Integer requiredCount; - - @Column(nullable = false) - private Integer timeoutHours; -} - -/** - * 审批实例实体 - */ -@Data -@EqualsAndHashCode(callSuper = true) -@Entity -@Table(name = "wf_approval_instance") -@LogicDelete -public class ApprovalInstance extends BaseEntity { - @Column(nullable = false) - private String nodeInstanceId; - - @Column(nullable = false) - private String workflowInstanceId; - - @Column(nullable = false) - private String projectId; - - @Column(nullable = false) - @Enumerated(EnumType.STRING) - private String envType; - - @Column(nullable = false) - @Enumerated(EnumType.STRING) - private ApprovalStatusEnum status; - - @Column(nullable = false) - private String initiatorId; - - private LocalDateTime approvedTime; - private String approverId; - private String comment; -} -``` - -#### 3.2.3 Repository接口 -```java -/** - * 审批配置Repository - */ -@Repository -public interface IApprovalConfigRepository extends IBaseRepository { - ApprovalConfig findByProjectIdAndEnvType(String projectId, String envType); -} - -/** - * 审批实例Repository - */ -@Repository -public interface IApprovalInstanceRepository extends IBaseRepository { - List findByStatus(ApprovalStatusEnum status); - ApprovalInstance findByWorkflowInstanceIdAndNodeInstanceId(String workflowInstanceId, String nodeInstanceId); -} -``` - -#### 3.2.4 Service接口 -```java -/** - * 审批服务接口 - */ -public interface IApprovalService { - /** - * 创建审批实例 - */ - ApprovalInstance createApprovalInstance(String nodeInstanceId, String workflowInstanceId, - String projectId, ApprovalConfig config); - - /** - * 处理审批结果 - */ - void handleApproval(Long approvalInstanceId, String approverId, - boolean approved, String comment); - - /** - * 检查审批状态 - */ - ApprovalStatusEnum checkApprovalStatus(Long approvalInstanceId); - - /** - * 发送审批通知 - */ - void sendApprovalNotifications(ApprovalInstance instance); -} -``` - -#### 3.2.5 工作流执行管理器 -```java -/** - * 工作流执行管理器 - */ -@Component -@Slf4j -public class WorkflowExecutionManager { - - @Resource - private ThreadPoolExecutor workflowExecutor; - - @Resource - private WorkflowInstanceRepository instanceRepository; - - @PostConstruct - public void init() { - // 初始化线程池 - workflowExecutor = new ThreadPoolExecutor( - 10, 20, 60L, TimeUnit.SECONDS, - new LinkedBlockingQueue<>(100), - new ThreadFactoryBuilder().setNameFormat("workflow-executor-%d").build() - ); - - // 恢复运行中的工作流 - resumeRunningWorkflows(); - } - - // ... 其他方法实现 -} -``` - -#### 3.2.6 审批节点执行器 -```java -/** - * 审批节点执行器 - */ -@Component -@Slf4j -public class ApprovalNodeExecutor extends AbstractNodeExecutor { - - @Resource - private IApprovalService approvalService; - - @Override - public NodeTypeEnum getNodeType() { - return NodeTypeEnum.APPROVAL; - } - - @Override - protected void doExecute(WorkflowInstance workflowInstance, NodeInstance nodeInstance) { - // ... 实现审批节点执行逻辑 - } -} -``` - -### 3.3 实时日志实现 - -#### 3.3.1 日志收集器 -```java -/** - * 日志收集器 - */ -@Component -@Slf4j -public class ProcessLogManager { - - @Resource - private WorkflowLogService workflowLogService; - - private final Map logBuffers = new ConcurrentHashMap<>(); - private final Map>> subscribers = new ConcurrentHashMap<>(); - - // ... 实现日志收集和推送逻辑 -} -``` - -#### 3.3.2 SSE控制器 -```java -/** - * 日志SSE控制器 - */ -@RestController -@RequestMapping("/api/v1/logs") -@Tag(name = "工作流日志", description = "工作流日志相关接口") -public class LogController { - - @GetMapping(value = "/{nodeInstanceId}/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) - public SseEmitter streamLogs(@PathVariable String nodeInstanceId) { - // ... 实现SSE日志推送 - } -} -``` - -## 4. 开发计划 - -### 4.1 核心功能改造 - -#### 4.1.1 工作流引擎核心 -1. **状态管理优化** - - 实现工作流实例状态机,支持更细粒度的状态转换 - - 增加节点实例的状态追踪和历史记录 - - 添加工作流暂停/恢复机制 - -2. **并发控制增强** - - 实现基于数据库的分布式锁 - - 添加节点级别的并发控制 - - 优化多实例并行执行的性能 - -3. **错误处理机制** - - 完善节点执行失败的重试机制 - - 实现节点回滚功能 - - 添加全局异常处理和日志记录 - - 支持自定义错误处理策略 - -#### 4.1.2 审批功能实现 -1. **审批节点开发** - - 实现审批节点执行器 - - 支持多种审批模式(单人、多人、任意人) - - 添加审批超时处理机制 - - 实现审批回调通知 - -2. **审批流程管理** - - 开发审批配置管理接口 - - 实现审批人动态配置 - - 支持审批规则的版本控制 - - 添加审批历史查询功能 - -3. **审批UI组件** - - 开发审批任务列表 - - 实现审批详情页面 - - 添加审批操作接口 - - 支持审批意见和附件上传 - -### 4.2 新增功能 - -#### 4.2.1 工作流监控 -1. **实时监控** - - 实现工作流执行状态实时展示 - - 添加节点执行时间统计 - - 支持执行瓶颈分析 - - 实现资源使用监控 - -2. **报警机制** - - 添加执行超时告警 - - 实现错误率监控告警 - - 支持自定义告警规则 - - 集成多种告警通道(邮件、短信、webhook等) - -#### 4.2.2 工作流分析 -1. **统计分析** - - 实现工作流执行趋势分析 - - 添加节点执行效率分析 - - 支持审批效率统计 - - 开发自定义报表功能 - -2. **可视化展示** - - 实现工作流执行路径可视化 - - 添加状态转换图表展示 - - 支持审批流程可视化 - - 开发数据大屏功能 - -### 4.3 性能优化 - -1. **数据库优化** - - 优化表结构和索引 - - 实现分库分表方案 - - 添加缓存机制 - - 优化大数据量查询性能 - -2. **执行引擎优化** - - 优化线程池管理 - - 实现节点执行预热 - - 添加资源限流机制 - - 优化内存使用 - -### 4.4 开发排期 - -#### Phase 1: 核心功能改造(4周) -- Week 1-2: 工作流引擎核心改造 -- Week 3-4: 审批功能实现 - -#### Phase 2: 新增功能开发(3周) -- Week 1-2: 工作流监控功能 -- Week 3: 工作流分析功能 - -#### Phase 3: 性能优化(2周) -- Week 1: 数据库优化 -- Week 2: 执行引擎优化 - -#### Phase 4: 测试与上线(1周) -- Day 1-3: 功能测试与bug修复 -- Day 4-5: 性能测试与优化 -- Day 6-7: 准备上线与部署 - -### 4.5 风险评估 - -1. **技术风险** - - 分布式环境下的状态一致性 - - 大规模并发下的性能表现 - - 复杂工作流的稳定性保证 - -2. **项目风险** - - 开发周期可能超出预期 - - 新旧版本兼容性问题 - - 现有业务的平滑迁移 - -### 4.6 后续规划 - -1. **功能扩展** - - 支持更多类型的节点 - - 添加工作流模板功能 - - 实现工作流版本管理 - - 支持跨系统工作流 - -2. **生态建设** - - 开发SDK工具包 - - 提供更多开箱即用的组件 - - 完善开发文档 - - 建设示例中心 - -## 5. 注意事项 - -1. 数据迁移 -- 需要处理已有的运行中工作流 -- 确保数据一致性 - -2. 性能考虑 -- 控制线程池大小 -- 合理设置日志缓冲区 -- 定期清理历史数据 - -3. 监控告警 -- 添加工作流执行监控 -- 配置审批超时告警 -- 监控系统资源使用 - -## 6. 后续优化 - -1. 功能优化 -- 支持更多审批策略 -- 优化日志查询性能 -- 添加工作流统计分析 - -2. 性能优化 -- 引入分布式锁 -- 优化数据库查询 -- 添加缓存机制 - -3. 运维优化 -- 完善监控指标 -- 优化日志清理 -- 添加性能诊断工具 diff --git a/backend/docs/workflow/workflow_instance_sync.md b/backend/docs/workflow/workflow_instance_sync.md deleted file mode 100644 index b48f95b2..00000000 --- a/backend/docs/workflow/workflow_instance_sync.md +++ /dev/null @@ -1,380 +0,0 @@ -# 工作流实例同步机制设计文档 - -## 目录 -1. [整体数据流向](#整体数据流向) -2. [Flowable与系统映射关系](#Flowable与系统映射关系) -3. [数据同步实现](#数据同步实现) -4. [事件监听机制](#事件监听机制) -5. [状态管理](#状态管理) -6. [最佳实践](#最佳实践) - -## 整体数据流向 - -``` -[前端流程设计器] - | - | 1. 用户拖拽设计流程 - | - 选择节点类型(从workflow_node_definition获取可用节点) - | - 配置节点属性(根据节点的form_config渲染表单) - | - 连接节点、设置条件 - ↓ -[前端数据模型] - | - 图形数据(nodes、edges) - | - 表单数据(各节点的配置信息) - | - 流程基本信息(名称、描述等) - | - | 2. 保存流程设计 - ↓ -[后端 WorkflowDefinitionController] - | - | 3. 接收流程设计数据 - ↓ -[后端 WorkflowDefinitionService] - | - | 4. 处理流程数据 - | - 保存流程定义基本信息到 workflow_definition - | - 转换图形数据为BPMN XML - | - 解析节点配置,更新workflow_node_definition - | * 对于新的节点类型:创建新记录 - | * 对于已有节点:更新配置 - | - | 5. 部署到Flowable - ↓ -[Flowable引擎] - | - | 6. 部署流程 - | - 保存BPMN XML - | - 生成流程定义ID - | - | 7. 启动流程实例 - ↓ -[后端 WorkflowInstanceService] - | - | 8. 创建流程实例 - | - 创建workflow_instance记录 - | - 关联workflow_definition - | - | 9. 节点实例化 - | - 创建workflow_node_instance记录 - | - 关联workflow_node_definition - ↓ -[数据库] - | - | 实时同步的表: - | - workflow_definition(流程定义) - | - workflow_node_definition(节点定义) - | - workflow_instance(流程实例) - | - workflow_node_instance(节点实例) - | - | Flowable表: - | - ACT_RE_*(流程定义相关表) - | - ACT_RU_*(运行时数据表) - | - ACT_HI_*(历史数据表) - ↓ -[前端任务列表/监控页面] - | - | 10. 展示流程实例 - | - 查询实例状态 - | - 显示节点执行情况 - | - 处理用户任务 - | - 查看历史记录 -``` - -## Flowable与系统映射关系 - -### 表结构映射 - -#### Flowable核心表 -``` -[流程定义相关] -ACT_RE_DEPLOYMENT: 流程部署表 -ACT_RE_PROCDEF: 流程定义表 - -[流程实例相关] -ACT_RU_EXECUTION: 运行时流程实例表 -ACT_RU_TASK: 运行时任务表 -ACT_RU_VARIABLE: 运行时变量表 - -[历史数据] -ACT_HI_PROCINST: 历史流程实例表 -ACT_HI_TASKINST: 历史任务实例表 -ACT_HI_ACTINST: 历史活动实例表 -``` - -#### 系统表与Flowable映射关系 -``` -我们的表 Flowable的表 --------------------------------------------------- -workflow_definition <-> ACT_RE_PROCDEF -workflow_instance <-> ACT_RU_EXECUTION/ACT_HI_PROCINST -workflow_node_instance <-> ACT_RU_TASK/ACT_HI_TASKINST -``` - -## 数据同步实现 - -### 流程实例服务实现 -```java -@Service -@Transactional -public class WorkflowInstanceService { - - @Resource - private RuntimeService runtimeService; // Flowable的运行时服务 - - @Resource - private TaskService taskService; // Flowable的任务服务 - - public WorkflowInstance startProcess(WorkflowInstanceCreateDTO createDTO) { - // 1. 启动Flowable流程实例 - ProcessInstance processInstance = runtimeService.startProcessInstanceById( - createDTO.getProcessDefinitionId(), - createDTO.getBusinessKey(), - createDTO.getVariables() - ); - - // 2. 创建我们自己的流程实例记录 - WorkflowInstance workflowInstance = new WorkflowInstance(); - workflowInstance.setProcessInstanceId(processInstance.getId()); - workflowInstance.setProcessDefinitionId(createDTO.getProcessDefinitionId()); - workflowInstance.setBusinessKey(createDTO.getBusinessKey()); - workflowInstance.setStatus(WorkflowInstanceStatusEnums.RUNNING); - workflowInstance.setVariables(JsonUtils.toJsonString(createDTO.getVariables())); - workflowInstance.setStartTime(LocalDateTime.now()); - workflowInstanceRepository.save(workflowInstance); - - // 3. 获取当前活动的任务 - List tasks = taskService.createTaskQuery() - .processInstanceId(processInstance.getId()) - .list(); - - // 4. 为每个活动的任务创建节点实例 - for (Task task : tasks) { - WorkflowNodeInstance nodeInstance = new WorkflowNodeInstance(); - nodeInstance.setWorkflowInstanceId(workflowInstance.getId()); - nodeInstance.setNodeId(task.getTaskDefinitionKey()); - nodeInstance.setNodeName(task.getName()); - nodeInstance.setNodeType("userTask"); // 或者从定义中获取 - nodeInstance.setStatus("ACTIVE"); - nodeInstance.setStartTime(LocalDateTime.now()); - workflowNodeInstanceRepository.save(nodeInstance); - } - - return workflowInstance; - } -} -``` - -### 数据同步服务实现 -```java -@Service -public class WorkflowSyncService { - - @Scheduled(fixedRate = 60000) // 每分钟执行一次 - public void syncWorkflowStatus() { - // 1. 查找所有运行中的实例 - List runningInstances = workflowInstanceRepository - .findByStatus(WorkflowInstanceStatusEnums.RUNNING); - - for (WorkflowInstance instance : runningInstances) { - // 2. 检查Flowable中的状态 - ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() - .processInstanceId(instance.getProcessInstanceId()) - .singleResult(); - - if (processInstance == null) { - // Flowable中实例已结束,更新我们的状态 - HistoricProcessInstance historicInstance = historyService - .createHistoricProcessInstanceQuery() - .processInstanceId(instance.getProcessInstanceId()) - .singleResult(); - - instance.setStatus(convertFlowableStatus(historicInstance.getEndActivityId())); - instance.setEndTime(LocalDateTime.now()); - workflowInstanceRepository.save(instance); - } - - // 3. 同步节点实例状态 - syncNodeInstances(instance.getId(), instance.getProcessInstanceId()); - } - } - - private void syncNodeInstances(Long workflowInstanceId, String processInstanceId) { - // 1. 获取当前活动的任务 - List activeTasks = taskService.createTaskQuery() - .processInstanceId(processInstanceId) - .list(); - - // 2. 获取历史任务 - List historicTasks = historyService - .createHistoricTaskInstanceQuery() - .processInstanceId(processInstanceId) - .finished() - .list(); - - // 3. 更新节点实例状态 - Set processedTaskIds = new HashSet<>(); - - // 处理活动任务 - for (Task task : activeTasks) { - WorkflowNodeInstance nodeInstance = getOrCreateNodeInstance( - workflowInstanceId, task.getTaskDefinitionKey()); - updateNodeInstance(nodeInstance, task, null); - processedTaskIds.add(task.getTaskDefinitionKey()); - } - - // 处理历史任务 - for (HistoricTaskInstance historicTask : historicTasks) { - if (!processedTaskIds.contains(historicTask.getTaskDefinitionKey())) { - WorkflowNodeInstance nodeInstance = getOrCreateNodeInstance( - workflowInstanceId, historicTask.getTaskDefinitionKey()); - updateNodeInstance(nodeInstance, null, historicTask); - } - } - } -} -``` - -## 事件监听机制 - -### Flowable事件监听器 -```java -@Component -public class FlowableEventListener implements TaskListener, ExecutionListener { - - @Resource - private WorkflowInstanceService workflowInstanceService; - - @Override - public void notify(DelegateTask task) { - // 任务事件处理 - String eventName = task.getEventName(); - switch (eventName) { - case "create": - workflowInstanceService.onTaskCreate(task); - break; - case "complete": - workflowInstanceService.onTaskComplete(task); - break; - case "delete": - workflowInstanceService.onTaskDelete(task); - break; - } - } - - @Override - public void notify(DelegateExecution execution) { - // 流程执行事件处理 - String eventName = execution.getEventName(); - switch (eventName) { - case "start": - workflowInstanceService.onProcessStart(execution); - break; - case "end": - workflowInstanceService.onProcessEnd(execution); - break; - } - } -} -``` - -## 状态管理 - -### 工作流实例状态枚举 -```java -public enum WorkflowInstanceStatusEnums { - NOT_STARTED, // 未开始 - RUNNING, // 运行中 - SUSPENDED, // 已暂停 - COMPLETED, // 已完成 - TERMINATED, // 已终止 - FAILED; // 执行失败 - - public static WorkflowInstanceStatusEnums fromFlowableStatus(String flowableStatus) { - switch (flowableStatus) { - case "active": - return RUNNING; - case "suspended": - return SUSPENDED; - case "completed": - return COMPLETED; - case "terminated": - return TERMINATED; - default: - return FAILED; - } - } -} -``` - -## 最佳实践 - -### 为什么需要自己维护实例表 - -1. **业务扩展性** - - 可以添加更多业务相关的字段 - - 可以实现自定义的状态管理 - - 可以关联更多业务数据 - -2. **性能优化** - - 避免频繁查询Flowable表 - - 可以建立更适合业务查询的索引 - - 可以实现更好的缓存策略 - -3. **数据完整性** - - 保存完整的业务上下文 - - 记录更详细的审计信息 - - 支持自定义的数据分析 - -### 数据一致性保证 - -1. **事务管理** -```java -@Transactional -public void startProcess() { - // 1. 启动Flowable流程 - // 2. 创建系统流程实例 - // 3. 创建节点实例 -} -``` - -2. **定时同步** -- 定期检查运行中的实例状态 -- 自动修复不一致的数据 -- 记录同步日志 - -3. **事件驱动** -- 监听Flowable事件 -- 实时更新系统状态 -- 保证数据实时性 - -### 性能优化建议 - -1. **索引优化** -- 为常用查询字段建立索引 -- 使用复合索引优化多字段查询 -- 避免过多索引影响写入性能 - -2. **缓存策略** -- 缓存活动的流程实例 -- 缓存常用的节点定义 -- 使用分布式缓存提高性能 - -3. **批量处理** -- 批量同步数据 -- 批量更新状态 -- 使用队列处理异步任务 - -## 总结 - -1. Flowable负责流程的实际执行和调度 -2. 系统维护业务层面的状态和数据 -3. 通过事件监听和定时同步保证数据一致性 -4. 使用状态映射处理不同系统间的状态转换 -5. 通过事务确保关键操作的原子性 - -这样的设计可以: -- 保持与Flowable的数据同步 -- 支持业务扩展 -- 提供更好的性能 -- 确保数据一致性 -- 便于问题追踪和修复 diff --git a/backend/readme1.md b/backend/readme1.md deleted file mode 100644 index d7135a7e..00000000 --- a/backend/readme1.md +++ /dev/null @@ -1,322 +0,0 @@ -# 工作流动态表单设计方案 - -## 1. 整体架构 - -### 1.1 数据流向 -``` -1. 流程设计阶段 -┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ -│ 表单设计器 │ │ 流程设计器 │ │ 后端存储 │ -│ Form Designer │ ──> │ Flow Designer │ ──> │ Database │ -└─────────────────┘ └─────────────────┘ └─────────────────┘ - │ │ │ - │ 设计表单 │ 配置节点表单 │ 保存定义 - │ JSON Schema │ 配置数据映射 │ - 表单定义 - │ UI Schema │ 配置权限 │ - 流程定义 - ▼ ▼ ▼ - -2. 流程运行阶段 -┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ -│ 流程发起 │ │ 节点激活 │ │ 表单实例化 │ -│ Start Flow │ ──> │ Node Activated │ ──> │ Form Instance │ -└─────────────────┘ └─────────────────┘ └─────────────────┘ - │ │ - │ 触发监听器 │ 创建表单实例 - │ │ 初始化数据 - ▼ ▼ - -3. 表单处理阶段 -┌─────────────��───┐ ┌─────────────────┐ ┌─────────────────┐ -│ 表单渲染 │ │ 表单交互 │ │ 数据处理 │ -│ Form Render │ ──> │ Form Interact │ ──> │ Data Process │ -└─────────────────┘ └─────────────────┘ └─────────────────┘ - │ │ │ - │ 加载表单定义 │ 用户输入 │ 数据验证 - │ 加载实例数据 │ 数据提交 │ 数据转换 - │ 应用权限 │ │ 变量映射 - ▼ ▼ ▼ - -4. 节点完成阶段 -┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ -│ 表单提交 │ │ 节点完成 │ │ 流程推进 │ -│ Form Submit │ ──> │ Node Complete │ ──> │ Flow Progress │ -└─────────────────┘ └─────────────���───┘ └─────────────────┘ - │ │ - │ 触发监听器 │ 流转到下一节点 - │ 更新流程变量 │ - ▼ ▼ -``` - -### 1.2 数据模型设计 - -1. **表单定义(FormDefinition)** -```java -@Data -@Table(name = "workflow_form_definition") -@Entity -@LogicDelete -public class FormDefinition extends Entity { - @Column(nullable = false) - private String name; // 表单名称 - - @Column(nullable = false, unique = true) - private String code; // 表单编码 - - @Column(columnDefinition = "TEXT") - private String description; // 表单描述 - - @Type(JsonType.class) - @Column(columnDefinition = "json", nullable = false) - private JsonNode schema; // 表单JSON Schema - - @Type(JsonType.class) - @Column(columnDefinition = "json") - private JsonNode uiSchema; // UI渲染Schema - - @Column(nullable = false) - private Boolean enabled = true; // 是否启用 - - @Column(nullable = false) - private Integer version = 1; // 版本号 -} -``` - -2. **表单实例(FormInstance)** -```java -@Data -@Table(name = "workflow_form_instance") -@Entity -@LogicDelete -public class FormInstance extends Entity { - @Column(nullable = false) - private Long formDefinitionId; // 表单定义ID - - @Column(nullable = false) - private String processInstanceId; // 流程实例ID - - @Column(nullable = false) - private String taskId; // 任务ID - - @Type(JsonType.class) - @Column(columnDefinition = "json", nullable = false) - private JsonNode formData; // 表单数据 - - @Column(nullable = false) - @Enumerated(EnumType.STRING) - private FormInstanceStatus status; // 状态 -} -``` - -## 2. 节点表单集成 - -### 2.1 工作流上下文设计 -```java -@Data -public class WorkflowContext { - // 系统上下文 - private SystemContext systemContext; - // 表单上下文 - private FormContext formContext; - // 流程变量上下文 - private ProcessContext processContext; - - @Data - public static class SystemContext { - private JsonNode systemConfig; // 系统配置 - private String executionId; // 执行ID - private String processInstanceId; // 流程实例ID - private String nodeId; // 节点ID - } - - @Data - public static class FormContext { - private Long formInstanceId; // 表单实例ID - private JsonNode formData; // 表单数据 - private JsonNode formConfig; // 表单配置 - private Map mappedData; // 映射后的数据 - } - - @Data - public static class ProcessContext { - private Map variables; // 流程变量 - private Map localVariables; // 节点本地变量 - } -} -``` - -### 2.2 节点定义中的表单配置 -```java -@Data -@Table(name = "workflow_node_definition") -@Entity -@LogicDelete -public class WorkflowNodeDefinition extends Entity { - // ... 现有字段 ... - - /** - * 表单配置 - */ - @Type(JsonType.class) - @Column(columnDefinition = "json") - private JsonNode formConfig; // 节点默认的表单配置 - - /** - * 表单Schema - */ - @Type(JsonType.class) - @Column(columnDefinition = "json") - private JsonNode formSchema; // 节点默认的表单结构 - - /** - * 表单UI Schema - */ - @Type(JsonType.class) - @Column(columnDefinition = "json") - private JsonNode formUiSchema; // 节点默认的UI渲染配置 -} -``` - -## 3. 动态接口支持 - -### 3.1 表单配置中的接口定义 -```json -{ - "formSchema": { - "type": "object", - "properties": { - "department": { - "type": "string", - "title": "部门", - "enum": "@api:getDepartments", - "enumNames": "@api:getDepartmentNames" - }, - "employee": { - "type": "string", - "title": "员工", - "enum": "@api:getEmployeesByDepartment(department)", - "enumNames": "@api:getEmployeeNamesByDepartment(department)" - } - } - } -} -``` - -### 3.2 动态接口注册 -```java -@Service -public class DynamicApiRegistry { - private final Map apiRegistry = new ConcurrentHashMap<>(); - - @PostConstruct - public void init() { - // 注册系统内置API - registerSystemApis(); - } - - // 注册API - public void register(String apiKey, DynamicApi api) { - apiRegistry.put(apiKey, api); - } - - // 获取API - public DynamicApi getApi(String apiKey) { - return apiRegistry.get(apiKey); - } -} -``` - -### 3.3 动态API定义 -```java -@Data -@Builder -public class DynamicApi { - private String url; // API地址 - private HttpMethod method; // 请求方法 - private Function, Map> paramsBuilder; // 参数构建器 - private String transform; // 数据转换路径 - private Map mapping; // 数据映射 - private List dependencies; // 依赖字段 -} -``` - -## 4. 使用示例 - -### 4.1 定义节点表单 -```json -{ - "systemConfig": { - "timeout": 3600, - "retryTimes": 3, - "notifyEmail": "admin@example.com" - }, - "formConfig": { - "formSchema": { /* 表单结构 */ }, - "formUiSchema": { /* UI渲染配置 */ }, - "permissions": { /* 权限配置 */ }, - "dataMapping": { - "input": { /* 输入映射 */ }, - "output": { /* 输出映射 */ } - } - } -} -``` - -### 4.2 注册动态接口 -```java -@Component -public class CustomApiConfig { - - @Resource - private DynamicApiRegistry apiRegistry; - - @PostConstruct - public void init() { - // 注册项目查询API - apiRegistry.register("getProjects", new DynamicApi.builder() - .url("/api/v1/projects") - .method(HttpMethod.GET) - .paramsBuilder(context -> Map.of( - "departmentId", context.get("department"), - "employeeId", context.get("employee") - )) - .transform("data.list") - .mapping(Map.of("value", "id", "label", "name")) - .dependencies(List.of("department", "employee")) - .build() - ); - } -} -``` - -### 4.3 实现委派类 -```java -@Component -@Slf4j -public class ApprovalTaskDelegate extends BaseWorkflowDelegate { - - @Override - protected void doExecute(WorkflowContext context) throws Exception { - // 1. 获取表单数据 - FormContext formContext = context.getFormContext(); - Map mappedData = formContext.getMappedData(); - - String approvalResult = (String) mappedData.get("approvalResult"); - String comments = (String) mappedData.get("comments"); - - // 2. 获取系统配置 - SystemContext systemContext = context.getSystemContext(); - JsonNode systemConfig = systemContext.getSystemConfig(); - String notifyEmail = systemConfig.get("notifyEmail").asText(); - - // 3. 处理审批结果 - ProcessContext processContext = context.getProcessContext(); - processContext.getVariables().put("approved", "APPROVED".equals(approvalResult)); - processContext.getVariables().put("approvalComments", comments); - - // 4. 发送通知 - if (notifyEmail != null) { - notificationService.sendApprovalNotification(notifyEmail, approvalResult, comments); - } - } -} -``` \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/api/TeamConfigApiController.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/api/TeamConfigApiController.java deleted file mode 100644 index b5a14c7f..00000000 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/api/TeamConfigApiController.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.qqchen.deploy.backend.deploy.api; - -import com.qqchen.deploy.backend.deploy.dto.TeamConfigDTO; -import com.qqchen.deploy.backend.deploy.entity.TeamConfig; -import com.qqchen.deploy.backend.deploy.query.TeamConfigQuery; -import com.qqchen.deploy.backend.deploy.service.ITeamConfigService; -import com.qqchen.deploy.backend.framework.api.Response; -import com.qqchen.deploy.backend.framework.controller.BaseController; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; -import lombok.extern.slf4j.Slf4j; -import org.springframework.data.domain.Page; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.util.List; -import java.util.concurrent.CompletableFuture; - -/** - * 团队配置API控制器 - * - * @author qqchen - * @since 2025-10-31 - */ -@Slf4j -@RestController -@RequestMapping("/api/v1/team-configs") -@Tag(name = "团队配置管理", description = "团队配置的增删改查接口") -public class TeamConfigApiController extends BaseController { - - @Resource - private ITeamConfigService teamConfigService; - - @Override - public Response create(@Validated @RequestBody TeamConfigDTO dto) { - return super.create(dto); - } - - @Override - public Response update(@PathVariable Long id, @Validated @RequestBody TeamConfigDTO dto) { - return super.update(id, dto); - } - - @Override - public Response delete(@PathVariable Long id) { - return super.delete(id); - } - - @Override - public Response findById(@PathVariable Long id) { - return super.findById(id); - } - - @Override - public Response> findAll() { - return super.findAll(); - } - - @Override - public Response> page(TeamConfigQuery query) { - return super.page(query); - } - - @Override - public Response> findAll(TeamConfigQuery query) { - return super.findAll(query); - } - - @Override - public CompletableFuture> batchProcess(List dtos) { - return super.batchProcess(dtos); - } - - @Operation(summary = "根据团队ID获取配置", description = "获取指定团队的配置信息") - @GetMapping("/team/{teamId}") - public Response getByTeamId( - @Parameter(description = "团队ID", required = true) @PathVariable Long teamId - ) { - return Response.success(teamConfigService.getByTeamId(teamId)); - } - - @Override - protected void exportData(HttpServletResponse response, List data) { - // TODO: 实现导出功能 - } -} - diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/api/TeamEnvironmentConfigApiController.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/api/TeamEnvironmentConfigApiController.java new file mode 100644 index 00000000..2455e53b --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/api/TeamEnvironmentConfigApiController.java @@ -0,0 +1,66 @@ +package com.qqchen.deploy.backend.deploy.api; + +import com.qqchen.deploy.backend.deploy.dto.TeamEnvironmentConfigDTO; +import com.qqchen.deploy.backend.deploy.entity.TeamEnvironmentConfig; +import com.qqchen.deploy.backend.deploy.query.TeamEnvironmentConfigQuery; +import com.qqchen.deploy.backend.deploy.service.ITeamEnvironmentConfigService; +import com.qqchen.deploy.backend.framework.api.Response; +import com.qqchen.deploy.backend.framework.controller.BaseController; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 团队环境配置Controller + * + * @author qqchen + * @since 2025-11-04 + */ +@Slf4j +@RestController +@RequestMapping("/api/v1/team-environment-config") +@Tag(name = "团队环境配置管理", description = "团队环境配置相关接口") +public class TeamEnvironmentConfigApiController extends BaseController { + + private final ITeamEnvironmentConfigService teamEnvironmentConfigService; + + public TeamEnvironmentConfigApiController(ITeamEnvironmentConfigService service) { + this.teamEnvironmentConfigService = service; + } + + @Operation(summary = "根据团队ID查询环境配置列表") + @GetMapping("/team/{teamId}") + public Response> getByTeamId( + @Parameter(description = "团队ID", required = true) @PathVariable Long teamId + ) { + return Response.success(teamEnvironmentConfigService.findByTeamId(teamId)); + } + + @Operation(summary = "根据团队ID和环境ID查询配置") + @GetMapping("/team/{teamId}/environment/{environmentId}") + public Response getByTeamIdAndEnvironmentId( + @Parameter(description = "团队ID", required = true) @PathVariable Long teamId, + @Parameter(description = "环境ID", required = true) @PathVariable Long environmentId + ) { + return Response.success(teamEnvironmentConfigService.findByTeamIdAndEnvironmentId(teamId, environmentId)); + } + + @Operation(summary = "检查配置是否存在") + @GetMapping("/exists") + public Response exists( + @Parameter(description = "团队ID", required = true) @RequestParam Long teamId, + @Parameter(description = "环境ID", required = true) @RequestParam Long environmentId + ) { + return Response.success(teamEnvironmentConfigService.exists(teamId, environmentId)); + } + + @Override + protected void exportData(jakarta.servlet.http.HttpServletResponse response, List data) { + // TODO: 实现导出功能 + } +} + diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/converter/TeamConfigConverter.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/converter/TeamConfigConverter.java deleted file mode 100644 index 76cd4dc1..00000000 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/converter/TeamConfigConverter.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.qqchen.deploy.backend.deploy.converter; - -import com.qqchen.deploy.backend.deploy.dto.TeamConfigDTO; -import com.qqchen.deploy.backend.deploy.entity.TeamConfig; -import com.qqchen.deploy.backend.framework.converter.BaseConverter; -import org.mapstruct.Mapper; - -/** - * 团队配置转换器 - * - * @author qqchen - * @since 2025-10-31 - */ -@Mapper(config = BaseConverter.class) -public interface TeamConfigConverter extends BaseConverter { -} - diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/converter/TeamEnvironmentConfigConverter.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/converter/TeamEnvironmentConfigConverter.java new file mode 100644 index 00000000..aa1a1b12 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/converter/TeamEnvironmentConfigConverter.java @@ -0,0 +1,17 @@ +package com.qqchen.deploy.backend.deploy.converter; + +import com.qqchen.deploy.backend.deploy.dto.TeamEnvironmentConfigDTO; +import com.qqchen.deploy.backend.deploy.entity.TeamEnvironmentConfig; +import com.qqchen.deploy.backend.framework.converter.BaseConverter; +import org.mapstruct.Mapper; + +/** + * 团队环境配置转换器 + * + * @author qqchen + * @since 2025-11-04 + */ +@Mapper(config = BaseConverter.class) +public interface TeamEnvironmentConfigConverter extends BaseConverter { +} + diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/DeployableEnvironmentDTO.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/DeployableEnvironmentDTO.java index 005f0f86..9ae98937 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/DeployableEnvironmentDTO.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/DeployableEnvironmentDTO.java @@ -39,6 +39,12 @@ public class DeployableEnvironmentDTO { @Schema(description = "审批人列表") private List approvers; + @Schema(description = "是否要求代码审查") + private Boolean requireCodeReview; + + @Schema(description = "备注信息") + private String remark; + @Schema(description = "可部署应用列表") private List applications; } diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamConfigDTO.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamConfigDTO.java deleted file mode 100644 index 6d7c640d..00000000 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamConfigDTO.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.qqchen.deploy.backend.deploy.dto; - -import com.qqchen.deploy.backend.framework.dto.BaseDTO; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.util.List; - -/** - * 团队配置DTO - * - * @author qqchen - * @since 2025-10-31 - */ -@Data -@EqualsAndHashCode(callSuper = true) -@Schema(description = "团队配置信息") -public class TeamConfigDTO extends BaseDTO { - - @Schema(description = "团队ID", required = true) - @NotNull(message = "团队ID不能为空") - private Long teamId; - - @Schema(description = "允许访问的环境ID列表", example = "[1, 2, 3]") - private List allowedEnvironmentIds; - - @Schema(description = "环境是否需要审批(与allowedEnvironmentIds位置对应)", example = "[false, false, true]") - private List environmentApprovalRequired; - - @Schema(description = "各环境的审批人列表(与allowedEnvironmentIds位置对应,无审批人用null)", example = "[null, null, [1, 4]]") - private List approverUserIds; -} - diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamEnvironmentConfigDTO.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamEnvironmentConfigDTO.java new file mode 100644 index 00000000..3b3b08c8 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamEnvironmentConfigDTO.java @@ -0,0 +1,76 @@ +package com.qqchen.deploy.backend.deploy.dto; + +import com.qqchen.deploy.backend.framework.dto.BaseDTO; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + * 团队环境配置DTO + * + * @author qqchen + * @since 2025-11-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class TeamEnvironmentConfigDTO extends BaseDTO { + + /** + * 团队ID + */ + private Long teamId; + + /** + * 环境ID + */ + private Long environmentId; + + /** + * 是否需要审批 + */ + private Boolean approvalRequired; + + /** + * 审批人用户ID列表 + */ + private List approverUserIds; + + /** + * 通知渠道ID + */ + private Long notificationChannelId; + + /** + * 是否启用部署通知 + */ + private Boolean notificationEnabled; + + /** + * 是否要求代码审查通过 + */ + private Boolean requireCodeReview; + + /** + * 备注信息 + */ + private String remark; + + // ===== 扩展字段(非数据库字段) ===== + + /** + * 环境名称(扩展字段) + */ + private String environmentName; + + /** + * 通知渠道名称(扩展字段) + */ + private String notificationChannelName; + + /** + * 该团队在该环境下关联的应用数量(扩展字段) + */ + private Long applicationCount; +} + diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/TeamConfig.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/TeamConfig.java deleted file mode 100644 index 5da3fea2..00000000 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/TeamConfig.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.qqchen.deploy.backend.deploy.entity; - -import com.qqchen.deploy.backend.framework.annotation.LogicDelete; -import com.qqchen.deploy.backend.framework.domain.Entity; -import com.vladmihalcea.hibernate.type.json.JsonType; -import jakarta.persistence.Column; -import jakarta.persistence.Table; -import lombok.Data; -import lombok.EqualsAndHashCode; -import org.hibernate.annotations.Type; - -import java.util.List; - -/** - * 团队配置实体 - * - * @author qqchen - * @since 2025-10-31 - */ -@Data -@EqualsAndHashCode(callSuper = true) -@jakarta.persistence.Entity -@Table(name = "deploy_team_config") -@LogicDelete -public class TeamConfig extends Entity { - - /** - * 团队ID - */ - @Column(name = "team_id", nullable = false) - private Long teamId; - - /** - * 允许访问的环境ID列表 - */ - @Type(JsonType.class) - @Column(name = "allowed_environment_ids", columnDefinition = "JSON") - private List allowedEnvironmentIds; - - /** - * 环境是否需要审批(boolean数组,与allowedEnvironmentIds位置对应) - * 例如:allowedEnvironmentIds=[1,2,3], environmentApprovalRequired=[false,false,true] - * 表示环境1和2不需要审批,环境3需要审批 - */ - @Type(JsonType.class) - @Column(name = "environment_approval_required", columnDefinition = "JSON") - private List environmentApprovalRequired; - - /** - * 各环境的审批人列表(数组,与allowedEnvironmentIds位置对应) - * 元素可以是:null(无审批人)或审批人ID数组 - * 例如:allowedEnvironmentIds=[1,2,3], approverUserIds=[null,null,[1,4]] - * 表示环境1和2无审批人,环境3的审批人是用户1和4 - */ - @Type(JsonType.class) - @Column(name = "approver_user_ids", columnDefinition = "JSON") - private List approverUserIds; -} - diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/TeamEnvironmentConfig.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/TeamEnvironmentConfig.java new file mode 100644 index 00000000..ee8c5866 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/TeamEnvironmentConfig.java @@ -0,0 +1,104 @@ +package com.qqchen.deploy.backend.deploy.entity; + +import com.qqchen.deploy.backend.framework.annotation.LogicDelete; +import com.qqchen.deploy.backend.framework.domain.Entity; +import com.vladmihalcea.hibernate.type.json.JsonType; +import jakarta.persistence.Column; +import jakarta.persistence.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.annotations.Type; + +import java.util.List; + +/** + * 团队环境配置实体 + * + *

定义团队对特定环境的配置: + *

    + *
  • 审批配置:是否需要审批、审批人列表
  • + *
  • 通知配置:通知渠道、是否启用
  • + *
  • 安全策略:代码审查要求
  • + *
+ * + *

数据迁移说明: + * 本表由 deploy_team_config 重构而来,将原来的 JSON 数组结构展开为标准表结构: + *

+ * 原结构:1条记录 = 1个团队的多个环境配置(JSON数组)
+ * 新结构:1条记录 = 1个团队在1个环境的配置
+ * 
+ * + * @author qqchen + * @since 2025-11-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@jakarta.persistence.Entity +@Table(name = "deploy_team_environment_config") +@LogicDelete(false) +public class TeamEnvironmentConfig extends Entity { + + // ===== 关联关系 ===== + + /** + * 团队ID + */ + @Column(name = "team_id", nullable = false) + private Long teamId; + + /** + * 环境ID + */ + @Column(name = "environment_id", nullable = false) + private Long environmentId; + + // ===== 审批配置 ===== + + /** + * 是否需要审批 + *

对应原 TeamConfig.environmentApprovalRequired[i] + */ + @Column(name = "approval_required", nullable = false, columnDefinition = "BOOLEAN DEFAULT FALSE") + private Boolean approvalRequired = false; + + /** + * 审批人用户ID列表 + *

对应原 TeamConfig.approverUserIds[i] + *

示例:[1, 4, 7] + */ + @Type(JsonType.class) + @Column(name = "approver_user_ids", columnDefinition = "JSON") + private List approverUserIds; + + // ===== 通知配置 ===== + + /** + * 通知渠道ID + *

关联 sys_notification_channel 表 + */ + @Column(name = "notification_channel_id") + private Long notificationChannelId; + + /** + * 是否启用部署通知 + */ + @Column(name = "notification_enabled", nullable = false, columnDefinition = "BOOLEAN DEFAULT TRUE") + private Boolean notificationEnabled = true; + + // ===== 安全策略 ===== + + /** + * 是否要求代码审查通过才能部署 + */ + @Column(name = "require_code_review", nullable = false, columnDefinition = "BOOLEAN DEFAULT FALSE") + private Boolean requireCodeReview = false; + + // ===== 备注 ===== + + /** + * 备注信息 + */ + @Column(name = "remark", length = 500) + private String remark; +} + diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/query/TeamConfigQuery.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/query/TeamConfigQuery.java deleted file mode 100644 index 1cb8da13..00000000 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/query/TeamConfigQuery.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.qqchen.deploy.backend.deploy.query; - -import com.qqchen.deploy.backend.framework.annotation.QueryField; -import com.qqchen.deploy.backend.framework.query.BaseQuery; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 团队配置查询条件 - * - * @author qqchen - * @since 2025-10-31 - */ -@Data -@EqualsAndHashCode(callSuper = true) -@Schema(description = "团队配置查询条件") -public class TeamConfigQuery extends BaseQuery { - - @QueryField(field = "teamId") - @Schema(description = "团队ID") - private Long teamId; -} - diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/query/TeamEnvironmentConfigQuery.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/query/TeamEnvironmentConfigQuery.java new file mode 100644 index 00000000..42a24fec --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/query/TeamEnvironmentConfigQuery.java @@ -0,0 +1,42 @@ +package com.qqchen.deploy.backend.deploy.query; + +import com.qqchen.deploy.backend.framework.annotation.QueryField; +import com.qqchen.deploy.backend.framework.query.BaseQuery; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 团队环境配置查询条件 + * + * @author qqchen + * @since 2025-11-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class TeamEnvironmentConfigQuery extends BaseQuery { + + /** + * 团队ID + */ + @QueryField(field = "teamId") + private Long teamId; + + /** + * 环境ID + */ + @QueryField(field = "environmentId") + private Long environmentId; + + /** + * 是否需要审批 + */ + @QueryField(field = "approvalRequired") + private Boolean approvalRequired; + + /** + * 是否启用通知 + */ + @QueryField(field = "notificationEnabled") + private Boolean notificationEnabled; +} + diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/IDeployRecordRepository.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/IDeployRecordRepository.java index 70472467..d1c279c1 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/IDeployRecordRepository.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/IDeployRecordRepository.java @@ -110,5 +110,11 @@ public interface IDeployRecordRepository extends IBaseRepository findRecentDeployRecordsByTeamApplicationIds( @Param("teamApplicationIds") List teamApplicationIds, @Param("limit") int limit); + + /** + * 根据团队应用ID查询所有部署记录(包括已删除的) + * 用于级联删除 + */ + List findByTeamApplicationId(Long teamApplicationId); } diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamApplicationRepository.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamApplicationRepository.java index 88c8a74b..e1aa72ed 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamApplicationRepository.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamApplicationRepository.java @@ -89,5 +89,24 @@ public interface ITeamApplicationRepository extends IBaseRepository findByTeamIdIn(Collection teamIds); + + /** + * 统计指定团队在指定环境下的应用数量 + * + * @param teamId 团队ID + * @param environmentId 环境ID + * @return 应用数量 + */ + Long countByTeamIdAndEnvironmentId(Long teamId, Long environmentId); + + /** + * 统计指定团队列表在指定环境下的不同应用数量(去重 application_id) + * + * @param teamIds 团队ID列表 + * @param environmentId 环境ID + * @return 应用数量 + */ + @Query("SELECT COUNT(DISTINCT ta.applicationId) FROM TeamApplication ta WHERE ta.teamId IN :teamIds AND ta.environmentId = :environmentId") + Long countDistinctApplicationIdByTeamIdsAndEnvironmentId(@Param("teamIds") Collection teamIds, @Param("environmentId") Long environmentId); } diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamConfigRepository.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamConfigRepository.java deleted file mode 100644 index 1684e610..00000000 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamConfigRepository.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.qqchen.deploy.backend.deploy.repository; - -import com.qqchen.deploy.backend.deploy.entity.TeamConfig; -import com.qqchen.deploy.backend.framework.repository.IBaseRepository; -import org.springframework.stereotype.Repository; - -import java.util.Collection; -import java.util.List; -import java.util.Optional; - -/** - * 团队配置Repository - * - * @author qqchen - * @since 2025-10-31 - */ -@Repository -public interface ITeamConfigRepository extends IBaseRepository { - - /** - * 根据团队ID查询配置 - * - * @param teamId 团队ID - * @return 团队配置 - */ - Optional findByTeamIdAndDeletedFalse(Long teamId); - - /** - * 检查团队配置是否存在 - * - * @param teamId 团队ID - * @return 是否存在 - */ - boolean existsByTeamIdAndDeletedFalse(Long teamId); - - /** - * 批量根据团队ID查询配置 - * - * @param teamIds 团队ID集合 - * @return 团队配置列表 - */ - List findByTeamIdIn(Collection teamIds); -} - diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamEnvironmentConfigRepository.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamEnvironmentConfigRepository.java new file mode 100644 index 00000000..fc8fb2be --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamEnvironmentConfigRepository.java @@ -0,0 +1,49 @@ +package com.qqchen.deploy.backend.deploy.repository; + +import com.qqchen.deploy.backend.deploy.entity.TeamEnvironmentConfig; +import com.qqchen.deploy.backend.framework.repository.IBaseRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +/** + * 团队环境配置Repository + * + * @author qqchen + * @since 2025-11-04 + */ +@Repository +public interface ITeamEnvironmentConfigRepository extends IBaseRepository { + + /** + * 根据团队ID查询所有环境配置 + */ + List findByTeamId(Long teamId); + + /** + * 根据团队ID列表批量查询 + */ + List findByTeamIdIn(List teamIds); + + /** + * 根据团队ID和环境ID查询 + */ + Optional findByTeamIdAndEnvironmentId(Long teamId, Long environmentId); + + /** + * 根据环境ID查询所有团队配置 + */ + List findByEnvironmentId(Long environmentId); + + /** + * 检查团队环境配置是否存在 + */ + boolean existsByTeamIdAndEnvironmentId(Long teamId, Long environmentId); + + /** + * 统计配置了指定环境的团队数量 + */ + Long countByEnvironmentId(Long environmentId); +} + diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/ITeamConfigService.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/ITeamConfigService.java deleted file mode 100644 index 24c8f972..00000000 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/ITeamConfigService.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.qqchen.deploy.backend.deploy.service; - -import com.qqchen.deploy.backend.deploy.dto.TeamConfigDTO; -import com.qqchen.deploy.backend.deploy.entity.TeamConfig; -import com.qqchen.deploy.backend.deploy.query.TeamConfigQuery; -import com.qqchen.deploy.backend.framework.service.IBaseService; - -/** - * 团队配置Service接口 - * - * @author qqchen - * @since 2025-10-31 - */ -public interface ITeamConfigService extends IBaseService { - - /** - * 根据团队ID获取配置 - * - * @param teamId 团队ID - * @return 团队配置 - */ - TeamConfigDTO getByTeamId(Long teamId); -} - diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/ITeamEnvironmentConfigService.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/ITeamEnvironmentConfigService.java new file mode 100644 index 00000000..6226dbb7 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/ITeamEnvironmentConfigService.java @@ -0,0 +1,33 @@ +package com.qqchen.deploy.backend.deploy.service; + +import com.qqchen.deploy.backend.deploy.dto.TeamEnvironmentConfigDTO; +import com.qqchen.deploy.backend.deploy.entity.TeamEnvironmentConfig; +import com.qqchen.deploy.backend.deploy.query.TeamEnvironmentConfigQuery; +import com.qqchen.deploy.backend.framework.service.IBaseService; + +import java.util.List; + +/** + * 团队环境配置Service接口 + * + * @author qqchen + * @since 2025-11-04 + */ +public interface ITeamEnvironmentConfigService extends IBaseService { + + /** + * 根据团队ID查询所有环境配置 + */ + List findByTeamId(Long teamId); + + /** + * 根据团队ID和环境ID查询配置 + */ + TeamEnvironmentConfigDTO findByTeamIdAndEnvironmentId(Long teamId, Long environmentId); + + /** + * 检查团队环境配置是否存在 + */ + boolean exists(Long teamId, Long environmentId); +} + diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/DeployServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/DeployServiceImpl.java index 1b40aa9e..fd2d931f 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/DeployServiceImpl.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/DeployServiceImpl.java @@ -53,7 +53,7 @@ public class DeployServiceImpl implements IDeployService { private ITeamRepository teamRepository; @Resource - private ITeamConfigRepository teamConfigRepository; + private ITeamEnvironmentConfigRepository teamEnvironmentConfigRepository; @Resource private IEnvironmentRepository environmentRepository; @@ -82,6 +82,24 @@ public class DeployServiceImpl implements IDeployService { @Resource private ObjectMapper objectMapper; + /** + * 数据组装上下文(封装所有需要的Map,避免方法参数过多) + */ + private static class BuildContext { + Map teamMap; + Map teamMemberMap; + Map teamEnvConfigMap; + Map envMap; + Map> teamAppsMap; + Map appMap; + Map systemMap; + Map workflowMap; + Map approverMap; + Map statisticsMap; + Map latestRecordMap; + Map> recentRecordsMap; + } + @Override @Transactional(readOnly = true) public UserDeployableDTO getDeployableEnvironments() { @@ -95,68 +113,119 @@ public class DeployServiceImpl implements IDeployService { // 3. 查询用户作为负责人的团队 List ownedTeams = teamRepository.findByOwnerIdAndDeletedFalse(currentUserId); - // 4. 合并团队ID(去重) + // 4. 合并团队ID并构建团队Map(优化:直接复用ownedTeams数据) Set teamIdSet = new HashSet<>(); - teamIdSet.addAll(teamMembers.stream().map(TeamMember::getTeamId).toList()); - teamIdSet.addAll(ownedTeams.stream().map(Team::getId).toList()); + Map teamMap = new HashMap<>(); + + // 4.1 从负责人团队中提取并直接放入teamMap(避免重复查询) + for (Team team : ownedTeams) { + teamIdSet.add(team.getId()); + teamMap.put(team.getId(), team); + } + + // 4.2 从成员关系中提取teamId + Set memberTeamIds = teamMembers.stream() + .map(TeamMember::getTeamId) + .collect(Collectors.toSet()); + teamIdSet.addAll(memberTeamIds); if (teamIdSet.isEmpty()) { log.info("用户 {} 未加入任何团队且不是任何团队的负责人", user.getUsername()); return buildEmptyResult(user); } + // 4.3 只查询成员团队中不在teamMap里的团队(减少查询量) + Set needQueryTeamIds = memberTeamIds.stream() + .filter(id -> !teamMap.containsKey(id)) + .collect(Collectors.toSet()); + + if (!needQueryTeamIds.isEmpty()) { + teamRepository.findAllById(needQueryTeamIds).forEach(team -> + teamMap.put(team.getId(), team) + ); + } + List teamIds = new ArrayList<>(teamIdSet); - // 3. 批量查询团队信息 - Map teamMap = teamRepository.findAllById(teamIds).stream().collect(toMap(Team::getId, t -> t)); - - // 4. 批量查询团队配置 - Map configMap = teamConfigRepository.findByTeamIdIn(teamIds).stream().collect(toMap(TeamConfig::getTeamId, c -> c)); - - // 5. 收集所有环境ID - Set allEnvIds = configMap.values().stream().filter(c -> c.getAllowedEnvironmentIds() != null).flatMap(c -> c.getAllowedEnvironmentIds().stream()).collect(Collectors.toSet()); + // 5. 批量查询所有团队的应用配置 + List allTeamApps = teamApplicationRepository.findByTeamIdIn(teamIds); + + // 5.1 提前判断:如果没有应用配置,直接返回 + if (allTeamApps.isEmpty()) { + log.info("用户 {} 所属团队未配置任何应用", user.getUsername()); + return buildEmptyResult(user); + } + + // 6. 一次遍历提取所有需要的ID集合(优化:减少4次额外遍历) + Set allEnvIds = new HashSet<>(); + Set appIds = new HashSet<>(); + Set systemIds = new HashSet<>(); + Set workflowIds = new HashSet<>(); + List teamApplicationIds = new ArrayList<>(allTeamApps.size()); + + for (TeamApplication ta : allTeamApps) { + teamApplicationIds.add(ta.getId()); + allEnvIds.add(ta.getEnvironmentId()); + appIds.add(ta.getApplicationId()); + if (ta.getDeploySystemId() != null) { + systemIds.add(ta.getDeploySystemId()); + } + if (ta.getWorkflowDefinitionId() != null) { + workflowIds.add(ta.getWorkflowDefinitionId()); + } + } if (allEnvIds.isEmpty()) { log.info("用户 {} 所属团队未配置任何环境", user.getUsername()); return buildEmptyResult(user); } - // 6. 批量查询环境信息 - Map envMap = environmentRepository.findAllById(allEnvIds).stream().collect(toMap(Environment::getId, e -> e)); + // 7. 应用配置按团队分组 + Map> teamAppsMap = allTeamApps.stream() + .collect(groupingBy(TeamApplication::getTeamId)); - // 7. 批量查询所有团队的应用配置 - List allTeamApps = teamApplicationRepository.findByTeamIdIn(teamIds); - Map> teamAppsMap = allTeamApps.stream().collect(groupingBy(TeamApplication::getTeamId)); + // 8. 批量查询环境信息 + Map envMap = environmentRepository.findAllById(allEnvIds).stream() + .collect(toMap(Environment::getId, e -> e)); - // 8. 批量查询应用信息 - Set appIds = allTeamApps.stream().map(TeamApplication::getApplicationId).collect(Collectors.toSet()); + // 9. 批量查询应用信息 final Map appMap; if (!appIds.isEmpty()) { - appMap = applicationRepository.findAllById(appIds).stream().collect(toMap(Application::getId, a -> a)); + appMap = applicationRepository.findAllById(appIds).stream() + .collect(toMap(Application::getId, a -> a)); } else { appMap = Collections.emptyMap(); } - // 9. 批量查询部署系统 - Set systemIds = allTeamApps.stream().map(TeamApplication::getDeploySystemId).filter(Objects::nonNull).collect(Collectors.toSet()); + // 10. 批量查询部署系统 final Map systemMap; if (!systemIds.isEmpty()) { - systemMap = externalSystemRepository.findAllById(systemIds).stream().collect(toMap(ExternalSystem::getId, s -> s)); + systemMap = externalSystemRepository.findAllById(systemIds).stream() + .collect(toMap(ExternalSystem::getId, s -> s)); } else { systemMap = Collections.emptyMap(); } - // 10. 批量查询工作流定义 - Set workflowIds = allTeamApps.stream().map(TeamApplication::getWorkflowDefinitionId).filter(Objects::nonNull).collect(Collectors.toSet()); + // 11. 批量查询工作流定义 final Map workflowMap; if (!workflowIds.isEmpty()) { - workflowMap = workflowDefinitionRepository.findAllById(workflowIds).stream().collect(toMap(WorkflowDefinition::getId, w -> w)); + workflowMap = workflowDefinitionRepository.findAllById(workflowIds).stream() + .collect(toMap(WorkflowDefinition::getId, w -> w)); } else { workflowMap = Collections.emptyMap(); } - // 11. 批量查询审批人信息 - Set approverUserIds = configMap.values().stream().filter(c -> c.getApproverUserIds() != null).flatMap(c -> c.getApproverUserIds().stream()).filter(obj -> obj instanceof List).flatMap(obj -> ((List) obj).stream()).filter(id -> id instanceof Number).map(id -> ((Number) id).longValue()).collect(Collectors.toSet()); + // 11. 批量查询团队环境配置 + List teamEnvConfigs = teamEnvironmentConfigRepository.findByTeamIdIn(teamIds); + // 按 (teamId, environmentId) 组织配置: key = teamId + "_" + environmentId + Map teamEnvConfigMap = teamEnvConfigs.stream() + .collect(toMap(c -> c.getTeamId() + "_" + c.getEnvironmentId(), c -> c)); + + // 12. 批量查询审批人信息 + Set approverUserIds = teamEnvConfigs.stream() + .filter(c -> c.getApproverUserIds() != null) + .flatMap(c -> c.getApproverUserIds().stream()) + .collect(Collectors.toSet()); final Map approverMap; if (!approverUserIds.isEmpty()) { approverMap = userRepository.findAllById(approverUserIds).stream().collect(toMap(User::getId, u -> u)); @@ -164,9 +233,7 @@ public class DeployServiceImpl implements IDeployService { approverMap = Collections.emptyMap(); } - // 12. 批量查询部署记录信息 - List teamApplicationIds = allTeamApps.stream().map(TeamApplication::getId).collect(toList()); - + // 13. 批量查询部署记录信息(teamApplicationIds 已在第6步提取) // 12.1 批量查询部署统计信息 final Map statisticsMap = new HashMap<>(); final Map latestRecordMap = new HashMap<>(); @@ -220,10 +287,33 @@ public class DeployServiceImpl implements IDeployService { recentRecordsMap.putAll(recentRecords.stream().collect(groupingBy(DeployRecord::getTeamApplicationId))); } - // 13. 组装团队数据 - Map teamMemberMap = teamMembers.stream().collect(toMap(TeamMember::getTeamId, tm -> tm)); + // 13. 组装团队数据(优化:处理潜在的Key冲突,保留第一个成员记录) + Map teamMemberMap = teamMembers.stream() + .collect(toMap( + TeamMember::getTeamId, + tm -> tm, + (existing, replacement) -> existing // 如果有重复,保留第一个 + )); - List teamDTOs = teamIds.stream().map(teamId -> buildTeamDTO(teamId, currentUserId, teamMap, teamMemberMap, configMap, envMap, teamAppsMap, appMap, systemMap, workflowMap, approverMap, statisticsMap, latestRecordMap, recentRecordsMap)).filter(Objects::nonNull).collect(toList()); + // 14. 构建上下文对象(避免方法参数过多) + BuildContext context = new BuildContext(); + context.teamMap = teamMap; + context.teamMemberMap = teamMemberMap; + context.teamEnvConfigMap = teamEnvConfigMap; + context.envMap = envMap; + context.teamAppsMap = teamAppsMap; + context.appMap = appMap; + context.systemMap = systemMap; + context.workflowMap = workflowMap; + context.approverMap = approverMap; + context.statisticsMap = statisticsMap; + context.latestRecordMap = latestRecordMap; + context.recentRecordsMap = recentRecordsMap; + + List teamDTOs = teamIds.stream() + .map(teamId -> buildTeamDTO(teamId, currentUserId, context)) + .filter(Objects::nonNull) + .collect(toList()); // 14. 组装最终结果 UserDeployableDTO result = new UserDeployableDTO(); @@ -251,14 +341,13 @@ public class DeployServiceImpl implements IDeployService { /** * 构建团队DTO */ - private TeamDeployableDTO buildTeamDTO(Long teamId, Long currentUserId, Map teamMap, Map teamMemberMap, Map configMap, Map envMap, Map> teamAppsMap, Map appMap, Map systemMap, Map workflowMap, Map approverMap, Map statisticsMap, Map latestRecordMap, Map> recentRecordsMap) { - Team team = teamMap.get(teamId); + private TeamDeployableDTO buildTeamDTO(Long teamId, Long currentUserId, BuildContext ctx) { + Team team = ctx.teamMap.get(teamId); if (team == null) { return null; } - TeamMember member = teamMemberMap.get(teamId); - TeamConfig config = configMap.get(teamId); + TeamMember member = ctx.teamMemberMap.get(teamId); TeamDeployableDTO teamDTO = new TeamDeployableDTO(); teamDTO.setTeamId(team.getId()); @@ -275,40 +364,45 @@ public class DeployServiceImpl implements IDeployService { teamDTO.setTeamRole(null); } - // 组装环境数据 - if (config != null && config.getAllowedEnvironmentIds() != null && !config.getAllowedEnvironmentIds().isEmpty()) { - List allowedEnvIds = config.getAllowedEnvironmentIds(); - List teamApps = teamAppsMap.getOrDefault(teamId, Collections.emptyList()); + // 组装环境数据(从 TeamApplication 中提取该团队的所有环境) + List teamApps = ctx.teamAppsMap.getOrDefault(teamId, Collections.emptyList()); + + // 按环境分组应用 + Map> appsByEnv = teamApps.stream() + .collect(groupingBy(TeamApplication::getEnvironmentId)); - // 按环境分组应用 - Map> appsByEnv = teamApps.stream().collect(groupingBy(TeamApplication::getEnvironmentId)); - - List envDTOs = new ArrayList<>(); - for (int i = 0; i < allowedEnvIds.size(); i++) { - Long envId = allowedEnvIds.get(i); - Environment env = envMap.get(envId); - if (env == null) { - continue; - } - - DeployableEnvironmentDTO envDTO = buildEnvironmentDTO(env, config, i, appsByEnv.getOrDefault(envId, Collections.emptyList()), appMap, systemMap, workflowMap, approverMap, statisticsMap, latestRecordMap, recentRecordsMap); - - envDTOs.add(envDTO); + List envDTOs = new ArrayList<>(); + for (Long envId : appsByEnv.keySet()) { + Environment env = ctx.envMap.get(envId); + if (env == null) { + continue; } - envDTOs.sort(Comparator.comparing(DeployableEnvironmentDTO::getSort)); - teamDTO.setEnvironments(envDTOs); - } else { - teamDTO.setEnvironments(Collections.emptyList()); + // 查找该团队+环境的配置 + String configKey = teamId + "_" + envId; + TeamEnvironmentConfig envConfig = ctx.teamEnvConfigMap.get(configKey); + + // 只显示在 deploy_team_environment_config 表中配置过的环境 + if (envConfig == null) { + log.debug("团队 {} 的环境 {} 未在 team_environment_config 中配置,跳过", teamId, envId); + continue; + } + + DeployableEnvironmentDTO envDTO = buildEnvironmentDTO(env, envConfig, appsByEnv.get(envId), ctx); + + envDTOs.add(envDTO); } + envDTOs.sort(Comparator.comparing(DeployableEnvironmentDTO::getSort)); + teamDTO.setEnvironments(envDTOs); + return teamDTO; } /** * 构建环境DTO */ - private DeployableEnvironmentDTO buildEnvironmentDTO(Environment env, TeamConfig config, int envIndex, List teamApps, Map appMap, Map systemMap, Map workflowMap, Map approverMap, Map statisticsMap, Map latestRecordMap, Map> recentRecordsMap) { + private DeployableEnvironmentDTO buildEnvironmentDTO(Environment env, TeamEnvironmentConfig envConfig, List teamApps, BuildContext ctx) { DeployableEnvironmentDTO envDTO = new DeployableEnvironmentDTO(); envDTO.setEnvironmentId(env.getId()); envDTO.setEnvironmentCode(env.getEnvCode()); @@ -317,29 +411,27 @@ public class DeployServiceImpl implements IDeployService { envDTO.setEnabled(env.getEnabled()); envDTO.setSort(env.getSort()); - // 审批配置 - if (config.getEnvironmentApprovalRequired() != null && envIndex < config.getEnvironmentApprovalRequired().size()) { - envDTO.setRequiresApproval(config.getEnvironmentApprovalRequired().get(envIndex)); - } else { - envDTO.setRequiresApproval(false); - } - - // 审批人 - if (config.getApproverUserIds() != null && envIndex < config.getApproverUserIds().size()) { - Object approverObj = config.getApproverUserIds().get(envIndex); - if (approverObj instanceof List) { - List approverList = (List) approverObj; - List approverDTOs = approverList.stream().filter(id -> id instanceof Number).map(id -> buildApproverDTO(((Number) id).longValue(), approverMap)).filter(Objects::nonNull).collect(toList()); - envDTO.setApprovers(approverDTOs); - } else { - envDTO.setApprovers(Collections.emptyList()); - } + // 从 TeamEnvironmentConfig 读取配置信息 + envDTO.setRequiresApproval(envConfig != null ? envConfig.getApprovalRequired() : false); + envDTO.setRequireCodeReview(envConfig != null ? envConfig.getRequireCodeReview() : false); + envDTO.setRemark(envConfig != null ? envConfig.getRemark() : null); + + // 审批人列表(需要从 List 转换为 List 以包含用户详细信息) + if (envConfig != null && envConfig.getApproverUserIds() != null && !envConfig.getApproverUserIds().isEmpty()) { + List approverDTOs = envConfig.getApproverUserIds().stream() + .map(id -> buildApproverDTO(id, ctx.approverMap)) + .filter(Objects::nonNull) + .collect(toList()); + envDTO.setApprovers(approverDTOs); } else { envDTO.setApprovers(Collections.emptyList()); } // 应用列表 - List appDTOs = teamApps.stream().map(ta -> buildApplicationDTO(ta, appMap, systemMap, workflowMap, statisticsMap, latestRecordMap, recentRecordsMap)).filter(Objects::nonNull).collect(toList()); + List appDTOs = teamApps.stream() + .map(ta -> buildApplicationDTO(ta, ctx)) + .filter(Objects::nonNull) + .collect(toList()); envDTO.setApplications(appDTOs); @@ -365,8 +457,8 @@ public class DeployServiceImpl implements IDeployService { /** * 构建应用DTO */ - private DeployableApplicationDTO buildApplicationDTO(TeamApplication ta, Map appMap, Map systemMap, Map workflowMap, Map statisticsMap, Map latestRecordMap, Map> recentRecordsMap) { - Application app = appMap.get(ta.getApplicationId()); + private DeployableApplicationDTO buildApplicationDTO(TeamApplication ta, BuildContext ctx) { + Application app = ctx.appMap.get(ta.getApplicationId()); if (app == null) { return null; } @@ -382,7 +474,7 @@ public class DeployServiceImpl implements IDeployService { // 部署系统 dto.setDeploySystemId(ta.getDeploySystemId()); if (ta.getDeploySystemId() != null) { - ExternalSystem system = systemMap.get(ta.getDeploySystemId()); + ExternalSystem system = ctx.systemMap.get(ta.getDeploySystemId()); dto.setDeploySystemName(system != null ? system.getName() : null); } @@ -392,7 +484,7 @@ public class DeployServiceImpl implements IDeployService { // 工作流定义 dto.setWorkflowDefinitionId(ta.getWorkflowDefinitionId()); if (ta.getWorkflowDefinitionId() != null) { - WorkflowDefinition workflow = workflowMap.get(ta.getWorkflowDefinitionId()); + WorkflowDefinition workflow = ctx.workflowMap.get(ta.getWorkflowDefinitionId()); if (workflow != null) { dto.setWorkflowDefinitionName(workflow.getName()); dto.setWorkflowDefinitionKey(workflow.getKey()); @@ -400,12 +492,12 @@ public class DeployServiceImpl implements IDeployService { } // 部署统计信息和记录 - DeployStatisticsDTO statistics = statisticsMap.get(ta.getId()); + DeployStatisticsDTO statistics = ctx.statisticsMap.get(ta.getId()); if (statistics != null) { dto.setDeployStatistics(statistics); // 判断是否正在部署中 - DeployRecord latestRecord = latestRecordMap.get(ta.getId()); + DeployRecord latestRecord = ctx.latestRecordMap.get(ta.getId()); if (latestRecord != null) { DeployRecordStatusEnums status = latestRecord.getStatus(); dto.setIsDeploying(status == DeployRecordStatusEnums.CREATED || status == DeployRecordStatusEnums.RUNNING); @@ -424,7 +516,7 @@ public class DeployServiceImpl implements IDeployService { } // 最近部署记录列表 - List recentRecords = recentRecordsMap.getOrDefault(ta.getId(), Collections.emptyList()); + List recentRecords = ctx.recentRecordsMap.getOrDefault(ta.getId(), Collections.emptyList()); List recordSummaryList = recentRecords.stream().map(this::buildDeployRecordSummary).collect(toList()); dto.setRecentDeployRecords(recordSummaryList); diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/EnvironmentServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/EnvironmentServiceImpl.java index 65c76bc4..2cfda7ec 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/EnvironmentServiceImpl.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/EnvironmentServiceImpl.java @@ -4,9 +4,11 @@ import com.qqchen.deploy.backend.deploy.converter.EnvironmentConverter; import com.qqchen.deploy.backend.deploy.dto.EnvironmentDTO; import com.qqchen.deploy.backend.deploy.entity.Environment; import com.qqchen.deploy.backend.deploy.entity.QEnvironment; +import com.qqchen.deploy.backend.deploy.entity.TeamEnvironmentConfig; import com.qqchen.deploy.backend.deploy.query.EnvironmentQuery; import com.qqchen.deploy.backend.deploy.repository.IEnvironmentRepository; import com.qqchen.deploy.backend.deploy.repository.ITeamApplicationRepository; +import com.qqchen.deploy.backend.deploy.repository.ITeamEnvironmentConfigRepository; import com.qqchen.deploy.backend.deploy.service.IEnvironmentService; import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl; import com.querydsl.jpa.impl.JPAQueryFactory; @@ -38,21 +40,16 @@ public class EnvironmentServiceImpl extends BaseServiceImpl page(EnvironmentQuery query) { Page page = super.page(query); // 填充统计数据 List content = page.getContent().stream() - .peek(env -> { - // 统计使用该环境的团队数量(去重) - Long teamCount = teamApplicationRepository.countDistinctTeamIdByEnvironmentId(env.getId()); - env.setTeamCount(teamCount != null ? teamCount : 0L); - - // 统计该环境关联的应用数量(去重) - Long appCount = teamApplicationRepository.countDistinctApplicationIdByEnvironmentId(env.getId()); - env.setApplicationCount(appCount != null ? appCount : 0L); - }) + .peek(env -> fillEnvironmentStatistics(env)) .collect(Collectors.toList()); return new PageImpl<>(content, page.getPageable(), page.getTotalElements()); @@ -64,18 +61,38 @@ public class EnvironmentServiceImpl extends BaseServiceImpl { - // 统计使用该环境的团队数量(去重) - Long teamCount = teamApplicationRepository.countDistinctTeamIdByEnvironmentId(env.getId()); - env.setTeamCount(teamCount != null ? teamCount : 0L); - - // 统计该环境关联的应用数量(去重) - Long appCount = teamApplicationRepository.countDistinctApplicationIdByEnvironmentId(env.getId()); - env.setApplicationCount(appCount != null ? appCount : 0L); - }) + .peek(env -> fillEnvironmentStatistics(env)) .collect(Collectors.toList()); } + /** + * 填充环境统计数据 + *

统计逻辑基于 deploy_team_environment_config 表,只统计已配置该环境的团队

+ */ + private void fillEnvironmentStatistics(EnvironmentDTO env) { + // 1. 统计配置了该环境的团队数量 + Long teamCount = teamEnvironmentConfigRepository.countByEnvironmentId(env.getId()); + env.setTeamCount(teamCount != null ? teamCount : 0L); + + // 2. 统计这些团队在该环境下的应用数量(去重) + if (teamCount != null && teamCount > 0) { + // 查询配置了该环境的所有团队ID + List teamIds = teamEnvironmentConfigRepository.findByEnvironmentId(env.getId()) + .stream() + .map(config -> config.getTeamId()) + .collect(Collectors.toList()); + + if (!teamIds.isEmpty()) { + Long appCount = teamApplicationRepository.countDistinctApplicationIdByTeamIdsAndEnvironmentId(teamIds, env.getId()); + env.setApplicationCount(appCount != null ? appCount : 0L); + } else { + env.setApplicationCount(0L); + } + } else { + env.setApplicationCount(0L); + } + } + @Override public List getProjectEnvironments(Long projectGroupId) { QEnvironment environment = QEnvironment.environment; diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/TeamApplicationServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/TeamApplicationServiceImpl.java index 66c78ed0..f5f4132f 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/TeamApplicationServiceImpl.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/TeamApplicationServiceImpl.java @@ -3,10 +3,14 @@ package com.qqchen.deploy.backend.deploy.service.impl; import com.qqchen.deploy.backend.deploy.converter.TeamApplicationConverter; import com.qqchen.deploy.backend.deploy.dto.TeamApplicationDTO; import com.qqchen.deploy.backend.deploy.entity.Application; +import com.qqchen.deploy.backend.deploy.entity.DeployRecord; +import com.qqchen.deploy.backend.deploy.entity.Environment; import com.qqchen.deploy.backend.deploy.entity.Team; import com.qqchen.deploy.backend.deploy.entity.TeamApplication; import com.qqchen.deploy.backend.deploy.query.TeamApplicationQuery; import com.qqchen.deploy.backend.deploy.repository.IApplicationRepository; +import com.qqchen.deploy.backend.deploy.repository.IDeployRecordRepository; +import com.qqchen.deploy.backend.deploy.repository.IEnvironmentRepository; import com.qqchen.deploy.backend.deploy.repository.ITeamApplicationRepository; import com.qqchen.deploy.backend.deploy.repository.ITeamRepository; import com.qqchen.deploy.backend.deploy.service.ITeamApplicationService; @@ -20,14 +24,12 @@ import org.springframework.data.domain.PageImpl; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.List; -import java.util.Optional; +import java.util.*; import java.util.stream.Collectors; @Slf4j @Service -public class TeamApplicationServiceImpl extends BaseServiceImpl - implements ITeamApplicationService { +public class TeamApplicationServiceImpl extends BaseServiceImpl implements ITeamApplicationService { @Resource private ITeamApplicationRepository teamApplicationRepository; @@ -38,6 +40,12 @@ public class TeamApplicationServiceImpl extends BaseServiceImpl page(TeamApplicationQuery query) { Page page = super.page(query); + fillExtendedFields(page.getContent()); + return new PageImpl<>(page.getContent(), page.getPageable(), page.getTotalElements()); + } - // 填充团队名称和应用信息 - List content = page.getContent().stream() - .peek(teamApp -> { - Optional teamOptional = teamRepository.findById(teamApp.getTeamId()); - teamOptional.ifPresent(team -> teamApp.setTeamName(team.getTeamName())); + @Override + public List findAll(TeamApplicationQuery query) { + List list = super.findAll(query); + fillExtendedFields(list); + return list; + } - Optional appOptional = applicationRepository.findById(teamApp.getApplicationId()); - appOptional.ifPresent(app -> { - teamApp.setApplicationName(app.getAppName()); - teamApp.setApplicationCode(app.getAppCode()); - }); - }) - .collect(Collectors.toList()); + /** + * 批量填充扩展字段:teamName、applicationName、applicationCode、environmentName + * + *

使用批量查询避免N+1问题 + */ + private void fillExtendedFields(List teamApps) { + if (teamApps.isEmpty()) { + return; + } - return new PageImpl<>(content, page.getPageable(), page.getTotalElements()); + // 1. 收集所有需要查询的ID + Set teamIds = teamApps.stream() + .map(TeamApplicationDTO::getTeamId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + Set applicationIds = teamApps.stream() + .map(TeamApplicationDTO::getApplicationId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + Set environmentIds = teamApps.stream() + .map(TeamApplicationDTO::getEnvironmentId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + // 2. 批量查询团队信息 + Map teamMap = new HashMap<>(); + if (!teamIds.isEmpty()) { + teamRepository.findAllById(teamIds).forEach(team -> + teamMap.put(team.getId(), team) + ); + } + + // 3. 批量查询应用信息 + Map appMap = new HashMap<>(); + if (!applicationIds.isEmpty()) { + applicationRepository.findAllById(applicationIds).forEach(app -> + appMap.put(app.getId(), app) + ); + } + + // 4. 批量查询环境信息 + Map envMap = new HashMap<>(); + if (!environmentIds.isEmpty()) { + environmentRepository.findAllById(environmentIds).forEach(env -> + envMap.put(env.getId(), env) + ); + } + + // 5. 填充扩展字段 + teamApps.forEach(teamApp -> { + // 填充团队名称 + if (teamApp.getTeamId() != null) { + Team team = teamMap.get(teamApp.getTeamId()); + if (team != null) { + teamApp.setTeamName(team.getTeamName()); + } + } + + // 填充应用信息 + if (teamApp.getApplicationId() != null) { + Application app = appMap.get(teamApp.getApplicationId()); + if (app != null) { + teamApp.setApplicationName(app.getAppName()); + teamApp.setApplicationCode(app.getAppCode()); + } + } + + // 填充环境名称 + if (teamApp.getEnvironmentId() != null) { + Environment env = envMap.get(teamApp.getEnvironmentId()); + if (env != null) { + teamApp.setEnvironmentName(env.getEnvName()); + } + } + }); + } + + /** + * 删除团队应用及其关联的部署记录 + * + *

删除逻辑: + *

    + *
  • 1. 先逻辑删除所有关联的部署记录(DeployRecord)
  • + *
  • 2. 再物理删除团队应用(TeamApplication)
  • + *
+ * + *

注意: + *

    + *
  • TeamApplication 使用物理删除(@LogicDelete(false))
  • + *
  • DeployRecord 使用逻辑删除(@LogicDelete)
  • + *
+ */ + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Long id) { + log.info("开始删除团队应用,ID: {}", id); + + // 1. 查询关联的部署记录 + List deployRecords = deployRecordRepository.findByTeamApplicationId(id); + + if (!deployRecords.isEmpty()) { + log.info("团队应用 {} 关联了 {} 条部署记录,开始级联删除", id, deployRecords.size()); + + // 2. 逻辑删除所有关联的部署记录 + deployRecords.forEach(record -> { + record.setDeleted(true); + deployRecordRepository.save(record); + }); + + log.info("成功逻辑删除 {} 条部署记录", deployRecords.size()); + } else { + log.info("团队应用 {} 没有关联的部署记录", id); + } + + // 3. 物理删除团队应用 + super.delete(id); + + log.info("成功删除团队应用,ID: {}", id); } } diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/TeamConfigServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/TeamConfigServiceImpl.java deleted file mode 100644 index c1b449da..00000000 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/TeamConfigServiceImpl.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.qqchen.deploy.backend.deploy.service.impl; - -import com.qqchen.deploy.backend.deploy.converter.TeamConfigConverter; -import com.qqchen.deploy.backend.deploy.dto.TeamConfigDTO; -import com.qqchen.deploy.backend.deploy.entity.TeamConfig; -import com.qqchen.deploy.backend.deploy.query.TeamConfigQuery; -import com.qqchen.deploy.backend.deploy.repository.ITeamConfigRepository; -import com.qqchen.deploy.backend.deploy.service.ITeamConfigService; -import com.qqchen.deploy.backend.framework.annotation.ServiceType; -import com.qqchen.deploy.backend.framework.enums.ResponseCode; -import com.qqchen.deploy.backend.framework.exception.BusinessException; -import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -/** - * 团队配置Service实现 - * - * @author qqchen - * @since 2025-10-31 - */ -@Slf4j -@Service -@ServiceType(ServiceType.Type.DATABASE) -public class TeamConfigServiceImpl extends BaseServiceImpl - implements ITeamConfigService { - - @Resource - private ITeamConfigRepository teamConfigRepository; - - @Resource - private TeamConfigConverter teamConfigConverter; - - @Override - public TeamConfigDTO getByTeamId(Long teamId) { - TeamConfig config = teamConfigRepository.findByTeamIdAndDeletedFalse(teamId) - .orElseThrow(() -> new BusinessException(ResponseCode.TEAM_CONFIG_NOT_FOUND, new Object[]{teamId})); - return teamConfigConverter.toDto(config); - } -} - diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/TeamEnvironmentConfigServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/TeamEnvironmentConfigServiceImpl.java new file mode 100644 index 00000000..44b92c0c --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/TeamEnvironmentConfigServiceImpl.java @@ -0,0 +1,152 @@ +package com.qqchen.deploy.backend.deploy.service.impl; + +import com.qqchen.deploy.backend.deploy.dto.TeamEnvironmentConfigDTO; +import com.qqchen.deploy.backend.deploy.entity.Environment; +import com.qqchen.deploy.backend.deploy.entity.TeamEnvironmentConfig; +import com.qqchen.deploy.backend.deploy.query.TeamEnvironmentConfigQuery; +import com.qqchen.deploy.backend.deploy.repository.IEnvironmentRepository; +import com.qqchen.deploy.backend.deploy.repository.ITeamApplicationRepository; +import com.qqchen.deploy.backend.deploy.repository.ITeamEnvironmentConfigRepository; +import com.qqchen.deploy.backend.deploy.service.ITeamEnvironmentConfigService; +import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl; +import com.qqchen.deploy.backend.notification.entity.NotificationChannel; +import com.qqchen.deploy.backend.notification.repository.INotificationChannelRepository; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 团队环境配置Service实现 + * + * @author qqchen + * @since 2025-11-04 + */ +@Slf4j +@Service +public class TeamEnvironmentConfigServiceImpl + extends BaseServiceImpl + implements ITeamEnvironmentConfigService { + + @Resource + private ITeamEnvironmentConfigRepository teamEnvironmentConfigRepository; + + @Resource + private IEnvironmentRepository environmentRepository; + + @Resource + private INotificationChannelRepository notificationChannelRepository; + + @Resource + private ITeamApplicationRepository teamApplicationRepository; + + @Override + public List findByTeamId(Long teamId) { + List list = teamEnvironmentConfigRepository.findByTeamId(teamId).stream() + .map(converter::toDto) + .collect(Collectors.toList()); + fillExtendedFields(list); + return list; + } + + @Override + public TeamEnvironmentConfigDTO findByTeamIdAndEnvironmentId(Long teamId, Long environmentId) { + TeamEnvironmentConfigDTO dto = teamEnvironmentConfigRepository.findByTeamIdAndEnvironmentId(teamId, environmentId) + .map(converter::toDto) + .orElse(null); + if (dto != null) { + fillExtendedFields(Collections.singletonList(dto)); + } + return dto; + } + + @Override + public boolean exists(Long teamId, Long environmentId) { + return teamEnvironmentConfigRepository.existsByTeamIdAndEnvironmentId(teamId, environmentId); + } + + @Override + public Page page(TeamEnvironmentConfigQuery query) { + Page page = super.page(query); + fillExtendedFields(page.getContent()); + return new PageImpl<>(page.getContent(), page.getPageable(), page.getTotalElements()); + } + + @Override + public List findAll(TeamEnvironmentConfigQuery query) { + List list = super.findAll(query); + fillExtendedFields(list); + return list; + } + + /** + * 批量填充扩展字段:environmentName、notificationChannelName、applicationCount + * + *

使用批量查询避免N+1问题 + */ + private void fillExtendedFields(List configs) { + if (configs.isEmpty()) { + return; + } + + // 1. 收集所有环境ID和通知渠道ID + Set environmentIds = configs.stream() + .map(TeamEnvironmentConfigDTO::getEnvironmentId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + Set channelIds = configs.stream() + .map(TeamEnvironmentConfigDTO::getNotificationChannelId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + // 2. 批量查询环境信息 + Map environmentMap = new HashMap<>(); + if (!environmentIds.isEmpty()) { + environmentRepository.findAllById(environmentIds).forEach(env -> + environmentMap.put(env.getId(), env) + ); + } + + // 3. 批量查询通知渠道信息 + Map channelMap = new HashMap<>(); + if (!channelIds.isEmpty()) { + notificationChannelRepository.findAllById(channelIds).forEach(channel -> + channelMap.put(channel.getId(), channel) + ); + } + + // 4. 填充扩展字段 + configs.forEach(config -> { + // 填充环境名称 + if (config.getEnvironmentId() != null) { + Environment env = environmentMap.get(config.getEnvironmentId()); + if (env != null) { + config.setEnvironmentName(env.getEnvName()); + } + } + + // 填充通知渠道名称 + if (config.getNotificationChannelId() != null) { + NotificationChannel channel = channelMap.get(config.getNotificationChannelId()); + if (channel != null) { + config.setNotificationChannelName(channel.getName()); + } + } + + // 填充应用数量统计 + if (config.getTeamId() != null && config.getEnvironmentId() != null) { + Long appCount = teamApplicationRepository.countByTeamIdAndEnvironmentId( + config.getTeamId(), + config.getEnvironmentId() + ); + config.setApplicationCount(appCount != null ? appCount : 0L); + } + }); + } +} + diff --git a/backend/src/main/java/com/qqchen/deploy/backend/framework/utils/NestedMapUtils.java b/backend/src/main/java/com/qqchen/deploy/backend/framework/utils/NestedMapUtils.java index 94beb378..7011b843 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/framework/utils/NestedMapUtils.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/framework/utils/NestedMapUtils.java @@ -344,5 +344,198 @@ public class NestedMapUtils { return getValueFromExecution(execution, variablePath); } + + // ==================== 统一解析入口(推荐使用) ==================== + + /** + * 统一解析方法 - 智能处理所有类型的输入 + * + *

自动识别并处理三种情况: + *

    + *
  • 纯文本: "hello" → "hello"
  • + *
  • 纯表达式: "${user.name}" → Object (原始类型,可能是 String/Integer/Map 等)
  • + *
  • 模板字符串: "Hello ${user.name}, age: ${user.age}" → "Hello John, age: 30"
  • + *
+ * + *

使用示例: + *

+     * // 纯表达式 - 返回原始类型
+     * Object status = NestedMapUtils.resolve(execution, "${jenkins.status}");
+     * // 返回: NodeExecutionStatusEnum.SUCCESS (枚举类型)
+     * 
+     * // 模板字符串 - 返回替换后的字符串
+     * String message = (String) NestedMapUtils.resolve(execution, "构建${jenkins.status}, 编号: ${jenkins.buildNumber}");
+     * // 返回: "构建SUCCESS, 编号: 123"
+     * 
+     * // 纯文本 - 直接返回
+     * String text = (String) NestedMapUtils.resolve(execution, "纯文本");
+     * // 返回: "纯文本"
+     * 
+ * + * @param execution Flowable 执行上下文 + * @param input 输入字符串(可能包含表达式) + * @return 解析后的值(可能是任意类型) + */ + public static Object resolve(DelegateExecution execution, String input) { + if (input == null || !input.contains("${")) { + return input; + } + + // 判断是否是纯表达式(整个字符串就是一个 ${xxx}) + if (isPureExpression(input)) { + // 纯表达式:返回原始类型(可能是 String/Integer/Enum/Map 等) + return resolveExpression(execution, input); + } else { + // 模板字符串:返回替换后的字符串 + return resolveTemplateString(execution, input); + } + } + + /** + * 统一解析方法 - DelegateTask 版本 + * + * @param task Flowable 任务上下文 + * @param input 输入字符串 + * @return 解析后的值 + */ + public static Object resolve(DelegateTask task, String input) { + if (input == null || !input.contains("${")) { + return input; + } + + if (isPureExpression(input)) { + return resolveExpression(task, input); + } else { + return resolveTemplateString(task, input); + } + } + + /** + * 判断是否是纯表达式 + * 纯表达式定义:整个字符串就是一个 ${xxx},前后没有其他内容 + * + * @param input 输入字符串 + * @return true 如果是纯表达式 + */ + private static boolean isPureExpression(String input) { + if (!input.startsWith("${") || !input.endsWith("}")) { + return false; + } + + // 检查是否只有一个表达式(没有第二个 ${) + int secondExprStart = input.indexOf("${", 2); + return secondExprStart == -1; + } + + // ==================== 内部实现方法 ==================== + + /** + * 解析模板字符串(支持多个嵌入式表达式) + * + *

内部方法,推荐使用 {@link #resolve(DelegateExecution, String)} 代替 + * + * @param execution Flowable 执行上下文 + * @param template 模板字符串 + * @return 替换后的字符串 + */ + private static String resolveTemplateString(DelegateExecution execution, String template) { + if (template == null || !template.contains("${")) { + return template; + } + + StringBuilder result = new StringBuilder(); + int currentIndex = 0; + + while (currentIndex < template.length()) { + int startIndex = template.indexOf("${", currentIndex); + + // 没有更多的表达式了 + if (startIndex == -1) { + result.append(template.substring(currentIndex)); + break; + } + + // 添加表达式前的普通文本 + if (startIndex > currentIndex) { + result.append(template.substring(currentIndex, startIndex)); + } + + // 查找表达式的结束位置 + int endIndex = template.indexOf("}", startIndex); + if (endIndex == -1) { + // 没有找到闭合的 },说明格式错误,保留原样 + log.warn("未找到闭合的 }} 符号,保留原样: {}", template.substring(startIndex)); + result.append(template.substring(startIndex)); + break; + } + + // 提取并解析表达式 + String expression = template.substring(startIndex, endIndex + 1); + try { + Object value = resolveExpression(execution, expression); + result.append(value != null ? value.toString() : ""); + log.debug(" 替换表达式: {} -> {}", expression, value); + } catch (Exception e) { + log.warn(" 表达式解析失败: {},保留原样", expression, e); + result.append(expression); + } + + currentIndex = endIndex + 1; + } + + return result.toString(); + } + + /** + * 解析模板字符串(支持多个嵌入式表达式)- DelegateTask 版本 + * + *

内部方法,推荐使用 {@link #resolve(DelegateTask, String)} 代替 + * + * @param task Flowable 任务上下文 + * @param template 模板字符串 + * @return 替换后的字符串 + */ + private static String resolveTemplateString(DelegateTask task, String template) { + if (template == null || !template.contains("${")) { + return template; + } + + StringBuilder result = new StringBuilder(); + int currentIndex = 0; + + while (currentIndex < template.length()) { + int startIndex = template.indexOf("${", currentIndex); + + if (startIndex == -1) { + result.append(template.substring(currentIndex)); + break; + } + + if (startIndex > currentIndex) { + result.append(template.substring(currentIndex, startIndex)); + } + + int endIndex = template.indexOf("}", startIndex); + if (endIndex == -1) { + log.warn("未找到闭合的 }} 符号,保留原样: {}", template.substring(startIndex)); + result.append(template.substring(startIndex)); + break; + } + + String expression = template.substring(startIndex, endIndex + 1); + try { + Object value = resolveExpression(task, expression); + result.append(value != null ? value.toString() : ""); + log.debug(" 替换表达式: {} -> {}", expression, value); + } catch (Exception e) { + log.warn(" 表达式解析失败: {},保留原样", expression, e); + result.append(expression); + } + + currentIndex = endIndex + 1; + } + + return result.toString(); + } } diff --git a/backend/src/main/java/com/qqchen/deploy/backend/notification/adapter/impl/WeworkChannelAdapter.java b/backend/src/main/java/com/qqchen/deploy/backend/notification/adapter/impl/WeworkChannelAdapter.java index 9a2a89a0..665bfd66 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/notification/adapter/impl/WeworkChannelAdapter.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/notification/adapter/impl/WeworkChannelAdapter.java @@ -50,21 +50,33 @@ public class WeworkChannelAdapter implements INotificationChannelAdapter { // 4. 构建企业微信消息体 Map messageBody = new HashMap<>(); - messageBody.put("msgtype", "text"); - Map textContent = new HashMap<>(); - textContent.put("content", message); + // 智能判断消息类型:如果包含 Markdown 标签,使用 markdown 类型 + boolean isMarkdown = isMarkdownMessage(message); + String msgType = isMarkdown ? "markdown" : "text"; + messageBody.put("msgtype", msgType); - if (!CollectionUtils.isEmpty(mentionedList)) { - textContent.put("mentioned_list", mentionedList); + if (isMarkdown) { + // Markdown 格式消息 + Map markdownContent = new HashMap<>(); + markdownContent.put("content", message); + messageBody.put("markdown", markdownContent); + } else { + // 纯文本消息 + Map textContent = new HashMap<>(); + textContent.put("content", message); + + if (!CollectionUtils.isEmpty(mentionedList)) { + textContent.put("mentioned_list", mentionedList); + } + + if (!CollectionUtils.isEmpty(mentionedMobileList)) { + textContent.put("mentioned_mobile_list", mentionedMobileList); + } + + messageBody.put("text", textContent); } - if (!CollectionUtils.isEmpty(mentionedMobileList)) { - textContent.put("mentioned_mobile_list", mentionedMobileList); - } - - messageBody.put("text", textContent); - // 5. 发送请求 HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); @@ -72,7 +84,7 @@ public class WeworkChannelAdapter implements INotificationChannelAdapter { String jsonBody = objectMapper.writeValueAsString(messageBody); HttpEntity entity = new HttpEntity<>(jsonBody, headers); - log.info("发送企业微信通知 - URL: {}, 消息: {}", weworkConfig.getWebhookUrl(), message); + log.info("发送企业微信通知 - URL: {}, 类型: {}, 消息: {}", weworkConfig.getWebhookUrl(), msgType, message); String response = restTemplate.exchange( weworkConfig.getWebhookUrl(), @@ -148,5 +160,28 @@ public class WeworkChannelAdapter implements INotificationChannelAdapter { return mentionedMobileList; } + + /** + * 判断是否是 Markdown 格式消息 + * 企业微信支持的 Markdown 标签包括: + * - 文本 + * - **加粗** + * - `代码` + * - [链接](url) + * + * @param message 消息内容 + * @return true 如果包含 Markdown 标签 + */ + private boolean isMarkdownMessage(String message) { + if (message == null || message.isEmpty()) { + return false; + } + + // 检测企业微信 Markdown 特征标签 + return message.contains(" { +public class NotificationChannelApiController extends BaseController { @Resource private INotificationChannelService notificationChannelService; @@ -45,23 +46,23 @@ public class NotificationChannelApiController private INotificationSendService notificationSendService; @Override - public Response create(NotificationChannelDTO dto) { + public Response create(@Validated @RequestBody NotificationChannelDTO dto) { return super.create(dto); } @Override - public Response update(Long aLong, NotificationChannelDTO dto) { - return super.update(aLong, dto); + public Response update(@PathVariable Long id,@Validated @RequestBody NotificationChannelDTO dto) { + return super.update(id, dto); } @Override - public Response delete(Long aLong) { - return super.delete(aLong); + public Response delete(@PathVariable Long id) { + return super.delete(id); } @Override - public Response findById(Long aLong) { - return super.findById(aLong); + public Response findById(@PathVariable Long id) { + return super.findById(id); } @Override diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/delegate/BaseNodeDelegate.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/delegate/BaseNodeDelegate.java index c0b10439..5c50e659 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/delegate/BaseNodeDelegate.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/delegate/BaseNodeDelegate.java @@ -2,6 +2,7 @@ package com.qqchen.deploy.backend.workflow.delegate; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.qqchen.deploy.backend.framework.utils.NestedMapUtils; import com.qqchen.deploy.backend.workflow.dto.outputs.BaseNodeOutputs; import com.qqchen.deploy.backend.workflow.enums.NodeExecutionStatusEnum; import com.qqchen.deploy.backend.workflow.model.NodeContext; @@ -174,7 +175,7 @@ public abstract class BaseNodeDelegate implements JavaDelegate { /** * 解析Map中的表达式 - * 使用简单的字符串替换方式,从execution变量中获取值 + * 自动处理纯表达式、模板字符串、纯文本三种情况 */ protected Map resolveExpressions(Map inputMap, DelegateExecution execution) { Map resolvedMap = new HashMap<>(); @@ -186,18 +187,13 @@ public abstract class BaseNodeDelegate implements JavaDelegate { if (value instanceof String) { String strValue = (String) value; - if (strValue.contains("${")) { - try { - // 使用工具类直接解析表达式 - Object resolvedValue = com.qqchen.deploy.backend.framework.utils.NestedMapUtils - .resolveExpression(execution, strValue); - log.debug("解析表达式: {} = {} -> {}", entry.getKey(), strValue, resolvedValue); - resolvedMap.put(entry.getKey(), resolvedValue); - } catch (Exception e) { - log.warn("Failed to resolve expression: {}, using original value", strValue, e); - resolvedMap.put(entry.getKey(), value); - } - } else { + try { + // ✅ 使用统一的 resolve 方法,自动智能处理所有情况 + Object resolvedValue = NestedMapUtils.resolve(execution, strValue); + log.debug("解析字段: {} = {} -> {}", entry.getKey(), strValue, resolvedValue); + resolvedMap.put(entry.getKey(), resolvedValue); + } catch (Exception e) { + log.warn("解析失败: {},使用原始值", strValue, e); resolvedMap.put(entry.getKey(), value); } } else { diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/delegate/NotificationNodeDelegate.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/delegate/NotificationNodeDelegate.java index 9d609187..3333c8d8 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/delegate/NotificationNodeDelegate.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/delegate/NotificationNodeDelegate.java @@ -25,11 +25,7 @@ public class NotificationNodeDelegate extends BaseNodeDelegate configs, - NotificationInputMapping input - ) { + protected NotificationOutputs executeInternal(DelegateExecution execution, Map configs, NotificationInputMapping input) { log.info("Sending notification - channel: {}, title: {}, content: {}", input.getChannelId(), input.getTitle(), input.getContent()); try { diff --git a/backend/src/main/resources/db/changelog/changes/v1.0.0-data.sql b/backend/src/main/resources/db/changelog/changes/v1.0.0-data.sql index 0fa63722..6b25f25e 100644 --- a/backend/src/main/resources/db/changelog/changes/v1.0.0-data.sql +++ b/backend/src/main/resources/db/changelog/changes/v1.0.0-data.sql @@ -1160,13 +1160,6 @@ VALUES (2, 4, 'ops_manager', '负责人', NOW(), 'admin', NOW(), 'admin', NOW(), 1, 0), (2, 2, 'it_manager', '运维', NOW(), 'admin', NOW(), 'admin', NOW(), 1, 0); --- 初始化团队配置数据 -INSERT INTO deploy_team_config (team_id, allowed_environment_ids, environment_approval_required, approver_user_ids, create_by, create_time, update_by, update_time, version, deleted) -VALUES --- 平台研发团队配置(可访问开发和测试环境,都不需要审批) -(1, '[1, 2]', '[false, false]', '[null, null]', 'admin', NOW(), 'admin', NOW(), 1, 0), --- DevOps团队配置(可访问所有环境,生产环境需要审批,审批人是用户1和4) -(2, '[1, 2, 3]', '[false, false, true]', '[null, null, [1, 4]]', 'admin', NOW(), 'admin', NOW(), 1, 0); -- ==================================================================== -- 定时任务初始化数据 diff --git a/backend/src/main/resources/db/changelog/changes/v1.0.0-schema.sql b/backend/src/main/resources/db/changelog/changes/v1.0.0-schema.sql index beb77ae8..e367011d 100644 --- a/backend/src/main/resources/db/changelog/changes/v1.0.0-schema.sql +++ b/backend/src/main/resources/db/changelog/changes/v1.0.0-schema.sql @@ -846,29 +846,40 @@ CREATE TABLE deploy_team_application ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='团队应用关联表'; -- 团队配置表 -CREATE TABLE deploy_team_config +CREATE TABLE deploy_team_environment_config ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - create_by VARCHAR(100) NULL COMMENT '创建人', - create_time DATETIME(6) NULL COMMENT '创建时间', - update_by VARCHAR(100) NULL COMMENT '更新人', - update_time DATETIME(6) NULL COMMENT '更新时间', - version INT NOT NULL DEFAULT 1 COMMENT '版本号', - deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除', + id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', + create_by VARCHAR(100) NULL COMMENT '创建人', + create_time DATETIME(6) NULL COMMENT '创建时间', + update_by VARCHAR(100) NULL COMMENT '更新人', + update_time DATETIME(6) NULL COMMENT '更新时间', + version INT NOT NULL DEFAULT 1 COMMENT '版本号', + deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除', - team_id BIGINT NOT NULL COMMENT '团队ID', + team_id BIGINT NOT NULL COMMENT '团队ID', + environment_id BIGINT NOT NULL COMMENT '环境ID', - -- 环境权限配置 - allowed_environment_ids JSON NULL COMMENT '团队可访问的环境ID列表,如:[1, 2, 3]', + -- 审批配置 + approval_required BIT NOT NULL DEFAULT 0 COMMENT '是否需要审批', + approver_user_ids JSON NULL COMMENT '审批人用户ID列表,如:[1, 4, 7]', - -- 审批配置(与allowed_environment_ids位置对应) - environment_approval_required JSON NULL COMMENT '各环境是否需要审批(boolean数组),如:[false, false, true]', - approver_user_ids JSON NULL COMMENT '各环境的审批人列表(数组,无审批人用null),如:[null, null, [1, 4]]', + -- 通知配置 + notification_channel_id BIGINT NULL COMMENT '通知渠道ID', + notification_enabled BIT NOT NULL DEFAULT 1 COMMENT '是否启用部署通知', - UNIQUE INDEX uk_team (team_id), - INDEX idx_deleted (deleted), - CONSTRAINT fk_team_config_team FOREIGN KEY (team_id) REFERENCES deploy_team (id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='团队配置表'; + -- 安全策略 + require_code_review BIT NOT NULL DEFAULT 0 COMMENT '是否要求代码审查通过', + + -- 备注 + remark VARCHAR(500) NULL COMMENT '备注信息', + + UNIQUE INDEX uk_team_env (team_id, environment_id), + INDEX idx_team (team_id), + INDEX idx_env (environment_id), + INDEX idx_deleted (deleted), + CONSTRAINT fk_team_env_config_team FOREIGN KEY (team_id) REFERENCES deploy_team (id), + CONSTRAINT fk_team_env_config_env FOREIGN KEY (environment_id) REFERENCES deploy_environment (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='团队环境配置表'; -- -------------------------------------------------------------------------------------- -- 通知渠道表 diff --git a/frontend/src/pages/Deploy/Team/List/components/TeamApplicationDialog.tsx b/frontend/src/pages/Deploy/Team/List/components/TeamApplicationDialog.tsx new file mode 100644 index 00000000..7e6f22e5 --- /dev/null +++ b/frontend/src/pages/Deploy/Team/List/components/TeamApplicationDialog.tsx @@ -0,0 +1,527 @@ +import React, { useState, useEffect } from 'react'; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogFooter, + DialogBody, +} from '@/components/ui/dialog'; +import { Button } from '@/components/ui/button'; +import { Label } from '@/components/ui/label'; +import { Input } from '@/components/ui/input'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; +import { Search, Check, ChevronDown, Loader2 } from 'lucide-react'; +import { cn } from '@/lib/utils'; +import { ScrollArea } from '@/components/ui/scroll-area'; +import { useToast } from '@/components/ui/use-toast'; +import type { + TeamApplication, + Application, +} from '../types'; +import type { RepositoryBranchResponse } from '@/pages/Resource/Git/List/types'; +import type { WorkflowDefinition } from '@/pages/Workflow/Definition/List/types'; + +interface TeamApplicationDialogProps { + open: boolean; + mode: 'create' | 'edit'; + teamId: number; + environmentId: number; + environmentName: string; + application?: TeamApplication; // 编辑时传入 + applications: Application[]; // 可选择的应用列表 + jenkinsSystems: any[]; + workflowDefinitions: WorkflowDefinition[]; + onOpenChange: (open: boolean) => void; + onSave: (data: { + id?: number; + appId: number; + branch: string; + deploySystemId: number | null; + deployJob: string; + workflowDefinitionId: number | null; + }) => Promise; + onLoadBranches: (appId: number, app: Application) => Promise; + onLoadJenkinsJobs: (systemId: number) => Promise; +} + +const TeamApplicationDialog: React.FC = ({ + open, + mode, + teamId, + environmentId, + environmentName, + application, + applications, + jenkinsSystems, + workflowDefinitions, + onOpenChange, + onSave, + onLoadBranches, + onLoadJenkinsJobs, +}) => { + const { toast } = useToast(); + + // 表单状态 + const [formData, setFormData] = useState({ + appId: null as number | null, + branch: '', + deploySystemId: null as number | null, + deployJob: '', + workflowDefinitionId: null as number | null, + }); + + // 加载状态 + const [branches, setBranches] = useState([]); + const [loadingBranches, setLoadingBranches] = useState(false); + const [jenkinsJobs, setJenkinsJobs] = useState([]); + const [loadingJobs, setLoadingJobs] = useState(false); + const [saving, setSaving] = useState(false); + + // 搜索和弹窗状态 + const [branchSearchValue, setBranchSearchValue] = useState(''); + const [branchPopoverOpen, setBranchPopoverOpen] = useState(false); + const [jobSearchValue, setJobSearchValue] = useState(''); + const [jobPopoverOpen, setJobPopoverOpen] = useState(false); + + // 初始化表单数据 + useEffect(() => { + if (open) { + if (mode === 'edit' && application) { + // 编辑模式:填充现有数据 + setFormData({ + appId: application.applicationId, + branch: application.branch || '', + deploySystemId: application.deploySystemId || null, + deployJob: application.deployJob || '', + workflowDefinitionId: application.workflowDefinitionId || null, + }); + + // 加载分支和Jobs + const app = applications.find(a => a.id === application.applicationId); + if (app) { + loadBranches(application.applicationId, app); + } + if (application.deploySystemId) { + loadJenkinsJobs(application.deploySystemId); + } + } else { + // 新建模式:重置表单 + setFormData({ + appId: null, + branch: '', + deploySystemId: null, + deployJob: '', + workflowDefinitionId: null, + }); + setBranches([]); + setJenkinsJobs([]); + } + } + }, [open, mode, application]); + + // 加载分支列表 + const loadBranches = async (appId: number, app: Application) => { + setLoadingBranches(true); + try { + const branchList = await onLoadBranches(appId, app); + setBranches(branchList || []); + } catch (error) { + console.error('加载分支失败:', error); + setBranches([]); + } finally { + setLoadingBranches(false); + } + }; + + // 加载 Jenkins Jobs + const loadJenkinsJobs = async (systemId: number) => { + setLoadingJobs(true); + try { + const jobs = await onLoadJenkinsJobs(systemId); + setJenkinsJobs(jobs || []); + } catch (error) { + console.error('加载Jenkins Jobs失败:', error); + setJenkinsJobs([]); + } finally { + setLoadingJobs(false); + } + }; + + // 处理应用选择 + const handleAppChange = (appId: number) => { + const app = applications.find(a => a.id === appId); + setFormData({ + appId: appId, + branch: '', + deploySystemId: null, + deployJob: '', + workflowDefinitionId: null, + }); + + // 加载该应用的分支列表 + if (app) { + loadBranches(appId, app); + } + }; + + // 处理 Jenkins 系统选择 + const handleJenkinsSystemChange = (systemId: number) => { + setFormData({ + ...formData, + deploySystemId: systemId, + deployJob: '', + }); + loadJenkinsJobs(systemId); + }; + + // 保存 + const handleSave = async () => { + // 表单验证 + if (!formData.appId) { + toast({ + variant: 'destructive', + title: '请选择应用', + }); + return; + } + + setSaving(true); + try { + await onSave({ + id: mode === 'edit' && application ? application.id : undefined, + appId: formData.appId, + branch: formData.branch, + deploySystemId: formData.deploySystemId, + deployJob: formData.deployJob, + workflowDefinitionId: formData.workflowDefinitionId, + }); + + toast({ + title: mode === 'edit' ? '保存成功' : '添加成功', + }); + onOpenChange(false); + } catch (error: any) { + toast({ + variant: 'destructive', + title: mode === 'edit' ? '保存失败' : '添加失败', + description: error.response?.data?.message || error.message, + }); + } finally { + setSaving(false); + } + }; + + // 分支过滤 + const filteredBranches = branches.filter(branch => + branch.name.toLowerCase().includes(branchSearchValue.toLowerCase()) + ); + + // Job 过滤 + const filteredJobs = jenkinsJobs.filter(job => + job.jobName.toLowerCase().includes(jobSearchValue.toLowerCase()) + ); + + return ( +

+ + + + {mode === 'edit' ? '编辑' : '添加'}应用配置 - {environmentName} + + + + +
+ {/* 应用选择 */} +
+ + + {mode === 'edit' && ( +

+ 编辑时不可修改应用,如需更换请删除后重新添加 +

+ )} +
+ + {/* 分支选择 */} +
+ + {formData.appId ? ( + + + + + +
+ + setBranchSearchValue(e.target.value)} + /> +
+ +
+ {filteredBranches.length === 0 ? ( +
+ 未找到分支 +
+ ) : ( + filteredBranches.map((branch) => ( +
{ + setFormData({ ...formData, branch: branch.name }); + setBranchSearchValue(''); + setBranchPopoverOpen(false); + }} + > + + {branch.name} + {branch.isDefaultBranch && ( + + (默认) + + )} + + {branch.name === formData.branch && ( + + )} +
+ )) + )} +
+
+
+
+ ) : ( + + )} +
+ + {/* Jenkins 系统 */} +
+ + +
+ + {/* Jenkins Job */} +
+ + {formData.deploySystemId ? ( + + + + + +
+ + setJobSearchValue(e.target.value)} + /> +
+ +
+ {filteredJobs.length === 0 ? ( +
+ 未找到Job +
+ ) : ( + filteredJobs.map((job) => ( +
{ + setFormData({ + ...formData, + deployJob: job.jobName, + }); + setJobSearchValue(''); + setJobPopoverOpen(false); + }} + > + + {job.jobName} + + {job.jobName === formData.deployJob && ( + + )} +
+ )) + )} +
+
+
+
+ ) : ( + + )} +
+ + {/* 工作流定义 */} +
+ + +
+
+
+ + + + + +
+
+ ); +}; + +export { TeamApplicationDialog }; +export default TeamApplicationDialog; +