Coze源码分析-资源库-编辑工作流-后端源码-IDL/API/应用服务层
前言
本文将深入分析Coze Studio项目中用户编辑工作流功能的后端实现。当用户登录Coze平台后,点击"资源库" → 接着在表格中点击要编辑的工作流行,触发的是一个完整的工作流资源编辑流程。工作流作为AI应用开发的核心组件,其编辑操作涉及权限验证、数据更新和多个后端服务层的协同工作。通过源码解读,我们将深入理解工作流编辑功能在整个Coze Studio后端架构中的设计思路和技术实现细节。
项目架构概览
工作流编辑功能架构设计
Coze Studio的工作流编辑功能采用了经典的分层架构模式,专门针对工作流资源的安全编辑进行了优化设计。整个架构围绕工作流的权限验证、数据更新和版本管理展开:
┌─────────────────────────────────────────────────────────────┐
│ IDL接口定义层 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ base.thrift │ │workflow. │ │ api.thrift │ │
│ │ │ │thrift │ │ │ │
│ │ │ │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│ API网关层 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Handler │ │ Router │ │ Middleware │ │
│ │ 处理器 │ │ 路由 │ │ 中间件 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│ 应用服务层 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ApplicationService │ │
│ │ SaveWorkflow UpdateWorkflowMeta │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│ 领域服务层 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ WorkflowService │ │
│ │ ┌─────────────────┐ │ │
│ │ │Save │ │ │
│ │ │UpdateMeta │ │ │
│ │ └─────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│ 数据访问层 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ WorkflowRepository │ │
│ │ ┌──── ─────── ──── ──┐ ┌─────────────────────────┐│ │
│ │ │WorkflowDAO │ │workflow_draft.gen.go ││ │
│ │ │UpdateWorkflow │ │workflow_meta.gen.go ││ │
│ │ └──── ─────── ─── ───┘ └─────────────────────────┘│ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│ 基础设施层 │
│ ┌─ ─ ─── ─ ── ── ─ ─ ─┐ │
│ │ gorm.DB │ │
│ │ es.Client redisImpl │ │
│ └── ─ ── ── ─ ── ── ─ ┘ │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│ 存储服务层 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ MySQL数据库 Redis数据库 │ │
│ │ ElasticSearch数据库 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
工作流编辑流程核心组件
API路由映射:
POST /api/workflow_api/save
- 保存工作流画布内容POST /api/workflow_api/update_meta
- 更新工作流元数据POST /api/workflow_api/canvas
- 获取工作流画布信息
核心数据模型:
// 文件位置:backend/domain/workflow/entity/workflow.go
type WorkflowInfo struct {ID int64WorkflowID stringName stringDesc stringIconURI stringSpaceID int64CreatorID int64Canvas stringInputParams stringOutputParams stringTestRunSuccess boolModified boolStatus WorkFlowStatusFlowMode WorkflowModeUpdateTime int64CreateTime int64
}// 文件位置:backend/domain/workflow/internal/repo/dal/model/workflow_draft.gen.go
type WorkflowDraft struct {ID int64 `gorm:"column:id;primaryKey;comment:workflow ID" json:"id"`Canvas string `gorm:"column:canvas;not null;comment:Front end schema" json:"canvas"`InputParams string `gorm:"column:input_params;comment:Input schema" json:"input_params"`OutputParams string `gorm:"column:output_params;comment:Output parameter schema" json:"output_params"`TestRunSuccess bool `gorm:"column:test_run_success;not null;comment:0 not running, 1 running successfully" json:"test_run_success"`Modified bool `gorm:"column:modified;not null;comment:0 has not been modified, 1 has been modified" json:"modified"`UpdatedAt int64 `gorm:"column:updated_at;autoUpdateTime:milli;comment:Update Time in Milliseconds" json:"updated_at"`DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;comment:Delete Time" json:"deleted_at"`CommitID string `gorm:"column:commit_id;not null;comment:used to uniquely identify a draft snapshot" json:"commit_id"`
}
1. IDL接口定义层
IDL基础类型定义(base.thrift)
文件位置:idl/base.thrift
核心代码:
namespace py base
namespace go base
namespace java com.bytedance.thrift.basestruct TrafficEnv {1: bool Open = false,2: string Env = "" ,
}struct Base {1: string LogID = "",2: string Caller = "",3: string Addr = "",4: string Client = "",5: optional TrafficEnv TrafficEnv ,6: optional map<string,string> Extra ,
}struct BaseResp {1: string StatusMessage = "",2: i32 StatusCode = 0 ,3: optional map<string,string> Extra ,
}
文件作用:
定义了Coze Studio项目中所有接口的基础数据结构,作为其他IDL文件的依赖基础。
工作流接口定义(workflow.thrift)
文件位置:idl/workflow/workflow.thrift
当Coze用户登录平台后点击"资源库" → 接着在表格中点击要编辑的工作流行时,前端会调用工作流编辑相关的接口。该文件定义了工作流编辑的核心数据结构和接口。
工作流基本结构定义
enum WorkflowMode {Workflow = 0 , // 标准工作流Imageflow = 1 , // 图像工作流SceneFlow = 2 , // 场景工作流ChatFlow = 3 , // 对话工作流All = 100 , // 仅用于查询
}enum WorkFlowDevStatus {CanNotSubmit = 1, // 不可提交(流程未提交且最新的版本未test run成功)CanSubmit = 2, // 未提交且可提交(流程未提交但最新的版本已经test run成功)HadSubmit = 3, // 已提交Deleted = 4, // 删除
}struct Workflow {1 : string workflow_id , // 流程id,全局唯一2 : string name , // 流程名称3 : string desc , // 流程描述4 : string url , // 流程URL5 : string icon_uri , // 流程图标URL6 : WorkFlowDevStatus status , // 流程的提交状态7 : WorkFlowType type , // 类型,1:官方模版8 : string plugin_id , // workflow对应的插件id9 : i64 create_time , // 创建时间10: i64 update_time , // 更新时间11: SchemaType schema_type , // 模式类型17: optional string space_id , // 空间ID18: optional string interface_str , // 流程出入参19: optional string schema_json , // 新版workflow的定义schema20: Creator creator , // workflow创作者信息21: PersistenceModel persistence_model , // 存储模型22: WorkflowMode flow_mode , // workflow or imageflow,默认值为workflow
}
保存工作流接口
struct SaveWorkflowRequest {1: required string workflow_id (api.body="workflow_id", api.js_conv="true")2: required string schema (api.body="schema")3: optional string space_id (api.body="space_id", api.js_conv="true")255: optional base.Base Base
}struct SaveWorkflowResponse {1: optional SaveWorkflowData data253: required i64 code254: required string msg255: required base.BaseResp BaseResp
}struct SaveWorkflowData {
}
更新工作流元数据接口
struct UpdateWorkflowMetaRequest {1: required string workflow_id (api.body="workflow_id", api.js_conv="true")2: optional string name (api.body="name")3: optional string desc (api.body="desc")4: optional string icon_uri (api.body="icon_uri")5: optional string space_id (api.body="space_id", api.js_conv="true")6: optional WorkflowMode flow_mode (api.body="flow_mode")255: optional base.Base Base
}struct UpdateWorkflowMetaResponse {253: required i64 code254: required string msg255: required base.BaseResp BaseResp
}#### 获取画布信息接口
```thrift
struct GetCanvasInfoRequest {1: required string workflow_id (api.body="workflow_id", api.js_conv="true")2: optional string space_id (api.body="space_id", api.js_conv="true")255: optional base.Base Base
}struct GetCanvasInfoResponse {1: optional GetCanvasInfoData data253: required i64 code254: required string msg255: required base.BaseResp BaseResp
}struct GetCanvasInfoData {1: required string canvas2: required WorkFlowDevStatus status3: required VCSCanvasType vcs_canvas_type4: optional string input_params5: optional string output_params6: optional i64 last_test_run_time
}
工作流服务接口定义
service WorkflowService {// 保存工作流SaveWorkflowResponse SaveWorkflow(1:SaveWorkflowRequest request)(api.post='/api/workflow_api/save', api.category="workflow",agw.preserve_base="true")// 更新工作流元数据UpdateWorkflowMetaResponse UpdateWorkflowMeta(1:UpdateWorkflowMetaRequest request)(api.post='/api/workflow_api/update_meta', api.category="workflow",agw.preserve_base="true")// 获取画布信息GetCanvasInfoResponse GetCanvasInfo(1:GetCanvasInfoRequest request)(api.post='/api/workflow_api/canvas', api.category="workflow",agw.preserve_base="true")// 其他工作流相关接口...
}1 : i64 code ,2 : string msg ,3 : i32 edit_version,255: optional base.BaseResp BaseResp ,
}
工作流服务接口定义
service WorkflowService {// 保存工作流SaveWorkflowResponse SaveWorkflow(1:SaveWorkflowRequest request)(api.post='/api/workflow_api/save', api.category="workflow",agw.preserve_base="true")// 更新工作流元数据UpdateWorkflowMetaResponse UpdateWorkflowMeta(1:UpdateWorkflowMetaRequest request)(api.post='/api/workflow_api/update_meta', api.category="workflow",agw.preserve_base="true")// 获取画布信息GetCanvasInfoResponse GetCanvasInfo(1:GetCanvasInfoRequest request)(api.post='/api/workflow_api/canvas', api.category="workflow",agw.preserve_base="true")// 其他工作流相关接口...
}### IDL主API服务聚合文件(api.thrift)
文件位置:`idl/api.thrift`该文件是整个Coze项目的API服务聚合入口点,负责将所有业务模块的IDL服务定义统一聚合,为代码生成工具提供完整的服务接口定义。核心代码:
```thriftinclude "./workflow/workflow.thrift"namespace go coze// 工作流核心服务聚合
service WorkflowService extends workflow.WorkflowService {}
// 其他业务服务聚合
工作流接口聚合说明:
通过 service WorkflowService extends workflow.WorkflowService {}
聚合定义,api.thrift将workflow.thrift中定义的所有工作流相关接口统一暴露.
2. API网关层
接口定义-workflow.go文件详细分析
文件位置:backend\api\model\workflow\workflow.go
核心代码:
type WorkflowService interface {SaveWorkflow(ctx context.Context, request *SaveWorkflowRequest) (r *SaveWorkflowResponse, err error)UpdateWorkflowMeta(ctx context.Context, request *UpdateWorkflowMetaRequest) (r *UpdateWorkflowMetaResponse, err error)GetCanvasInfo(ctx context.Context, request *GetCanvasInfoRequest) (r *GetCanvasInfoResponse, err error)}
工作流相关接口模型定义
当Coze用户登录平台后点击"资源库" → 接着在表格中点击要编辑的工作流行时,前端会调用工作流编辑相关接口来编辑工作流资源。
SaveWorkflowRequest 保存工作流请求结构体:
type SaveWorkflowRequest struct {// 工作流IDWorkflowID string `thrift:"workflow_id,1,required" form:"workflow_id,required" json:"workflow_id,required" query:"workflow_id,required"`// 工作流定义schemaSchema string `thrift:"schema,2,required" form:"schema,required" json:"schema,required" query:"schema,required"`// 空间IDSpaceID string `thrift:"space_id,3" form:"space_id" json:"space_id" query:"space_id"`Base *base.Base `thrift:"Base,255" form:"Base" json:"Base" query:"Base"`
}
UpdateWorkflowMetaRequest 更新工作流元数据请求结构体:
type UpdateWorkflowMetaRequest struct {// 工作流IDWorkflowID string `thrift:"workflow_id,1,required" form:"workflow_id,required" json:"workflow_id,required" query:"workflow_id,required"`// 工作流名称Name string `thrift:"name,2" form:"name" json:"name" query:"name"`// 工作流描述Desc string `thrift:"desc,3" form:"desc" json:"desc" query:"desc"`// 工作流图标URLIconURI string `thrift:"icon_uri,4" form:"icon_uri" json:"icon_uri" query:"icon_uri"`// 空间IDSpaceID string `thrift:"space_id,5" form:"space_id" json:"space_id" query:"space_id"`// 工作流模式FlowMode WorkflowMode `thrift:"flow_mode,6" form:"flow_mode" json:"flow_mode" query:"flow_mode"`Base *base.Base `thrift:"Base,255" form:"Base" json:"Base" query:"Base"`
}
GetCanvasInfoRequest 获取画布信息请求结构体:
type GetCanvasInfoRequest struct {// 工作流IDWorkflowID string `thrift:"workflow_id,1,required" form:"workflow_id,required" json:"workflow_id,required" query:"workflow_id,required"`// 空间IDSpaceID string `thrift:"space_id,2" form:"space_id" json:"space_id" query:"space_id"`Base *base.Base `thrift:"Base,255" form:"Base" json:"Base" query:"Base"`
}
接口功能说明
业务功能:
- 工作流保存:将用户编辑的工作流定义保存到系统中
- 工作流元数据更新:根据用户输入更新工作流的名称、描述、图标等基本信息
- 画布信息获取:获取工作流的画布定义和状态信息
- 权限验证:确保只有有权限的用户才能编辑特定工作流
- 数据存储:将更新后的工作流信息持久化存储
- 版本管理:维护工作流的不同版本状态
技术特性:
- 类型安全:使用强类型定义确保工作流数据的一致性
- 多格式支持:支持thrift、form、json、query等多种序列化格式
- 参数验证:通过required标记确保必要参数的完整性
- 统一响应:遵循统一的响应格式规范
文件作用:
由thriftgo自动生成的Go代码文件,基于workflow.thrift IDL定义生成对应的Go结构体和接口,提供类型安全的工作流API模型。该文件实现了完整的Thrift RPC通信机制,包括客户端调用、服务端处理、序列化/反序列化等功能,确保了工作流服务间的可靠通信。
工作流接口处理器实现
文件位置:backend/api/handler/coze/workflow_service.go
该文件包含了用户登录后点击"资源库" → 接着在表格中点击要编辑的工作流行功能的核心API接口处理器,主要负责处理工作流资源的编辑功能。
保存工作流处理器
// SaveWorkflow .
// @router /api/workflow_api/save [POST]
func SaveWorkflow(ctx context.Context, c *app.RequestContext) {var err errorvar req workflow.SaveWorkflowRequesterr = c.BindAndValidate(&req)if err != nil {invalidParamRequestResponse(c, err.Error())return}if req.WorkflowID == "" {invalidParamRequestResponse(c, "workflowID is invalid")return}resp, err := workflow.WorkflowApplicationSVC.SaveWorkflow(ctx, &req)if err != nil {internalServerErrorResponse(ctx, c, err)return}c.JSON(consts.StatusOK, resp)
}
更新工作流元数据处理器
// UpdateWorkflowMeta .
// @router /api/workflow_api/update_meta [POST]
func UpdateWorkflowMeta(ctx context.Context, c *app.RequestContext) {var err errorvar req workflow.UpdateWorkflowMetaRequesterr = c.BindAndValidate(&req)if err != nil {invalidParamRequestResponse(c, err.Error())return}if req.WorkflowID == "" {invalidParamRequestResponse(c, "workflowID is invalid")return}resp, err := workflow.WorkflowApplicationSVC.UpdateWorkflowMeta(ctx, &req)if err != nil {internalServerErrorResponse(ctx, c, err)return}c.JSON(consts.StatusOK, resp)
}
获取画布信息处理器
// GetCanvasInfo .
// @router /api/workflow_api/canvas [GET]
func GetCanvasInfo(ctx context.Context, c *app.RequestContext) {var err errorvar req workflow.GetCanvasInfoRequesterr = c.BindAndValidate(&req)if err != nil {invalidParamRequestResponse(c, err.Error())return}if req.WorkflowID == "" {invalidParamRequestResponse(c, "workflowID is invalid")return}resp, err := workflow.WorkflowApplicationSVC.GetCanvasInfo(ctx, &req)if err != nil {internalServerErrorResponse(ctx, c, err)return}c.JSON(consts.StatusOK, resp)
}
实现功能:
- 参数验证:验证工作流ID等参数的有效性
- 业务调用:调用WorkflowApplicationSVC应用服务层的相应方法处理工作流编辑业务逻辑
- 错误处理:统一的错误处理机制,包括参数错误和内部服务错误
- 响应返回:返回标准化的操作响应结果
参数校验逻辑:
- 工作流ID验证:确保工作流ID不为空,防止无效的编辑操作
路由注册实现-api.go文件详细分析
文件位置:backend/api/router/coze/api.go
该文件是Coze Studio后端的核心路由注册文件,由hertz generator自动生成,负责将所有HTTP API接口路由与对应的处理函数进行绑定和注册。对于工作流编辑功能,构建了专门的路由结构:
/api/workflow_api/save [POST]
├── rootMw() # 根级中间件
├── _apiMw() # API组中间件
├── _workflow_apiMw() # 工作流API组中间件
├── _saveMw() # 保存工作流接口专用中间件
└── coze.SaveWorkflow # 保存工作流处理函数/api/workflow_api/update_meta [POST]
├── rootMw() # 根级中间件
├── _apiMw() # API组中间件
├── _workflow_apiMw() # 工作流API组中间件
├── _updatemetaMw() # 更新工作流元数据接口专用中间件
└── coze.UpdateWorkflowMeta # 更新工作流元数据处理函数/api/workflow_api/canvas [GET]
├── rootMw() # 根级中间件
├── _apiMw() # API组中间件
├── _workflow_apiMw() # 工作流API组中间件
├── _canvasMw() # 获取画布信息接口专用中间件
└── coze.GetCanvasInfo # 获取画布信息处理函数
核心代码:
{_workflow_api := _api.Group("/workflow_api", _workflow_apiMw()...)_workflow_api.POST("/save", append(_saveMw(), coze.SaveWorkflow)...)_workflow_api.POST("/update_meta", append(_updatemetaMw(), coze.UpdateWorkflowMeta)...)_workflow_api.GET("/canvas", append(_canvasMw(), coze.GetCanvasInfo)...)// 其他工作流相关路由...
}
3. 应用服务层
WorkflowApplicationService 应用服务层概述
WorkflowApplicationService是工作流编辑功能的核心应用服务层组件,负责协调各个领域服务和数据访问层,处理工作流编辑的业务逻辑。该服务实现了WorkflowService接口,为API网关层提供具体的业务处理能力。
保存工作流实现
保存工作流是用户编辑工作流画布内容的核心操作,负责将前端提交的工作流定义schema保存到后端系统。
func (w *WorkflowApplicationService) SaveWorkflow(ctx context.Context, req *workflow.SaveWorkflowRequest) (resp *workflow.SaveWorkflowResponse, err error) {// 初始化响应结构体resp = &workflow.SaveWorkflowResponse{}// 验证用户权限和授权信息authInfo, err := w.getSaveAuthInfo(ctx, req)if err != nil {// 处理权限错误return nil, err}// 构建保存请求saveReq := &domain.SaveWorkflowRequest{WorkflowID: req.WorkflowID,Schema: req.Schema,SpaceID: authInfo.SpaceID,UserID: authInfo.UserID,EditVersion: authInfo.EditVersion,}// 调用领域服务保存工作流err = w.domainSVC.SaveWorkflow(ctx, saveReq)if err != nil {// 处理保存错误return nil, err}// 发布工作流变更事件w.publishWorkflowEvent(ctx, req.WorkflowID, common.WorkflowEventType_Save)// 设置响应信息resp.Code = 0resp.Msg = ""resp.Data = &workflow.SaveWorkflowData{}return resp, nil
}
更新工作流元数据实现
更新工作流元数据是工作流编辑的重要操作,负责根据用户提交的信息更新工作流的基本属性。
func (w *WorkflowApplicationService) UpdateWorkflowMeta(ctx context.Context, req *workflow.UpdateWorkflowMetaRequest) (resp *workflow.UpdateWorkflowMetaResponse, err error) {// 初始化响应结构体resp = &workflow.UpdateWorkflowMetaResponse{}// 验证用户权限和授权信息authInfo, err := w.getUpdateMetaAuthInfo(ctx, req)if err != nil {// 处理权限错误return nil, err}// 构建更新请求updateReq := &domain.UpdateWorkflowMetaRequest{WorkflowID: req.WorkflowID,Name: req.Name,Description: req.Desc,IconURI: req.IconURI,FlowMode: req.FlowMode,SpaceID: authInfo.SpaceID,UserID: authInfo.UserID,EditVersion: authInfo.EditVersion,}// 调用领域服务更新工作流元数据err = w.domainSVC.UpdateWorkflowMeta(ctx, updateReq)if err != nil {// 处理更新错误return nil, err}// 发布工作流变更事件w.publishWorkflowEvent(ctx, req.WorkflowID, common.WorkflowEventType_UpdateMeta)// 设置响应信息resp.Code = 0resp.Msg = ""resp.EditVersion = authInfo.EditVersionreturn resp, nil
}
解锁工作流编辑实现
解锁工作流编辑是用户完成编辑后释放编辑锁的操作,允许其他用户对工作流进行编辑。
func (w *WorkflowApplicationService) UnlockWorkflowEdit(ctx context.Context, req *workflowAPI.UnlockWorkflowEditRequest) (resp *workflowAPI.UnlockWorkflowEditResponse, err error) {// 初始化响应结构体并设置释放状态为已释放resp = &workflowAPI.UnlockWorkflowEditResponse{Code: 0,Msg: "",Released: true,}return resp, nil
}
应用服务层工作流程
工作流编辑功能的完整工作流程如下:
-
编辑准备阶段:
- 用户点击要编辑的工作流
- 前端调用
CheckAndLockWorkflowEdit
接口 - 应用服务层验证权限并获取编辑锁
- 返回工作流当前信息给前端进行展示
-
编辑进行阶段:
- 用户在前端界面修改工作流信息(元数据、画布内容等)
- 完成修改后点击保存
- 前端调用
SaveWorkflow
或UpdateWorkflowMeta
接口 - 应用服务层验证权限、检查锁定状态
- 调用领域服务更新工作流数据
- 返回更新结果
-
编辑完成阶段:
- 用户完成所有编辑操作
- 前端调用
UnlockWorkflowEdit
接口 - 应用服务层释放编辑锁
- 工作流可以被其他用户编辑
核心业务逻辑与协作
应用服务层在工作流编辑过程中主要负责以下核心业务逻辑:
- 权限验证:确保只有有权限的用户才能编辑特定工作流
- 并发控制:通过编辑锁定机制防止并发编辑冲突
- 数据验证:验证用户提交的数据符合业务规则和约束
- 事务管理:确保工作流数据更新的原子性和一致性
- 版本管理:维护工作流的编辑版本号,用于冲突检测
- 领域协调:协调调用各个领域服务完成完整的编辑流程
应用服务层作为API网关层和领域服务层之间的桥梁,将复杂的业务流程分解为可管理的步骤,确保工作流编辑功能的正确性和可靠性。