前言
本文将深入分析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
- 获取工作流画布信息
核心数据模型:
go
// 文件位置:backend/domain/workflow/entity/workflow.go
type WorkflowInfo struct {
ID int64
WorkflowID string
Name string
Desc string
IconURI string
SpaceID int64
CreatorID int64
Canvas string
InputParams string
OutputParams string
TestRunSuccess bool
Modified bool
Status WorkFlowStatus
FlowMode WorkflowMode
UpdateTime int64
CreateTime 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
核心代码:
thrift
namespace py base
namespace go base
namespace java com.bytedance.thrift.base
struct 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用户登录平台后点击"资源库" → 接着在表格中点击要编辑的工作流行时,前端会调用工作流编辑相关的接口。该文件定义了工作流编辑的核心数据结构和接口。
工作流基本结构定义
thrift
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 , // 流程URL
5 : string icon_uri , // 流程图标URL
6 : WorkFlowDevStatus status , // 流程的提交状态
7 : WorkFlowType type , // 类型,1:官方模版
8 : string plugin_id , // workflow对应的插件id
9 : i64 create_time , // 创建时间
10: i64 update_time , // 更新时间
11: SchemaType schema_type , // 模式类型
17: optional string space_id , // 空间ID
18: optional string interface_str , // 流程出入参
19: optional string schema_json , // 新版workflow的定义schema
20: Creator creator , // workflow创作者信息
21: PersistenceModel persistence_model , // 存储模型
22: WorkflowMode flow_mode , // workflow or imageflow,默认值为workflow
}
保存工作流接口
thrift
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 data
253: required i64 code
254: required string msg
255: required base.BaseResp BaseResp
}
struct SaveWorkflowData {
}
更新工作流元数据接口
thrift
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 code
254: required string msg
255: 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 data
253: required i64 code
254: required string msg
255: required base.BaseResp BaseResp
}
struct GetCanvasInfoData {
1: required string canvas
2: required WorkFlowDevStatus status
3: required VCSCanvasType vcs_canvas_type
4: optional string input_params
5: optional string output_params
6: optional i64 last_test_run_time
}
工作流服务接口定义
thrift
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 ,
}
工作流服务接口定义
thrift
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服务定义统一聚合,为代码生成工具提供完整的服务接口定义。
核心代码:
```thrift
include "./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
核心代码:
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 保存工作流请求结构体:
go
type SaveWorkflowRequest struct {
// 工作流ID
WorkflowID string `thrift:"workflow_id,1,required" form:"workflow_id,required" json:"workflow_id,required" query:"workflow_id,required"`
// 工作流定义schema
Schema string `thrift:"schema,2,required" form:"schema,required" json:"schema,required" query:"schema,required"`
// 空间ID
SpaceID 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 更新工作流元数据请求结构体:
go
type UpdateWorkflowMetaRequest struct {
// 工作流ID
WorkflowID 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"`
// 工作流图标URL
IconURI string `thrift:"icon_uri,4" form:"icon_uri" json:"icon_uri" query:"icon_uri"`
// 空间ID
SpaceID 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 获取画布信息请求结构体:
go
type GetCanvasInfoRequest struct {
// 工作流ID
WorkflowID string `thrift:"workflow_id,1,required" form:"workflow_id,required" json:"workflow_id,required" query:"workflow_id,required"`
// 空间ID
SpaceID 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接口处理器,主要负责处理工作流资源的编辑功能。
保存工作流处理器
go
// SaveWorkflow .
// @router /api/workflow_api/save [POST]
func SaveWorkflow(ctx context.Context, c *app.RequestContext) {
var err error
var req workflow.SaveWorkflowRequest
err = 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)
}
更新工作流元数据处理器
go
// UpdateWorkflowMeta .
// @router /api/workflow_api/update_meta [POST]
func UpdateWorkflowMeta(ctx context.Context, c *app.RequestContext) {
var err error
var req workflow.UpdateWorkflowMetaRequest
err = 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)
}
获取画布信息处理器
go
// GetCanvasInfo .
// @router /api/workflow_api/canvas [GET]
func GetCanvasInfo(ctx context.Context, c *app.RequestContext) {
var err error
var req workflow.GetCanvasInfoRequest
err = 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 # 获取画布信息处理函数
核心代码:
go
{
_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保存到后端系统。
go
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 := &vo.SaveReq{
Canvas: req.Schema,
InputParams: req.InputParams,
OutputParams: req.OutputParams,
CommitID: req.CommitID,
DraftMeta: &vo.DraftMeta{
Modified: req.Modified,
TestRunSuccess: req.TestRunSuccess,
},
}
// 调用领域服务保存工作流
err = w.domainSVC.Save(ctx, req.WorkflowID, saveReq)
if err != nil {
// 处理保存错误
return nil, err
}
// 发布工作流变更事件
w.publishWorkflowEvent(ctx, req.WorkflowID, common.WorkflowEventType_Save)
// 设置响应信息
resp.Code = 0
resp.Msg = ""
resp.Data = &workflow.SaveWorkflowData{}
return resp, nil
}
更新工作流元数据实现
更新工作流元数据是工作流编辑的重要操作,负责根据用户提交的信息更新工作流的基本属性。
go
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 := &vo.MetaUpdate{
Name: req.Name,
Description: req.Description,
IconURI: req.IconURI,
WorkflowMode: req.WorkflowMode,
}
// 调用领域服务更新工作流元数据
err = w.domainSVC.UpdateMeta(ctx, req.WorkflowID, updateReq)
if err != nil {
// 处理更新错误
return nil, err
}
// 发布工作流变更事件
w.publishWorkflowEvent(ctx, req.WorkflowID, common.WorkflowEventType_UpdateMeta)
// 设置响应信息
resp.Code = 0
resp.Msg = ""
return resp, nil
}
应用服务层工作流程
工作流编辑功能的完整工作流程如下:
-
编辑准备阶段:
- 用户点击要编辑的工作流
- 前端获取工作流信息和权限验证
- 返回工作流当前信息给前端进行展示
-
编辑进行阶段:
- 用户在前端界面修改工作流信息(元数据、画布内容等)
- 完成修改后点击保存
- 前端调用
SaveWorkflow
或UpdateWorkflowMeta
接口 - 应用服务层验证权限和数据
- 调用领域服务更新工作流数据
- 返回更新结果
-
发布阶段:
- 用户完成编辑并准备发布
- 前端调用发布接口
- 应用服务层调用领域服务的发布功能
- 领域服务创建新版本并更新元数据
- 返回发布结果
核心业务逻辑与协作
应用服务层在工作流编辑过程中主要负责以下核心业务逻辑:
- 权限验证:确保只有有权限的用户才能编辑特定工作流
- 数据验证:验证用户提交的数据符合业务规则和约束
- 事务协调:确保工作流数据更新的原子性和一致性
- 版本管理:通过调用领域服务实现工作流的版本控制
- 事件发布:在工作流变更后发布相应的事件通知
- 领域协调:协调调用各个领域服务完成完整的编辑流程
应用服务层作为API网关层和领域服务层之间的桥梁,将复杂的业务流程分解为可管理的步骤,确保工作流编辑功能的正确性和可靠性。