当前位置: 首页 > ai >正文

Coze源码分析-工作空间-项目开发-后端源码

前言

本文将深入分析Coze Studio项目中用户登录后点击"项目开发"功能的后端实现,通过源码解读来理解整个智能体项目管理系统的架构设计和技术实现。

项目架构概览

整体架构设计

Coze Studio后端采用了经典的分层架构模式,将项目开发功能划分为以下几个核心层次:

┌─────────────────────────────────────────────────────────────┐
│                    IDL接口定义层                             │
│  ┌─────────────┐  ┌───────────────── ┐    ┌─────────────┐    │
│  │ base.thrift │  │openapiauth.thrift│    │ api.thrift  │    │
│  └─────────────┘  └───────────────── ┘    └─────────────┘    │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│                    API网关层                                 │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐          │
│  │   Handler   │  │   Router    │  │ Middleware  │          │
│  │   处理器     │  │   路由      │  │   中间件     │          │
│  └─────────────┘  └─────────────┘  └─────────────┘          │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│                   应用服务层                                 │
│  ┌─────────────────────────────────────────────────────┐    │
│  │            APPApplicationService                    │    │
│  │            GetDraftIntelligenceList                 │    │
│  │                                                     │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│                   领域服务层                                 │
│  ┌─────────────────────────────────────────────────────┐    │
│  │        APP  Service      Search Service             │    │
│  │               SingleAgent  Service                  │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│                   数据访问层                                 │
│              ┌─ ─ ─── ─── ── ─ ─ ─┐                         │
│              │ APPDraftDAO        │                         │
│              │ SingleAgentDraftDAO│                         │
│              └── ─ ── ─── ── ── ─ ┘                         │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│                   基础设施层                                 │
│              ┌─ ─ ─── ─── ── ─ ─ ─┐                         │
│              │     gorm.DB        │                         │
│              │    es.Client       │                         │
│              └── ─ ── ─── ── ── ─ ┘                         │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│                   存储服务层                                 │
│  ┌─────────────────────────────────────────────────────┐    │
│  │                MySQL数据库                           │    │
│  │                ElasticSearch数据库                   │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘

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             ,
}

文件作用:
定义了项目中所有接口的基础数据结构,作为其他IDL文件的依赖基础。

项目开发查询接口定义(intelligence.thrift)

文件位置:idl/app/intelligence.thrift

GetDraftIntelligenceList接口定义
search.GetDraftIntelligenceListResponse GetDraftIntelligenceList(1: search.GetDraftIntelligenceListRequest req) 
(api.post='/api/intelligence_api/search/get_draft_intelligence_list', api.category="search",agw.preserve_base="true")

项目开发查询结构体定义(search.thrift)

文件位置:idl/app/search.thrift

请求结构体:

struct GetDraftIntelligenceListRequest {1: required i64 space_id (agw.js_conv="str", api.js_conv="true"),2: optional string name,3: optional bool has_published,4: optional list<intelligence_common_struct.IntelligenceStatus> status,5: optional list<intelligence_common_struct.IntelligenceType> types,6: optional SearchScope search_scope,51: optional bool is_fav,52: optional bool recently_open,99: optional GetDraftIntelligenceListOption option,100: optional OrderBy order_by,101: optional string cursor_id,102: optional i32 size,255: optional base.Base Base
}

响应结构体:

struct IntelligenceData {1: intelligence_common_struct.IntelligenceBasicInfo        basic_info,2: intelligence_common_struct.IntelligenceType             type,3: IntelligencePublishInfo      publish_info,4: IntelligencePermissionInfo   permission_info,5: common_struct.User           owner_info,6: common_struct.AuditInfo      latest_audit_info,7: FavoriteInfo                 favorite_info,50: OtherInfo                   other_info,
}struct DraftIntelligenceListData {1: list<IntelligenceData> intelligences,2: i32 total,3: bool has_more,4: string next_cursor_id,
}struct GetDraftIntelligenceListResponse {1: DraftIntelligenceListData data,253: i32 code,254: string msg,255: optional base.BaseResp BaseResp (api.none="true"),
}

项目开发查询公共结构体定义(common_struct.thrift)

文件位置:idl/app/common_struct/common_struct.thrift

公共结构体:

namespace go app.intelligence.commonstruct UserLabel {1: string             label_id ,2: string             label_name ,3: string             icon_uri ,4: string             icon_url  ,5: string             jump_link ,
}struct User {1: i64 user_id (agw.js_conv="str", api.js_conv="true"),2: string nickname, // user nickname3: string avatar_url, // user avatar4: string user_unique_name, // user name5: UserLabel user_label, // user tag
}struct IntelligencePublishInfo {1: string                      publish_time,2: bool                        has_published,3: list<ConnectorInfo>         connectors,
}

文件位置:idl/app/common_struct/intelligence_common_struct.thrift

公共结构体:

namespace go app.intelligence.commonenum IntelligenceStatus {Using = 1,Deleted = 2,Banned = 3,MoveFailed = 4, // Migration failedCopying    = 5, // CopyingCopyFailed = 6, // Copy failed
}enum IntelligenceType {Bot = 1Project = 2
}struct IntelligenceBasicInfo {1: i64                          id (agw.js_conv="str", api.js_conv="true"),2: string                       name,3: string                       description,4: string                       icon_uri,5: string                       icon_url,6: i64                          space_id (agw.js_conv="str", api.js_conv="true"),7: i64                          owner_id (agw.js_conv="str", api.js_conv="true"),8: i64                          create_time (agw.js_conv="str", api.js_conv="true"),9: i64                          update_time (agw.js_conv="str", api.js_conv="true"),10: IntelligenceStatus          status,11: i64                         publish_time (agw.js_conv="str", api.js_conv="true"),12: optional string             enterprise_id,13: optional i64                organization_id,
}

IDL主API服务聚合文件(api.thrift)

文件位置:idl/api.thrift

该文件是整个Coze项目的API服务聚合入口点,负责将所有业务模块的IDL服务定义统一聚合,为代码生成工具提供完整的服务接口定义。

核心代码:


include "./app/intelligence.thrift"namespace go coze// 项目开发核心服务聚合
service IntelligenceService extends intelligence.IntelligenceService {}
// 其他业务服务聚合

项目开发接口聚合说明:
通过 service IntelligenceService extends intelligence.IntelligenceService {} 聚合定义,api.thrift将intelligence.thrift中定义的所有项目开发相关接口统一暴露,包括:

文件作用:

  1. 服务聚合中心: 统一管理所有业务模块的服务接口定义
  2. 代码生成入口: 作为Hertz框架代码生成的主要入口文件
  3. 接口统一暴露: 将分散在各个模块的接口统一暴露给客户端
  4. 依赖管理: 通过include语句管理各模块间的依赖关系
  5. 命名空间管理: 统一设置Go语言的包命名空间

技术特性:

  • 使用Apache Thrift作为IDL(接口定义语言)
  • 支持服务继承和扩展机制
  • 模块化的服务组织结构
  • 统一的命名空间管理
  • 自动代码生成支持
  • 跨语言兼容性
  • 强类型约束和接口安全性

2. API网关层

接口定义-intelligence.go文件详细分析

文件位置:backend\api\model\app\intelligence\intelligence.go

IntelligenceService接口定义
// IntelligenceService 智能体服务接口
type IntelligenceService interface {// GetDraftIntelligenceInfo 获取草稿智能体信息// 用于获取指定智能体项目的详细信息,支持版本预览GetDraftIntelligenceList(ctx context.Context, req *GetDraftIntelligenceListRequest) (r *GetDraftIntelligenceListResponse, err error)// 其他接口方法...}
请求响应结构体定义

文件位置:backend\api\model\app\intelligence\search.go

GetDraftIntelligenceListRequest 请求结构体

type GetDraftIntelligenceListRequest struct {SpaceID      int64                           `thrift:"space_id,1,required" form:"space_id,required" json:"space_id,string,required" query:"space_id,required"`Name         *string                         `thrift:"name,2,optional" form:"name" json:"name,omitempty" query:"name"`HasPublished *bool                           `thrift:"has_published,3,optional" form:"has_published" json:"has_published,omitempty" query:"has_published"`Status       []common.IntelligenceStatus     `thrift:"status,4,optional" form:"status" json:"status,omitempty" query:"status"`Types        []common.IntelligenceType       `thrift:"types,5,optional" form:"types" json:"types,omitempty" query:"types"`SearchScope  *SearchScope                    `thrift:"search_scope,6,optional" form:"search_scope" json:"search_scope,omitempty" query:"search_scope"`IsFav        *bool                           `thrift:"is_fav,51,optional" form:"is_fav" json:"is_fav,omitempty" query:"is_fav"`RecentlyOpen *bool                           `thrift:"recently_open,52,optional" form:"recently_open" json:"recently_open,omitempty" query:"recently_open"`Option       *GetDraftIntelligenceListOption `thrift:"option,99,optional" form:"option" json:"option,omitempty" query:"option"`OrderBy      *OrderBy                        `thrift:"order_by,100,optional" form:"order_by" json:"order_by,omitempty" query:"order_by"`CursorID     *string                         `thrift:"cursor_id,101,optional" form:"cursor_id" json:"cursor_id,omitempty" query:"cursor_id"`Size         *int32                          `thrift:"size,102,optional" form:"size" json:"size,omitempty" query:"size"`Base         *base.Base                      `thrift:"Base,255,optional" form:"Base" json:"Base,omitempty" query:"Base"`
}

GetDraftIntelligenceInfoResponse 响应结构体

type GetDraftIntelligenceListResponse struct {Data     *DraftIntelligenceListData `thrift:"data,1" form:"data" json:"data" query:"data"`Code     int32                      `thrift:"code,253" form:"code" json:"code" query:"code"`Msg      string                     `thrift:"msg,254" form:"msg" json:"msg" query:"msg"`BaseResp *base.BaseResp             `thrift:"BaseResp,255,optional" form:"-" json:"-" query:"-"`
}
接口功能说明

业务功能

  • 智能体详情获取:根据智能体ID获取完整的项目信息
  • 版本预览支持:通过可选的version参数支持特定版本的预览
  • 多维度信息:返回基本信息、发布状态、所有者信息等完整数据
  • 权限控制:基于用户身份和项目权限进行访问控制

技术特性

  • 类型安全:使用强类型定义确保数据一致性
  • 多格式支持:支持thrift、form、json、query等多种序列化格式
  • 可选字段:使用optional标记支持向后兼容
  • 统一响应:遵循统一的响应格式规范

文件作用
由thriftgo自动生成的Go代码文件,基于IDL定义生成对应的Go结构体和接口,提供类型安全的API模型。该文件实现了完整的Thrift RPC通信机制,包括客户端调用、服务端处理、序列化/反序列化等功能,确保了分布式服务间的可靠通信。

项目开发接口处理器实现

文件位置:backend/api/handler/coze/intelligence_service.go

该文件包含了用户登录后点击项目开发功能的所有核心API接口处理器,主要负责处理草稿项目的CRUD操作、项目发布、项目复制等功能。

核心代码:

// GetDraftIntelligenceList 获取草稿智能体列表
// 用户登录后进入项目开发页面时调用此接口获取项目列表
// @router /api/intelligence/draft/list [GET]
func GetDraftIntelligenceList(ctx context.Context, c *app.RequestContext) {var err errorvar req intelligence.GetDraftIntelligenceListRequesterr = c.BindAndValidate(&req)if err != nil {invalidParamRequestResponse(c, err.Error())return}// 调用搜索服务获取用户的草稿项目列表resp, err := search.SearchSVC.GetDraftIntelligenceList(ctx, &req)if err != nil {logs.CtxErrorf(ctx, "SearchSVC.GetDraftIntelligenceList failed, err=%v", err)internalServerErrorResponse(ctx, c, err)return}c.JSON(consts.StatusOK, resp)
}

实现功能

  1. 项目列表获取:获取用户的草稿智能体列表,支持分页和搜索

路由注册实现-api.go文件详细分析

文件位置:backend/api/router/coze/api.go
核心代码:

// Code generated by hertz generator. DO NOT EDIT.func Register(r *server.Hertz) {root := r.Group("/", rootMw()...){_api := root.Group("/api", _apiMw()...){_intelligence_api := _api.Group("/intelligence_api", _intelligence_apiMw()...){_search := _intelligence_api.Group("/search", _searchMw()...)_search.POST("/get_draft_intelligence_list", append(_getdraftintelligencelistMw(), coze.GetDraftIntelligenceList)...)}}}
}

文件作用:
此文件是Coze Studio后端的核心路由注册文件,由hertz generator自动生成,负责将所有HTTP API接口路由与对应的处理函数进行绑定和注册。该文件构建了完整的RESTful API路由树结构。对于智能体项目开发模块,构建了层次化的路由结构:

/api/intelligence_api/search/get_draft_intelligence_list [POST]
├── _intelligence_apiMw() # 智能体API组中间件
├── _searchMw() # 搜索模块中间件
├── _getdraftintelligencelistMw() # 草稿智能体列表接口中间件
└── coze.GetDraftIntelligenceList # 处理函数

中间件系统-middleware.go文件详细分析

文件位置:backend/api/router/coze/middleware.go
核心代码:

func _intelligence_apiMw() []app.HandlerFunc {// 智能体API模块中间件return nil
}func _searchMw() []app.HandlerFunc {// 搜索模块中间件return nil
}func _getdraftintelligencelistMw() []app.HandlerFunc {// 草稿智能体列表查询接口专用中间件return nil
}

文件作用:

  1. 中间件函数定义:为智能体项目开发模块的每个路由组和特定路由提供中间件挂载点
  2. 路由层级管理:按照路由的层级结构组织中间件函数,支持三层中间件架构
  3. 开发者扩展接口:提供统一的接口供开发者添加自定义中间件逻辑,如认证、鉴权、限流、日志记录等
  4. 粒度化控制:支持从模块级别到接口级别的细粒度中间件控制

API网关层Restful接口路由-Coze+Hertz

Hertz为每个HTTP方法维护独立的路由树,通过分组路由的方式构建层次化的API结构。对于草稿智能体列表查询接口的完整路由链路:

/api/intelligence_api/search/get_draft_intelligence_list [POST]
├── rootMw() # 根级中间件
├── _apiMw() # API组中间件
├── _intelligence_apiMw() # 智能体API组中间件
├── _searchMw() # 搜索模块中间件
├── _getdraftintelligencelistMw() # 接口级中间件
└── coze.GetDraftIntelligenceList # 处理函数

这种设计的优势:

  • 层次化管理:不同层级的中间件处理不同的关注点
  • 可扩展性:每个层级都可以独立添加中间件
  • 性能优化:中间件按需执行,避免不必要的开销
  • POST请求支持:专门处理POST请求的JSON数据绑定和验证
  • 智能体项目管理:专门为智能体项目开发功能设计的路由结构

3. 应用服务层

APPApplicationService初始化

文件位置:backend/application/app/app.go

应用服务层是项目开发功能的核心协调层,负责协调各个领域服务完成复杂的业务操作。

核心代码:

// APPApplicationService 应用服务,处理项目开发的核心业务逻辑
type APPApplicationService struct {projectEventBus search.ProjectEventBus  // 项目事件总线appDomainSVC    service.AppService      // APP领域服务userSVC         user.User               // 用户服务storage         storage.Storage         // 存储服务modelMgr        modelmgr.Manager        // 模型管理器
}// 全局应用服务实例
var APPApplicationSVC = &APPApplicationService{}// 服务组件依赖
type ServiceComponents struct {ProjectEventBus search.ProjectEventBusAPPDomainSVC    service.AppServiceUserSVC         user.UserStorage         storage.StorageModelMgr        modelmgr.Manager
}// InitService 初始化应用服务,注入所有依赖
func InitService(components *ServiceComponents) *APPApplicationService {APPApplicationSVC.projectEventBus = components.ProjectEventBusAPPApplicationSVC.appDomainSVC = components.APPDomainSVCAPPApplicationSVC.userSVC = components.UserSVCAPPApplicationSVC.storage = components.StorageAPPApplicationSVC.modelMgr = components.ModelMgrreturn APPApplicationSVC
}

服务初始化特点

  1. 依赖注入:通过ServiceComponents结构体注入所有依赖服务,实现松耦合设计
  2. 事件总线:集成ProjectEventBus用于项目事件的发布和订阅,支持异步处理
  3. 领域服务:注入AppService处理核心业务逻辑,包括项目CRUD和发布管理
  4. 用户服务:集成用户管理服务进行权限控制和用户信息获取
  5. 存储服务:集成文件存储服务处理项目资源,如图标、配置文件等
  6. 模型管理:集成AI模型管理服务,支持智能体项目的模型配置

应用服务核心实现

文件位置:backend\application\search\project_search.go

应用服务层实现了项目开发的核心业务逻辑,包括草稿项目的创建、获取、更新、删除等操作。

核心代码:

func (s *SearchApplicationService) GetDraftIntelligenceList(ctx context.Context, req *intelligence.GetDraftIntelligenceListRequest) (resp *intelligence.GetDraftIntelligenceListResponse, err error,
) {userID := ctxutil.GetUIDFromCtx(ctx)if userID == nil {return nil, errorx.New(errno.ErrSearchPermissionCode, errorx.KV("msg", "session is required"))}do := searchRequestTo2Do(*userID, req)searchResp, err := s.DomainSVC.SearchProjects(ctx, do)if err != nil {return nil, err}if len(searchResp.Data) == 0 {return &intelligence.GetDraftIntelligenceListResponse{Data: &intelligence.DraftIntelligenceListData{Intelligences: make([]*intelligence.IntelligenceData, 0),Total:         0,HasMore:       false,NextCursorID:  "",},}, nil}tasks := taskgroup.NewUninterruptibleTaskGroup(ctx, len(searchResp.Data))lock := sync.Mutex{}intelligenceDataList := make([]*intelligence.IntelligenceData, len(searchResp.Data))logs.CtxDebugf(ctx, "[GetDraftIntelligenceList] searchResp.Data: %v", conv.DebugJsonToStr(searchResp.Data))if len(searchResp.Data) > 1 {for idx := range searchResp.Data[1:] {index := idx + 1data := searchResp.Data[index]tasks.Go(func() error {info, err := s.packIntelligenceData(ctx, data)if err != nil {logs.CtxErrorf(ctx, "[packIntelligenceData] failed id %v, type %d , name %s, err: %v", data.ID, data.Type, data.GetName(), err)return err}lock.Lock()defer lock.Unlock()intelligenceDataList[index] = inforeturn nil})}}if len(searchResp.Data) != 0 {info, err := s.packIntelligenceData(ctx, searchResp.Data[0])if err != nil {logs.CtxErrorf(ctx, "[packIntelligenceData] failed id %v, type %d , name %s, err: %v", searchResp.Data[0].ID, searchResp.Data[0].Type, searchResp.Data[0].GetName(), err)return nil, err}lock.Lock()intelligenceDataList[0] = infolock.Unlock()}err = tasks.Wait()if err != nil {return nil, err}filterDataList := make([]*intelligence.IntelligenceData, 0)for _, data := range intelligenceDataList {if data != nil {filterDataList = append(filterDataList, data)}}return &intelligence.GetDraftIntelligenceListResponse{Code: 0,Data: &intelligence.DraftIntelligenceListData{Intelligences: filterDataList,Total:         int32(len(filterDataList)),HasMore:       searchResp.HasMore,NextCursorID:  searchResp.NextCursor,},}, nil
}

代码功能:
在用户登录后获取项目列表的场景中(如 GetDraftIntelligenceList 方法),系统会:

  • 首先通过搜索服务获取项目列表: searchResp, err := s.DomainSVC.SearchProjects(ctx, do)
  • 然后 循环遍历 每个项目,为每个项目调用 packIntelligenceData
  • 在 packIntelligenceData 中,为每个项目创建对应的 packer 并调用 GetProjectInfo获取具体Project的信息。
func (s *SearchApplicationService) packIntelligenceData(ctx context.Context, doc *searchEntity.ProjectDocument) (*intelligence.IntelligenceData, error) {intelligenceData := &intelligence.IntelligenceData{Type: doc.Type,BasicInfo: &common.IntelligenceBasicInfo{ID:          doc.ID,Name:        doc.GetName(),SpaceID:     doc.GetSpaceID(),OwnerID:     doc.GetOwnerID(),Status:      doc.Status,CreateTime:  doc.GetCreateTime() / 1000,UpdateTime:  doc.GetUpdateTime() / 1000,PublishTime: doc.GetPublishTime() / 1000,},}uid := ctxutil.MustGetUIDFromCtx(ctx)packer, err := NewPackProject(uid, doc.ID, doc.Type, s)if err != nil {return nil, err}projInfo, err := packer.GetProjectInfo(ctx)if err != nil {return nil, errorx.Wrapf(err, "GetProjectInfo failed, id: %v, type: %v", doc.ID, doc.Type)}intelligenceData.BasicInfo.Description = projInfo.descintelligenceData.BasicInfo.IconURI = projInfo.iconURIintelligenceData.BasicInfo.IconURL = s.getProjectIconURL(ctx, projInfo.iconURI, doc.Type)intelligenceData.PermissionInfo = packer.GetPermissionInfo()publishedInf := packer.GetPublishedInfo(ctx)if publishedInf != nil {intelligenceData.PublishInfo = packer.GetPublishedInfo(ctx)} else {intelligenceData.PublishInfo = &intelligence.IntelligencePublishInfo{HasPublished: false,}}intelligenceData.OwnerInfo = packer.GetUserInfo(ctx, doc.GetOwnerID())intelligenceData.LatestAuditInfo = &common.AuditInfo{}intelligenceData.FavoriteInfo = s.buildProjectFavoriteInfo(doc)intelligenceData.OtherInfo = s.buildProjectOtherInfo(doc)return intelligenceData, nil
}

代码功能:
这段代码是 SearchApplicationService 中的 packIntelligenceData 方法,用于将搜索文档数据转换为智能体数据结构。主要功能包括:

核心功能
数据转换与封装 :将 searchEntity.ProjectDocument 转换为 intelligence.IntelligenceData 结构

主要处理步骤

  1. 基础信息构建
  • 创建 IntelligenceData 结构体
  • 填充基础信息:ID、名称、空间ID、所有者ID、状态
  • 时间戳转换:将毫秒时间戳转换为秒(除以1000)
    项目信息获取
  • 从上下文获取用户ID ( ctxutil.MustGetUIDFromCtx )
  • 创建项目打包器 ( NewPackProject )
  • 获取项目详细信息 ( GetProjectInfo )
    详细信息填充
  • 描述和图标 :从项目信息中获取描述、图标URI和图标URL
  • 权限信息 :通过打包器获取权限信息
  • 发布信息 :检查是否有发布信息,没有则设置为未发布状态
  • 用户信息 :获取所有者的用户信息
  • 其他信息 :构建审计信息、收藏信息和其他扩展信息
    设计模式
    使用了 Builder 模式 和 Adapter 模式 ,通过 packer 对象统一处理不同类型项目的信息获取和转换,实现了数据结构的标准化封装。

这是典型的 数据传输对象(DTO)转换 方法,用于搜索服务中将内部数据结构转换为前端展示所需的格式

GetProjectInfo详解

文件位置:backend\application\search\project_pack.go
接口定义核心代码:

type ProjectPacker interface {GetProjectInfo(ctx context.Context) (*projectInfo, error)// 其他方法...
}

agentPacker实现核心代码:

func (a *agentPacker) GetProjectInfo(ctx context.Context) (*projectInfo, error) {agent, err := a.SVC.SingleAgentDomainSVC.GetSingleAgentDraft(ctx, a.projectID)if err != nil {return nil, err}if agent == nil {return nil, fmt.Errorf("agent info is nil")}return &projectInfo{iconURI: agent.IconURI,desc:    agent.Desc,}, nil
}

appPacker实现核心代码:

func (a *appPacker) GetProjectInfo(ctx context.Context) (*projectInfo, error) {app, err := a.SVC.APPDomainSVC.GetDraftAPP(ctx, a.projectID)if err != nil {return nil, err}return &projectInfo{iconURI: app.GetIconURI(),desc:    app.GetDesc(),}, nil
}

说明:

  • GetProjectInfo 方法有两个不同的实现,分别用于处理不同类型的项目
  • agentPacker 用于处理 Bot 类型的项目( IntelligenceType_Bot )
  • appPacker 用于处理 Project 类型的项目( IntelligenceType_Project )
  • 两个实现都返回包含 iconURI 和 desc 字段的 projectInfo 结构体
  • 具体使用哪个实现取决于项目类型,通过 NewPackProject 工厂函数来创建相应的实现

4. 领域服务层

搜索领域服务接口

文件位置:backend\domain\search\service\service.go
核心代码:

package serviceimport ("context""github.com/coze-dev/coze-studio/backend/domain/search/entity"
)type ProjectEventBus interface {PublishProject(ctx context.Context, event *entity.ProjectDomainEvent) error
}type ResourceEventBus interface {PublishResources(ctx context.Context, event *entity.ResourceDomainEvent) error
}type Search interface {SearchProjects(ctx context.Context, req *entity.SearchProjectsRequest) (resp *entity.SearchProjectsResponse, err error)SearchResources(ctx context.Context, req *entity.SearchResourcesRequest) (resp *entity.SearchResourcesResponse, err error)
}

搜索领域服务实现-业务接口

文件位置:backend\domain\search\service\search.go

核心代码:

package serviceimport ("context""strconv""github.com/bytedance/sonic"model "github.com/coze-dev/coze-studio/backend/api/model/crossdomain/search"searchEntity "github.com/coze-dev/coze-studio/backend/domain/search/entity""github.com/coze-dev/coze-studio/backend/infra/contract/es""github.com/coze-dev/coze-studio/backend/pkg/lang/conv""github.com/coze-dev/coze-studio/backend/pkg/lang/ptr""github.com/coze-dev/coze-studio/backend/pkg/logs"
)var searchInstance *searchImplfunc NewDomainService(ctx context.Context, e es.Client) Search {return &searchImpl{esClient: e,}
}type searchImpl struct {esClient es.Client
}func (s *searchImpl) SearchProjects(ctx context.Context, req *searchEntity.SearchProjectsRequest) (resp *searchEntity.SearchProjectsResponse, err error) {logs.CtxDebugf(ctx, "[SearchProjects] search : %s", conv.DebugJsonToStr(req))searchReq := &es.Request{Query: &es.Query{Bool: &es.BoolQuery{},},}result, err := s.esClient.Search(ctx, projectIndexName, searchReq)if err != nil {logs.CtxDebugf(ctx, "[Serarch.DO] err : %v", err)return nil, err}hits := result.Hits.HitshasMore := func() bool {if len(hits) > reqLimit {return true}return false}()if hasMore {hits = hits[:reqLimit]}docs := make([]*searchEntity.ProjectDocument, 0, len(hits))for _, hit := range hits {doc, err := hit2AppDocument(hit)if err != nil {return nil, err}docs = append(docs, doc)}nextCursor := ""if len(docs) > 0 {nextCursor = formatProjectNextCursor(req.OrderFiledName, docs[len(docs)-1])}if nextCursor == "" {hasMore = false}resp = &searchEntity.SearchProjectsResponse{Data:       docs,HasMore:    hasMore,NextCursor: nextCursor,}return resp, nil
}

APP领域服务接口

文件位置:backend/domain/app/service/service.go

领域服务层定义了项目开发的核心业务接口,封装了复杂的业务逻辑和数据操作。

核心代码:

type AppService interface {GetDraftAPP(ctx context.Context, req *GetDraftAPPRequest) (*entity.APP, error)}

领域服务特点

  1. 业务抽象:定义了智能体应用开发的核心业务操作,包括CRUD和发布管理
  2. 状态管理:管理应用从草稿到发布的状态转换和生命周期
  3. 版本控制:支持应用的版本管理和发布历史记录
  4. 权限控制:通过OwnerID确保用户只能操作自己的应用
  5. 资源管理:管理应用相关的资源,如图标、配置等
  6. 连接器集成:支持应用发布时的连接器配置和管理

APP领域服务实现-业务接口

文件位置:backend/domain/app/service/service_impl.go

核心代码:

func (a *appServiceImpl) GetDraftAPP(ctx context.Context, appID int64) (app *entity.APP, err error) {app, exist, err := a.APPRepo.GetDraftAPP(ctx, appID)if err != nil {return nil, err}if !exist {return nil, errorx.New(errno.ErrAppRecordNotFound)}return app, nil
}

APP领域服务层实现-业务实体

文件位置:backend/domain/app/entity/app.go

实体模型定义了智能体应用的核心数据结构和业务方法。

核心代码:

package entity// APP 智能体应用实体
type APP struct {ID                int64  `gorm:"column:id;primaryKey" json:"id"`SpaceID           int64  `gorm:"column:space_id" json:"space_id"`           // 工作空间IDIconURI           string `gorm:"column:icon_uri" json:"icon_uri"`           // 应用图标URIName              string `gorm:"column:name" json:"name"`                   // 应用名称Desc              string `gorm:"column:desc" json:"desc"`                   // 应用描述OwnerID           int64  `gorm:"column:owner_id" json:"owner_id"`           // 应用所有者IDConnectorIDs      string `gorm:"column:connector_ids" json:"connector_ids"` // 连接器ID列表(JSON)Version           string `gorm:"column:version" json:"version"`             // 当前版本PublishRecordID   int64  `gorm:"column:publish_record_id" json:"publish_record_id"` // 发布记录IDPublishStatus     int32  `gorm:"column:publish_status" json:"publish_status"`       // 发布状态PublishExtraInfo  string `gorm:"column:publish_extra_info" json:"publish_extra_info"` // 发布额外信息CreatedAtMS       int64  `gorm:"column:created_at_ms" json:"created_at_ms"`   // 创建时间(毫秒)UpdatedAtMS       int64  `gorm:"column:updated_at_ms" json:"updated_at_ms"`   // 更新时间(毫秒)PublishedAtMS     int64  `gorm:"column:published_at_ms" json:"published_at_ms"` // 发布时间(毫秒)
}

实体设计特点

  1. 状态管理:通过PublishStatus字段管理草稿、发布成功、发布失败等状态
  2. 工作空间隔离:通过SpaceID实现多工作空间的数据隔离
  3. 权限控制:通过OwnerID确保应用的所有权管理
  4. 版本管理:支持应用的版本号和发布记录管理
  5. 连接器集成:通过ConnectorIDs字段管理应用的连接器配置
  6. 时间追踪:精确到毫秒的创建、更新、发布时间记录
  7. 扩展信息:通过PublishExtraInfo字段存储发布相关的额外信息

单Agent领域服务接口

文件位置:backend\domain\agent\singleagent\service\single_agent.go

核心代码:

package singleagentimport ("context""github.com/cloudwego/eino/schema""github.com/coze-dev/coze-studio/backend/api/model/playground""github.com/coze-dev/coze-studio/backend/domain/agent/singleagent/entity"
)type SingleAgent interface {GetSingleAgentDraft(ctx context.Context, agentID int64) (agentInfo *entity.SingleAgent, err error)}

单Agent领域服务实现-业务接口

文件位置:domain\agent\singleagent\service\single_agent_impl.go

核心代码:


func (s *singleAgentImpl) GetSingleAgentDraft(ctx context.Context, agentID int64) (*entity.SingleAgent, error) {return s.AgentDraftRepo.Get(ctx, agentID)
}

单Agent领域服务层实现-业务实体

文件位置:backend/domain/agent/singleagent/entity/single_agent.go
核心代码:

package entity
import ("github.com/coze-dev/coze-studio/backend/api/model/crossdomain/singleagent"
)// Use composition instead of aliasing for domain entities to enhance extensibility
type SingleAgent struct {*singleagent.SingleAgent
}type AgentIdentity = singleagent.AgentIdentity

文件位置:backend/api/model/crossdomain/singleagent/single_agent.go
核心代码:

package singleagenttype SingleAgent struct {AgentID   int64CreatorID int64SpaceID   int64Name      stringDesc      stringIconURI   stringCreatedAt int64UpdatedAt int64Version   stringDeletedAt gorm.DeletedAtVariablesMetaID         *int64OnboardingInfo          *bot_common.OnboardingInfoModelInfo               *bot_common.ModelInfoPrompt                  *bot_common.PromptInfoPlugin                  []*bot_common.PluginInfoKnowledge               *bot_common.KnowledgeWorkflow                []*bot_common.WorkflowInfoSuggestReply            *bot_common.SuggestReplyInfoJumpConfig              *bot_common.JumpConfigBackgroundImageInfoList []*bot_common.BackgroundImageInfoDatabase                []*bot_common.DatabaseBotMode                 bot_common.BotModeLayoutInfo              *bot_common.LayoutInfoShortcutCommand         []string
}type AgentIdentity struct {AgentID int64// State   AgentStateVersion     stringIsDraft     boolConnectorID int64
}

5. 数据访问层

AppRepo仓储接口定义

文件位置:backend/domain/app/repository/app.go

type AppRepository interface {GetDraftAPP(ctx context.Context, appID int64) (app *entity.APP, exist bool, err error)
}
AppRepo仓储实现

文件位置:backend/domain/app/repository/app_impl.go

func (a *appRepoImpl) GetDraftAPP(ctx context.Context, appID int64) (app *entity.APP, exist bool, err error) {return a.appDraftDAO.Get(ctx, appID)
}

GetDraftAPP接口功能分析

  1. 接口职责:根据应用ID获取草稿状态的智能体应用信息
  2. 参数验证:接收上下文和应用ID作为参数
  3. 数据查询:通过仓储层调用DAO层查询草稿应用数据
  4. 存在性检查:验证应用是否存在,不存在时返回特定错误
  5. 错误处理:统一的错误处理和包装机制
  6. 返回结果:返回完整的APP实体对象

设计特点

  1. 分层架构:严格遵循领域驱动设计,服务层调用仓储层
  2. 错误处理:使用统一的错误包装机制,提供清晰的错误信息
  3. 数据完整性:确保返回的应用数据完整且有效
  4. 接口简洁:接口设计简洁明了,职责单一
  5. 类型安全:使用强类型参数和返回值,确保类型安全
数据访问(app_draft.go)

文件位置:backend\domain\app\internal\dal\app_draft.go

func (a *APPDraftDAO) Get(ctx context.Context, appID int64) (app *entity.APP, exist bool, err error) {table := a.query.AppDraftres, err := table.WithContext(ctx).Where(table.ID.Eq(appID)).First()if err != nil {if errors.Is(err, gorm.ErrRecordNotFound) {return nil, false, nil}return nil, false, err}app = appDraftPO(*res).ToDO()return app, true, nil
}

代码功能:

  1. 数据库查询 - 使用GORM查询框架,从 AppDraft 表中根据ID查找记录
  2. 错误处理 - 区分处理两种情况:
    • 记录不存在:返回 (nil, false, nil)
    • 其他数据库错误:返回 (nil, false, err)
  3. 数据转换 - 将数据库持久化对象(PO)转换为领域实体(DO)
    • appDraftPO(*res).ToDO() 执行PO到DO的转换
  4. 成功返回 - 找到记录时返回 (app, true, nil)
SingleAgentRepo仓储接口定义

文件位置:backend\domain\agent\singleagent\repository\repository.go

package repositoryimport ("context""gorm.io/gorm""github.com/coze-dev/coze-studio/backend/domain/agent/singleagent/entity""github.com/coze-dev/coze-studio/backend/domain/agent/singleagent/internal/dal")func NewSingleAgentRepo(db *gorm.DB, idGen idgen.IDGenerator, cli cache.Cmdable) SingleAgentDraftRepo {return dal.NewSingleAgentDraftDAO(db, idGen, cli)
}type SingleAgentDraftRepo interface {Get(ctx context.Context, agentID int64) (*entity.SingleAgent, error)
}
SingleAgentRepo仓储实现

文件位置:backend\domain\agent\singleagent\internal\dal\single_agent_draft.go

type SingleAgentDraftDAO struct {idGen       idgen.IDGeneratordbQuery     *query.QuerycacheClient cache.Cmdable
}func NewSingleAgentDraftDAO(db *gorm.DB, idGen idgen.IDGenerator, cli cache.Cmdable) *SingleAgentDraftDAO {query.SetDefault(db)return &SingleAgentDraftDAO{idGen:       idGen,dbQuery:     query.Use(db),cacheClient: cli,}
}func (sa *SingleAgentDraftDAO) Get(ctx context.Context, agentID int64) (*entity.SingleAgent, error) {singleAgentDAOModel := sa.dbQuery.SingleAgentDraftsingleAgent, err := sa.dbQuery.SingleAgentDraft.Where(singleAgentDAOModel.AgentID.Eq(agentID)).First()if errors.Is(err, gorm.ErrRecordNotFound) {return nil, nil}if err != nil {return nil, errorx.WrapByCode(err, errno.ErrAgentGetCode)}do := sa.singleAgentDraftPo2Do(singleAgent)return do, nil
}

数据模型层

AppRepo-统一数据查询(query\gen.go)

文件位置:backend\domain\app\internal\dal\query\gen.go

package queryimport ("context""database/sql""gorm.io/gorm""gorm.io/gen""gorm.io/plugin/dbresolver"
)var (Q                      = new(Query)AppConnectorReleaseRef *appConnectorReleaseRefAppDraft               *appDraftAppReleaseRecord       *appReleaseRecord
)func SetDefault(db *gorm.DB, opts ...gen.DOOption) {*Q = *Use(db, opts...)AppConnectorReleaseRef = &Q.AppConnectorReleaseRefAppDraft = &Q.AppDraftAppReleaseRecord = &Q.AppReleaseRecord
}func Use(db *gorm.DB, opts ...gen.DOOption) *Query {return &Query{db:                     db,AppConnectorReleaseRef: newAppConnectorReleaseRef(db, opts...),AppDraft:               newAppDraft(db, opts...),AppReleaseRecord:       newAppReleaseRecord(db, opts...),}
}type Query struct {db *gorm.DBAppConnectorReleaseRef appConnectorReleaseRefAppDraft               appDraftAppReleaseRecord       appReleaseRecord
}

代码作用:

  1. 初始化管理
  • SetDefault() : 设置全局默认查询实例
  • Use() : 创建新的查询实例,初始化各表的查询器
  1. 数据库连接管理
  • Available() : 检查数据库连接是否可用
  • clone() : 克隆查询实例
  • ReadDB() / WriteDB() : 支持读写分离
  • ReplaceDB() : 替换数据库连接
  1. 上下文支持
  • WithContext() : 为查询操作添加上下文,返回带接口的查询上下文
  1. 事务管理
  • Transaction() : 执行事务操作
  • Begin() : 开始事务,返回 QueryTx
  • QueryTx : 事务查询器,支持 Commit、Rollback、SavePoint 等操作
    这是典型的 Repository 模式 实现,为 app 领域的数据访问提供了统一、类型安全的接口,支持事务、读写分离等高级数据库功能。
AppRepo-app_draft数据查询

文件位置:backend\domain\app\internal\dal\query\app_draft.gen.go

package queryimport ("context""gorm.io/gorm""gorm.io/gorm/clause""gorm.io/gorm/schema""gorm.io/gen""gorm.io/gen/field""gorm.io/plugin/dbresolver""github.com/coze-dev/coze-studio/backend/domain/app/internal/dal/model"
)func newAppDraft(db *gorm.DB, opts ...gen.DOOption) appDraft {_appDraft := appDraft{}_appDraft.appDraftDo.UseDB(db, opts...)_appDraft.appDraftDo.UseModel(&model.AppDraft{})tableName := _appDraft.appDraftDo.TableName()_appDraft.ALL = field.NewAsterisk(tableName)_appDraft.ID = field.NewInt64(tableName, "id")_appDraft.SpaceID = field.NewInt64(tableName, "space_id")_appDraft.OwnerID = field.NewInt64(tableName, "owner_id")_appDraft.IconURI = field.NewString(tableName, "icon_uri")_appDraft.Name = field.NewString(tableName, "name")_appDraft.Description = field.NewString(tableName, "description")_appDraft.CreatedAt = field.NewInt64(tableName, "created_at")_appDraft.UpdatedAt = field.NewInt64(tableName, "updated_at")_appDraft.DeletedAt = field.NewField(tableName, "deleted_at")_appDraft.fillFieldMap()return _appDraft
}// appDraft Draft Application
type appDraft struct {appDraftDoALL         field.AsteriskID          field.Int64  // APP IDSpaceID     field.Int64  // Space IDOwnerID     field.Int64  // Owner IDIconURI     field.String // Icon URIName        field.String // Application NameDescription field.String // Application DescriptionCreatedAt   field.Int64  // Create Time in MillisecondsUpdatedAt   field.Int64  // Update Time in MillisecondsDeletedAt   field.Field  // Delete TimefieldMap map[string]field.Expr
}type IAppDraftDo interface {gen.SubQueryDebug() IAppDraftDoWithContext(ctx context.Context) IAppDraftDoWithResult(fc func(tx gen.Dao)) gen.ResultInfoReplaceDB(db *gorm.DB)ReadDB() IAppDraftDoWriteDB() IAppDraftDoAs(alias string) gen.DaoSession(config *gorm.Session) IAppDraftDoColumns(cols ...field.Expr) gen.ColumnsClauses(conds ...clause.Expression) IAppDraftDoNot(conds ...gen.Condition) IAppDraftDoOr(conds ...gen.Condition) IAppDraftDoSelect(conds ...field.Expr) IAppDraftDoWhere(conds ...gen.Condition) IAppDraftDoOrder(conds ...field.Expr) IAppDraftDoDistinct(cols ...field.Expr) IAppDraftDoOmit(cols ...field.Expr) IAppDraftDoJoin(table schema.Tabler, on ...field.Expr) IAppDraftDoLeftJoin(table schema.Tabler, on ...field.Expr) IAppDraftDoRightJoin(table schema.Tabler, on ...field.Expr) IAppDraftDoGroup(cols ...field.Expr) IAppDraftDoHaving(conds ...gen.Condition) IAppDraftDoLimit(limit int) IAppDraftDoOffset(offset int) IAppDraftDoCount() (count int64, err error)Scopes(funcs ...func(gen.Dao) gen.Dao) IAppDraftDoUnscoped() IAppDraftDoCreate(values ...*model.AppDraft) errorCreateInBatches(values []*model.AppDraft, batchSize int) errorSave(values ...*model.AppDraft) errorFirst() (*model.AppDraft, error)Take() (*model.AppDraft, error)Last() (*model.AppDraft, error)Find() ([]*model.AppDraft, error)FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.AppDraft, err error)FindInBatches(result *[]*model.AppDraft, batchSize int, fc func(tx gen.Dao, batch int) error) errorPluck(column field.Expr, dest interface{}) errorDelete(...*model.AppDraft) (info gen.ResultInfo, err error)Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error)UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)Updates(value interface{}) (info gen.ResultInfo, err error)UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)UpdateColumns(value interface{}) (info gen.ResultInfo, err error)UpdateFrom(q gen.SubQuery) gen.DaoAttrs(attrs ...field.AssignExpr) IAppDraftDoAssign(attrs ...field.AssignExpr) IAppDraftDoJoins(fields ...field.RelationField) IAppDraftDoPreload(fields ...field.RelationField) IAppDraftDoFirstOrInit() (*model.AppDraft, error)FirstOrCreate() (*model.AppDraft, error)FindByPage(offset int, limit int) (result []*model.AppDraft, count int64, err error)ScanByPage(result interface{}, offset int, limit int) (count int64, err error)Scan(result interface{}) (err error)Returning(value interface{}, columns ...string) IAppDraftDoUnderlyingDB() *gorm.DBschema.Tabler
}func (a appDraftDo) Debug() IAppDraftDo {return a.withDO(a.DO.Debug())
}func (a appDraftDo) WithContext(ctx context.Context) IAppDraftDo {return a.withDO(a.DO.WithContext(ctx))
}

代码作用:
这是典型的 ORM 查询构建器模式 ,为 app_draft 表提供了类型安全、功能丰富的数据库操作接口。

AppRepo-app_draft数据模型

文件位置:backend\domain\app\internal\dal\model\app_draft.gen.go

package modelimport ("gorm.io/gorm"
)const TableNameAppDraft = "app_draft"// AppDraft Draft Application
type AppDraft struct {ID          int64          `gorm:"column:id;primaryKey;comment:APP ID" json:"id"`                                                         // APP IDSpaceID     int64          `gorm:"column:space_id;not null;comment:Space ID" json:"space_id"`                                             // Space IDOwnerID     int64          `gorm:"column:owner_id;not null;comment:Owner ID" json:"owner_id"`                                             // Owner IDIconURI     string         `gorm:"column:icon_uri;not null;comment:Icon URI" json:"icon_uri"`                                             // Icon URIName        string         `gorm:"column:name;not null;comment:Application Name" json:"name"`                                             // Application NameDescription string         `gorm:"column:description;comment:Application Description" json:"description"`                                 // Application DescriptionCreatedAt   int64          `gorm:"column:created_at;not null;autoCreateTime:milli;comment:Create Time in Milliseconds" json:"created_at"` // Create Time in MillisecondsUpdatedAt   int64          `gorm:"column:updated_at;not null;autoUpdateTime:milli;comment:Update Time in Milliseconds" json:"updated_at"` // Update Time in MillisecondsDeletedAt   gorm.DeletedAt `gorm:"column:deleted_at;comment:Delete Time" json:"deleted_at"`                                               // Delete Time
}// TableName AppDraft's table name
func (*AppDraft) TableName() string {return TableNameAppDraft
}

代码作用:

  • 1.数据模型定义 - 定义了 AppDraft 结构体,对应数据库中的 app_draft 表
  • 2.字段映射 - 通过GORM标签将Go结构体字段映射到数据库表列
  • 3.表名绑定 - 通过 TableName() 方法指定对应的数据库表名
SingleAgentRepo-统一数据查询(query\gen.go)

文件位置:backend\domain\agent\singleagent\internal\dal\query\gen.go

package queryimport ("context""database/sql""gorm.io/gorm""gorm.io/gen""gorm.io/plugin/dbresolver"
)var (Q                  = new(Query)SingleAgentDraft   *singleAgentDraftSingleAgentPublish *singleAgentPublishSingleAgentVersion *singleAgentVersion
)func SetDefault(db *gorm.DB, opts ...gen.DOOption) {*Q = *Use(db, opts...)SingleAgentDraft = &Q.SingleAgentDraftSingleAgentPublish = &Q.SingleAgentPublishSingleAgentVersion = &Q.SingleAgentVersion
}func Use(db *gorm.DB, opts ...gen.DOOption) *Query {return &Query{db:                 db,SingleAgentDraft:   newSingleAgentDraft(db, opts...),SingleAgentPublish: newSingleAgentPublish(db, opts...),SingleAgentVersion: newSingleAgentVersion(db, opts...),}
}type Query struct {db *gorm.DBSingleAgentDraft   singleAgentDraftSingleAgentPublish singleAgentPublishSingleAgentVersion singleAgentVersion
}
SingleAgentRepo-single_agent_draft数据查询

文件位置:backend\domain\agent\singleagent\internal\dal\query\single_agent_draft.gen.go

package queryimport ("context""gorm.io/gorm""gorm.io/gorm/clause""gorm.io/gorm/schema""gorm.io/gen""gorm.io/gen/field""gorm.io/plugin/dbresolver""github.com/coze-dev/coze-studio/backend/domain/agent/singleagent/internal/dal/model"
)func newSingleAgentDraft(db *gorm.DB, opts ...gen.DOOption) singleAgentDraft {_singleAgentDraft := singleAgentDraft{}_singleAgentDraft.singleAgentDraftDo.UseDB(db, opts...)_singleAgentDraft.singleAgentDraftDo.UseModel(&model.SingleAgentDraft{})tableName := _singleAgentDraft.singleAgentDraftDo.TableName()_singleAgentDraft.ALL = field.NewAsterisk(tableName)_singleAgentDraft.ID = field.NewInt64(tableName, "id")_singleAgentDraft.AgentID = field.NewInt64(tableName, "agent_id")_singleAgentDraft.CreatorID = field.NewInt64(tableName, "creator_id")_singleAgentDraft.SpaceID = field.NewInt64(tableName, "space_id")_singleAgentDraft.Name = field.NewString(tableName, "name")_singleAgentDraft.Description = field.NewString(tableName, "description")_singleAgentDraft.IconURI = field.NewString(tableName, "icon_uri")_singleAgentDraft.CreatedAt = field.NewInt64(tableName, "created_at")_singleAgentDraft.UpdatedAt = field.NewInt64(tableName, "updated_at")_singleAgentDraft.DeletedAt = field.NewField(tableName, "deleted_at")_singleAgentDraft.VariablesMetaID = field.NewInt64(tableName, "variables_meta_id")_singleAgentDraft.ModelInfo = field.NewField(tableName, "model_info")_singleAgentDraft.OnboardingInfo = field.NewField(tableName, "onboarding_info")_singleAgentDraft.Prompt = field.NewField(tableName, "prompt")_singleAgentDraft.Plugin = field.NewField(tableName, "plugin")_singleAgentDraft.Knowledge = field.NewField(tableName, "knowledge")_singleAgentDraft.Workflow = field.NewField(tableName, "workflow")_singleAgentDraft.SuggestReply = field.NewField(tableName, "suggest_reply")_singleAgentDraft.JumpConfig = field.NewField(tableName, "jump_config")_singleAgentDraft.BackgroundImageInfoList = field.NewField(tableName, "background_image_info_list")_singleAgentDraft.DatabaseConfig = field.NewField(tableName, "database_config")_singleAgentDraft.BotMode = field.NewInt32(tableName, "bot_mode")_singleAgentDraft.ShortcutCommand = field.NewField(tableName, "shortcut_command")_singleAgentDraft.LayoutInfo = field.NewField(tableName, "layout_info")_singleAgentDraft.fillFieldMap()return _singleAgentDraft
}// singleAgentDraft Single Agent Draft Copy Table
type singleAgentDraft struct {singleAgentDraftDoALL                     field.AsteriskID                      field.Int64  // Primary Key IDAgentID                 field.Int64  // Agent IDCreatorID               field.Int64  // Creator IDSpaceID                 field.Int64  // Space IDName                    field.String // Agent NameDescription             field.String // Agent DescriptionIconURI                 field.String // Icon URICreatedAt               field.Int64  // Create Time in MillisecondsUpdatedAt               field.Int64  // Update Time in MillisecondsDeletedAt               field.Field  // delete time in millisecondVariablesMetaID         field.Int64  // variables meta table IDModelInfo               field.Field  // Model Configuration InformationOnboardingInfo          field.Field  // Onboarding InformationPrompt                  field.Field  // Agent Prompt ConfigurationPlugin                  field.Field  // Agent Plugin Base ConfigurationKnowledge               field.Field  // Agent Knowledge Base ConfigurationWorkflow                field.Field  // Agent Workflow ConfigurationSuggestReply            field.Field  // Suggested RepliesJumpConfig              field.Field  // Jump ConfigurationBackgroundImageInfoList field.Field  // Background imageDatabaseConfig          field.Field  // Agent Database Base ConfigurationBotMode                 field.Int32  // mod,0:single mode 2:chatflow modeShortcutCommand         field.Field  // shortcut commandLayoutInfo              field.Field  // chatflow layout infofieldMap map[string]field.Expr
}func (s singleAgentDraft) Table(newTableName string) *singleAgentDraft {s.singleAgentDraftDo.UseTable(newTableName)return s.updateTableName(newTableName)
}func (s singleAgentDraft) As(alias string) *singleAgentDraft {s.singleAgentDraftDo.DO = *(s.singleAgentDraftDo.As(alias).(*gen.DO))return s.updateTableName(alias)
}func (s *singleAgentDraft) updateTableName(table string) *singleAgentDraft {s.ALL = field.NewAsterisk(table)s.ID = field.NewInt64(table, "id")s.AgentID = field.NewInt64(table, "agent_id")s.CreatorID = field.NewInt64(table, "creator_id")s.SpaceID = field.NewInt64(table, "space_id")s.Name = field.NewString(table, "name")s.Description = field.NewString(table, "description")s.IconURI = field.NewString(table, "icon_uri")s.CreatedAt = field.NewInt64(table, "created_at")s.UpdatedAt = field.NewInt64(table, "updated_at")s.DeletedAt = field.NewField(table, "deleted_at")s.VariablesMetaID = field.NewInt64(table, "variables_meta_id")s.ModelInfo = field.NewField(table, "model_info")s.OnboardingInfo = field.NewField(table, "onboarding_info")s.Prompt = field.NewField(table, "prompt")s.Plugin = field.NewField(table, "plugin")s.Knowledge = field.NewField(table, "knowledge")s.Workflow = field.NewField(table, "workflow")s.SuggestReply = field.NewField(table, "suggest_reply")s.JumpConfig = field.NewField(table, "jump_config")s.BackgroundImageInfoList = field.NewField(table, "background_image_info_list")s.DatabaseConfig = field.NewField(table, "database_config")s.BotMode = field.NewInt32(table, "bot_mode")s.ShortcutCommand = field.NewField(table, "shortcut_command")s.LayoutInfo = field.NewField(table, "layout_info")s.fillFieldMap()return s
}func (s *singleAgentDraft) GetFieldByName(fieldName string) (field.OrderExpr, bool) {_f, ok := s.fieldMap[fieldName]if !ok || _f == nil {return nil, false}_oe, ok := _f.(field.OrderExpr)return _oe, ok
}func (s *singleAgentDraft) fillFieldMap() {s.fieldMap = make(map[string]field.Expr, 24)s.fieldMap["id"] = s.IDs.fieldMap["agent_id"] = s.AgentIDs.fieldMap["creator_id"] = s.CreatorIDs.fieldMap["space_id"] = s.SpaceIDs.fieldMap["name"] = s.Names.fieldMap["description"] = s.Descriptions.fieldMap["icon_uri"] = s.IconURIs.fieldMap["created_at"] = s.CreatedAts.fieldMap["updated_at"] = s.UpdatedAts.fieldMap["deleted_at"] = s.DeletedAts.fieldMap["variables_meta_id"] = s.VariablesMetaIDs.fieldMap["model_info"] = s.ModelInfos.fieldMap["onboarding_info"] = s.OnboardingInfos.fieldMap["prompt"] = s.Prompts.fieldMap["plugin"] = s.Plugins.fieldMap["knowledge"] = s.Knowledges.fieldMap["workflow"] = s.Workflows.fieldMap["suggest_reply"] = s.SuggestReplys.fieldMap["jump_config"] = s.JumpConfigs.fieldMap["background_image_info_list"] = s.BackgroundImageInfoLists.fieldMap["database_config"] = s.DatabaseConfigs.fieldMap["bot_mode"] = s.BotModes.fieldMap["shortcut_command"] = s.ShortcutCommands.fieldMap["layout_info"] = s.LayoutInfo
}func (s singleAgentDraft) clone(db *gorm.DB) singleAgentDraft {s.singleAgentDraftDo.ReplaceConnPool(db.Statement.ConnPool)return s
}func (s singleAgentDraft) replaceDB(db *gorm.DB) singleAgentDraft {s.singleAgentDraftDo.ReplaceDB(db)return s
}type singleAgentDraftDo struct{ gen.DO }type ISingleAgentDraftDo interface {gen.SubQueryDebug() ISingleAgentDraftDoWithContext(ctx context.Context) ISingleAgentDraftDoWithResult(fc func(tx gen.Dao)) gen.ResultInfoReplaceDB(db *gorm.DB)ReadDB() ISingleAgentDraftDoWriteDB() ISingleAgentDraftDoAs(alias string) gen.DaoSession(config *gorm.Session) ISingleAgentDraftDoColumns(cols ...field.Expr) gen.ColumnsClauses(conds ...clause.Expression) ISingleAgentDraftDoNot(conds ...gen.Condition) ISingleAgentDraftDoOr(conds ...gen.Condition) ISingleAgentDraftDoSelect(conds ...field.Expr) ISingleAgentDraftDoWhere(conds ...gen.Condition) ISingleAgentDraftDoOrder(conds ...field.Expr) ISingleAgentDraftDoDistinct(cols ...field.Expr) ISingleAgentDraftDoOmit(cols ...field.Expr) ISingleAgentDraftDoJoin(table schema.Tabler, on ...field.Expr) ISingleAgentDraftDoLeftJoin(table schema.Tabler, on ...field.Expr) ISingleAgentDraftDoRightJoin(table schema.Tabler, on ...field.Expr) ISingleAgentDraftDoGroup(cols ...field.Expr) ISingleAgentDraftDoHaving(conds ...gen.Condition) ISingleAgentDraftDoLimit(limit int) ISingleAgentDraftDoOffset(offset int) ISingleAgentDraftDoCount() (count int64, err error)Scopes(funcs ...func(gen.Dao) gen.Dao) ISingleAgentDraftDoUnscoped() ISingleAgentDraftDoCreate(values ...*model.SingleAgentDraft) errorCreateInBatches(values []*model.SingleAgentDraft, batchSize int) errorSave(values ...*model.SingleAgentDraft) errorFirst() (*model.SingleAgentDraft, error)Take() (*model.SingleAgentDraft, error)Last() (*model.SingleAgentDraft, error)Find() ([]*model.SingleAgentDraft, error)FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.SingleAgentDraft, err error)FindInBatches(result *[]*model.SingleAgentDraft, batchSize int, fc func(tx gen.Dao, batch int) error) errorPluck(column field.Expr, dest interface{}) errorDelete(...*model.SingleAgentDraft) (info gen.ResultInfo, err error)Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error)UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)Updates(value interface{}) (info gen.ResultInfo, err error)UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error)UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error)UpdateColumns(value interface{}) (info gen.ResultInfo, err error)UpdateFrom(q gen.SubQuery) gen.DaoAttrs(attrs ...field.AssignExpr) ISingleAgentDraftDoAssign(attrs ...field.AssignExpr) ISingleAgentDraftDoJoins(fields ...field.RelationField) ISingleAgentDraftDoPreload(fields ...field.RelationField) ISingleAgentDraftDoFirstOrInit() (*model.SingleAgentDraft, error)FirstOrCreate() (*model.SingleAgentDraft, error)FindByPage(offset int, limit int) (result []*model.SingleAgentDraft, count int64, err error)ScanByPage(result interface{}, offset int, limit int) (count int64, err error)Scan(result interface{}) (err error)Returning(value interface{}, columns ...string) ISingleAgentDraftDoUnderlyingDB() *gorm.DBschema.Tabler
}
AppRepo-single_agent_draft数据模型

文件位置:backend\domain\agent\singleagent\internal\dal\model\single_agent_draft.gen.go

package modelimport ("github.com/coze-dev/coze-studio/backend/api/model/app/bot_common""gorm.io/gorm"
)const TableNameSingleAgentDraft = "single_agent_draft"// SingleAgentDraft Single Agent Draft Copy Table
type SingleAgentDraft struct {ID                      int64                             `gorm:"column:id;primaryKey;autoIncrement:true;comment:Primary Key ID" json:"id"`                                     // Primary Key IDAgentID                 int64                             `gorm:"column:agent_id;not null;comment:Agent ID" json:"agent_id"`                                                    // Agent IDCreatorID               int64                             `gorm:"column:creator_id;not null;comment:Creator ID" json:"creator_id"`                                              // Creator IDSpaceID                 int64                             `gorm:"column:space_id;not null;comment:Space ID" json:"space_id"`                                                    // Space IDName                    string                            `gorm:"column:name;not null;comment:Agent Name" json:"name"`                                                          // Agent NameDescription             string                            `gorm:"column:description;not null;comment:Agent Description" json:"description"`                                     // Agent DescriptionIconURI                 string                            `gorm:"column:icon_uri;not null;comment:Icon URI" json:"icon_uri"`                                                    // Icon URICreatedAt               int64                             `gorm:"column:created_at;not null;autoCreateTime:milli;comment:Create Time in Milliseconds" json:"created_at"`        // Create Time in MillisecondsUpdatedAt               int64                             `gorm:"column:updated_at;not null;autoUpdateTime:milli;comment:Update Time in Milliseconds" json:"updated_at"`        // Update Time in MillisecondsDeletedAt               gorm.DeletedAt                    `gorm:"column:deleted_at;comment:delete time in millisecond" json:"deleted_at"`                                       // delete time in millisecondVariablesMetaID         *int64                            `gorm:"column:variables_meta_id;comment:variables meta table ID" json:"variables_meta_id"`                            // variables meta table IDModelInfo               *bot_common.ModelInfo             `gorm:"column:model_info;comment:Model Configuration Information;serializer:json" json:"model_info"`                  // Model Configuration InformationOnboardingInfo          *bot_common.OnboardingInfo        `gorm:"column:onboarding_info;comment:Onboarding Information;serializer:json" json:"onboarding_info"`                 // Onboarding InformationPrompt                  *bot_common.PromptInfo            `gorm:"column:prompt;comment:Agent Prompt Configuration;serializer:json" json:"prompt"`                               // Agent Prompt ConfigurationPlugin                  []*bot_common.PluginInfo          `gorm:"column:plugin;comment:Agent Plugin Base Configuration;serializer:json" json:"plugin"`                          // Agent Plugin Base ConfigurationKnowledge               *bot_common.Knowledge             `gorm:"column:knowledge;comment:Agent Knowledge Base Configuration;serializer:json" json:"knowledge"`                 // Agent Knowledge Base ConfigurationWorkflow                []*bot_common.WorkflowInfo        `gorm:"column:workflow;comment:Agent Workflow Configuration;serializer:json" json:"workflow"`                         // Agent Workflow ConfigurationSuggestReply            *bot_common.SuggestReplyInfo      `gorm:"column:suggest_reply;comment:Suggested Replies;serializer:json" json:"suggest_reply"`                          // Suggested RepliesJumpConfig              *bot_common.JumpConfig            `gorm:"column:jump_config;comment:Jump Configuration;serializer:json" json:"jump_config"`                             // Jump ConfigurationBackgroundImageInfoList []*bot_common.BackgroundImageInfo `gorm:"column:background_image_info_list;comment:Background image;serializer:json" json:"background_image_info_list"` // Background imageDatabaseConfig          []*bot_common.Database            `gorm:"column:database_config;comment:Agent Database Base Configuration;serializer:json" json:"database_config"`      // Agent Database Base ConfigurationBotMode                 int32                             `gorm:"column:bot_mode;not null;comment:mod,0:single mode 2:chatflow mode" json:"bot_mode"`                           // mod,0:single mode 2:chatflow modeShortcutCommand         []string                          `gorm:"column:shortcut_command;comment:shortcut command;serializer:json" json:"shortcut_command"`                     // shortcut commandLayoutInfo              *bot_common.LayoutInfo            `gorm:"column:layout_info;comment:chatflow layout info;serializer:json" json:"layout_info"`                           // chatflow layout info
}// TableName SingleAgentDraft's table name
func (*SingleAgentDraft) TableName() string {return TableNameSingleAgentDraft
}

文件依赖关系

APP依赖层次:数据库表结构 (schema.sql)↓    gen_orm_query.go
模型文件 (model/app_draft.gen.go) - 并行生成↓
查询文件 (query/app_draft.gen.go) - 依赖对应模型↓
统一入口 (query/gen.go) - 依赖所有查询文件
singleAgent依赖层次:数据库表结构 (schema.sql)↓    gen_orm_query.go
模型文件 (model/single_agent_draft.gen.go) - 模型生成↓
查询文件 (query/single_agent_draft.gen.go) - 依赖对应模型↓
统一入口 (query/gen.go) - 依赖所有查询文件

6. 基础设施层

database.go文件详解

文件位置:backend/infra/contract/orm/database.go
核心代码:

package ormimport ("gorm.io/gorm"
)type DB = gorm.DB

文件作用:

  • 定义了 type DB = gorm.DB ,为 GORM 数据库对象提供类型别名
  • 作为契约层(Contract),为上层提供统一的数据库接口抽象
  • 便于后续可能的数据库实现替换(如从 MySQL 切换到 PostgreSQL)

mysql.go文件详解

文件位置:backend/infra/impl/mysql/mysql.go
核心代码:

package mysqlimport ("fmt""os""gorm.io/driver/mysql""gorm.io/gorm"
)func New() (*gorm.DB, error) {dsn := os.Getenv("MYSQL_DSN")db, err := gorm.Open(mysql.Open(dsn))if err != nil {return nil, fmt.Errorf("mysql open, dsn: %s, err: %w", dsn, err)}return db, nil
}

文件作用:

  • 定义了 New() 函数,负责建立 GORM MySQL 数据库连接
  • 使用环境变量 MYSQL_DSN 配置数据库连接字符串
  • 返回 *gorm.DB 实例,作为整个应用的数据库连接对象
  • 后端服务启动时,调用 mysql.New() 初始化数据库连接
main.go → application.Init() → appinfra.Init() → mysql.New()

ElasticSearch架构设计

Contract 层(接口定义)

backend/infra/contract/es/ 目录定义了 ElasticSearch 的抽象接口:

  • es.go: 定义了核心接口

    • Client 接口:包含 SearchCreateUpdateDeleteCreateIndex 等方法
    • Types 接口:定义属性类型创建方法
    • BulkIndexer 接口:批量操作接口
  • model.go: 定义数据模型

    • Request:搜索请求结构体,包含查询条件、分页、排序等
    • Response:搜索响应结构体,包含命中结果和元数据
    • Hit:单个搜索结果
    • BulkIndexerItem:批量操作项
  • query.go: 定义查询相关结构

    • Query:查询结构体,支持多种查询类型
    • QueryType 常量:equalmatchmulti_matchnot_existscontainsin
    • BoolQuery:布尔查询,支持 mustshouldfiltermust_not
    • 各种查询构造函数:NewEqualQueryNewMatchQuery
Implementation 层(具体实现)

backend/infra/impl/es/ 目录提供了具体实现:

  • es_impl.go: 工厂方法

    • New() 函数根据环境变量 ES_VERSION 选择 ES7 或 ES8 实现
    • 类型别名导出,统一接口
  • es7.go: ElasticSearch 7.x 实现

    • es7Client 结构体实现 Client 接口
    • 使用 github.com/elastic/go-elasticsearch/v7 官方客户端
    • Search 方法将抽象查询转换为 ES7 格式的 JSON 查询
    • query2ESQuery 方法处理查询类型转换
  • es8.go: ElasticSearch 8.x 实现

    • es8Client 结构体实现 Client 接口
    • 使用 github.com/elastic/go-elasticsearch/v8 官方客户端
    • 使用类型化 API,更加类型安全
    • Search 方法使用 ES8 的 typed API
查询执行流程
  1. 业务层调用backend/domain/search/service/search.go 中的 SearchProjects 方法
  2. 构建查询:创建 es.Request 对象,设置查询条件、排序、分页等
  3. 执行查询:调用 s.esClient.Search(ctx, projectIndexName, searchReq)
  4. 版本适配:根据 ES_VERSION 环境变量,自动选择 ES7 或 ES8 实现
  5. 查询转换
    • ES7:将抽象查询转换为 JSON 格式
    • ES8:将抽象查询转换为类型化结构体
  6. 结果处理:将 ES 响应转换为统一的 Response 结构体
索引使用:
  • 项目索引projectIndexName = "project_draft" 存储项目草稿信息
  • 资源索引resourceIndexName = "coze_resource" 存储各类资源信息
设计优势
  1. 版本兼容:同时支持 ES7 和 ES8,通过环境变量切换
  2. 接口统一:业务代码无需关心具体 ES 版本
  3. 类型安全:ES8 使用类型化 API,减少运行时错误
  4. 查询抽象:提供统一的查询构建方式,支持复杂的布尔查询
  5. 易于扩展:新增查询类型只需在 contract 层定义,impl 层实现

这种设计模式体现了依赖倒置原则,业务层依赖抽象接口而非具体实现,使得系统更加灵活和可维护。

7. 数据存储层

数据库表结构

文件位置:docker/volumes/mysql/schema.sql

-- 应用草稿表
CREATE TABLE IF NOT EXISTS `app_draft` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`space_id` bigint(20) NOT NULL COMMENT '工作空间ID',`owner_id` bigint(20) NOT NULL COMMENT '应用所有者ID',`icon_uri` varchar(255) NOT NULL COMMENT '应用图标URI',`name` varchar(255) NOT NULL COMMENT '应用名称',`description` text COMMENT '应用描述',`created_at` bigint(20) NOT NULL COMMENT '创建时间(毫秒级)',`updated_at` bigint(20) NOT NULL COMMENT '更新时间(毫秒级)',`deleted_at` bigint(20) DEFAULT NULL COMMENT '删除时间(毫秒级)',PRIMARY KEY (`id`),KEY `idx_space_id` (`space_id`),KEY `idx_owner_id` (`owner_id`),KEY `idx_deleted_at` (`deleted_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用草稿表';-- 单智能体草稿表
CREATE TABLE IF NOT EXISTS `single_agent_draft` (`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'Primary Key ID',`agent_id` bigint NOT NULL DEFAULT 0 COMMENT 'Agent ID',`creator_id` bigint NOT NULL DEFAULT 0 COMMENT 'Creator ID',`space_id` bigint NOT NULL DEFAULT 0 COMMENT 'Space ID',`name` varchar(255) NOT NULL DEFAULT '' COMMENT 'Agent Name',`description` text NULL COMMENT 'Agent Description',`icon_uri` varchar(255) NOT NULL DEFAULT '' COMMENT 'Icon URI',`created_at` bigint unsigned NOT NULL DEFAULT 0 COMMENT 'Create Time in Milliseconds',`updated_at` bigint unsigned NOT NULL DEFAULT 0 COMMENT 'Update Time in Milliseconds',`deleted_at` datetime(3) NULL COMMENT 'delete time in millisecond',`variables_meta_id` bigint NULL COMMENT 'variables meta table ID',`model_info` json NULL COMMENT 'Model Configuration Information',`onboarding_info` json NULL COMMENT 'Onboarding Information',`prompt` json NULL COMMENT 'Agent Prompt Configuration',`plugin` json NULL COMMENT 'Agent Plugin Base Configuration',`knowledge` json NULL COMMENT 'Agent Knowledge Base Configuration',`workflow` json NULL COMMENT 'Agent Workflow Configuration',`suggest_reply` json NULL COMMENT 'Suggested Replies',`jump_config` json NULL COMMENT 'Jump Configuration',`background_image_info_list` json NULL COMMENT 'Background image',`database_config` json NULL COMMENT 'Agent Database Base Configuration',`bot_mode` tinyint NOT NULL DEFAULT 0 COMMENT 'bot mode,0:single mode 2:chatflow mode',`layout_info` text NULL COMMENT 'chatflow layout info',`shortcut_command` json NULL COMMENT 'shortcut command',PRIMARY KEY (`id`),INDEX `idx_creator_id` (`creator_id`),UNIQUE INDEX `uniq_agent_id` (`agent_id`)
) ENGINE=InnoDB CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT 'Single Agent Draft Copy Table';

project_draft ElasticSearch 索引结构

{"mappings": {"properties": {"id": {"type": "long","description": "项目唯一标识符"},"type": {"type": "integer","description": "智能体类型,枚举值:1=Bot, 2=Project"},"status": {"type": "integer","description": "项目状态,枚举值:1=Using(使用中), 2=Deleted(已删除), 3=Banned(已禁用), 4=MoveFailed(迁移失败), 5=Copying(复制中), 6=CopyFailed(复制失败)"},"name": {"type": "text","fields": {"raw": {"type": "keyword"}},"description": "项目名称"},"space_id": {"type": "long","description": "工作空间ID"},"owner_id": {"type": "long","description": "项目所有者ID"},"has_published": {"type": "integer","description": "是否已发布,0=未发布, 1=已发布"},"create_time": {"type": "long","description": "创建时间(毫秒时间戳)"},"update_time": {"type": "long","description": "更新时间(毫秒时间戳)"},"publish_time": {"type": "long","description": "发布时间(毫秒时间戳)"},"recently_open_time": {"type": "long","description": "最近打开时间(毫秒时间戳)"},"fav_time": {"type": "long","description": "收藏时间(毫秒时间戳)"},"is_fav": {"type": "integer","description": "是否收藏,0=未收藏, 1=已收藏"},"is_recently_open": {"type": "integer","description": "是否最近打开,0=否, 1=是"}}}
}

字段说明:
该索引主要用于存储项目草稿的元数据信息,支持以下功能:

  1. 基础信息:项目ID、名称、类型、状态
  2. 权限管理:工作空间ID、所有者ID
  3. 时间追踪:创建时间、更新时间、发布时间、最近打开时间、收藏时间
  4. 状态标记:发布状态、收藏状态、最近打开状态
  5. 搜索支持:项目名称支持全文搜索和精确匹配

该索引与 MySQL 中的 app_draft 表类似,但专门用于 ElasticSearch 的高效搜索和查询功能,支持复杂的搜索条件、排序和分页操作。

8. 安全和权限验证机制

用户身份验证流程

在项目开发功能中,系统需要验证用户身份以确保数据安全。整个身份验证流程如下:

  1. 会话验证:通过 ctxutil.GetUIDFromCtx(ctx) 从请求上下文中提取用户ID
  2. 工作空间隔离:确保用户只能访问所属工作空间的应用
  3. 所有者权限验证:验证用户对特定应用的所有权和操作权限
  4. 资源权限控制:验证用户对应用关联资源的访问权限

权限验证实现

文件位置:backend/application/app/app.go

核心代码:

// validateAPPPermission 验证用户对应用的操作权限
func (s *APPApplicationService) validateAPPPermission(ctx context.Context, appID int64, userID int64) error {// 获取应用信息app, err := s.appDomainSVC.GetDraftAPP(ctx, &service.GetDraftAPPRequest{APPID:   appID,OwnerID: userID,})if err != nil {return err}// 验证应用所有权if app.OwnerID != userID {return errors.New("permission denied: user does not own this app")}// 验证工作空间权限if !s.validateSpacePermission(ctx, userID, app.SpaceID) {return errors.New("permission denied: space access denied")}return nil
}// validateSpacePermission 验证工作空间权限
func (s *APPApplicationService) validateSpacePermission(ctx context.Context, userID, spaceID int64) bool {// 检查用户是否有访问该工作空间的权限return s.userSVC.HasSpaceAccess(ctx, userID, spaceID)
}func (s *APPApplicationService) DraftProjectUpdate(ctx context.Context, req *intelligence.DraftProjectUpdateRequest) (*intelligence.DraftProjectUpdateResponse, error) {resp := &intelligence.DraftProjectUpdateResponse{}userID := ctxutil.GetUIDFromCtx(ctx)// 验证操作权限err := s.validateAPPPermission(ctx, req.ProjectID, userID)if err != nil {logs.CtxErrorf(ctx, "validateAPPPermission failed, err=%v", err)return resp, err}// 执行更新操作app, err := s.appDomainSVC.UpdateDraftAPP(ctx, &service.UpdateDraftAPPRequest{APPID:       req.ProjectID,OwnerID:     userID,Name:        req.Name,Desc:        req.Description,IconURI:     req.IconURI,})if err != nil {logs.CtxErrorf(ctx, "appDomainSVC.UpdateDraftAPP failed, err=%v", err)return resp, err}resp.Project = s.convertToIntelligenceInfo(app)return resp, nil
}

安全机制特点

  1. 身份验证:每个请求都需要验证用户身份
  2. 权限隔离:用户只能操作自己的项目
  3. 操作审计:记录所有项目操作的日志
  4. 数据验证:对输入参数进行严格验证

9. 错误处理和日志记录

错误处理机制

在项目开发功能中,系统采用了分层错误处理机制:

  1. 参数验证错误:通过 invalidParamRequestResponse 处理参数绑定和验证错误,返回400状态码
  2. 权限验证错误:专门处理权限验证失败的错误,返回403状态码
  3. 资源不存在错误:处理项目不存在等资源错误,返回404状态码
  4. 业务逻辑错误:通过 internalServerErrorResponse 处理业务逻辑错误,返回422状态码
  5. 系统内部错误:处理数据库连接失败、第三方服务异常等,返回500状态码

日志记录机制

系统在关键节点记录日志,便于问题排查和系统监控:

  1. 操作日志:记录项目创建、更新、删除等操作
  2. 错误日志:记录错误信息和堆栈跟踪
  3. 性能日志:记录关键操作的执行时间
  4. 安全审计日志:记录权限验证和敏感操作

操作日志示例

// 应用创建成功日志
logs.CtxInfof(ctx, "DraftAPPCreate success, userID=%d, spaceID=%d, appID=%d, appName=%s", userID, req.SpaceID, app.ID, app.Name)// 应用发布成功日志
logs.CtxInfof(ctx, "PublishAPP success, userID=%d, appID=%d, version=%s, publishRecordID=%d", userID, req.APPID, req.Version, publishRecord.ID)

错误日志示例

// 权限验证失败日志
logs.CtxErrorf(ctx, "validateAPPPermission failed, userID=%d, appID=%d, err=%v", userID, req.APPID, err)// 业务逻辑错误日志
logs.CtxErrorf(ctx, "appDomainSVC.CreateDraftAPP failed, userID=%d, spaceID=%d, err=%v", userID, req.SpaceID, err)// 数据打包错误日志
logs.CtxErrorf(ctx, "packIntelligenceData failed, appID=%d, err=%v", app.ID, err)

性能监控日志示例

// 接口响应时间监控
start := time.Now()
defer func() {logs.CtxInfof(ctx, "GetDraftIntelligenceList completed, userID=%d, duration=%v, count=%d", userID, time.Since(start), len(result.List))
}()

日志特点

  1. 上下文关联:使用 CtxInfofCtxErrorf 记录带请求上下文的日志
  2. 结构化信息:包含用户ID、应用ID、工作空间ID等关键业务标识
  3. 操作追踪:记录完整的操作链路,便于问题排查和性能分析
  4. 错误详情:详细记录错误信息、参数和调用栈
  5. 业务监控:记录关键业务指标,支持运营分析和系统监控
  6. 安全审计:记录权限验证、敏感操作等安全相关事件

日志记录实现

文件位置:backend/api/handler/coze/intelligence_service.go

核心代码:

func DraftProjectCreate(ctx context.Context, c *app.RequestContext) {var err errorvar req intelligence.DraftProjectCreateRequest// 记录请求开始logs.CtxInfof(ctx, "DraftProjectCreate started, req=%v", req)err = c.BindAndValidate(&req)if err != nil {logs.CtxErrorf(ctx, "DraftProjectCreate bind failed, err=%v", err)invalidParamRequestResponse(c, err.Error())return}resp, err := appApplication.APPApplicationSVC.DraftProjectCreate(ctx, &req)if err != nil {logs.CtxErrorf(ctx, "APPApplicationSVC.DraftProjectCreate failed, err=%v", err)internalServerErrorResponse(ctx, c, err)return}// 记录操作成功logs.CtxInfof(ctx, "DraftProjectCreate success, projectID=%d", resp.ProjectID)c.JSON(consts.StatusOK, resp)
}

日志机制特点

  1. 上下文追踪:使用 logs.CtxInfoflogs.CtxErrorf 记录带上下文的日志
  2. 分级记录:根据日志级别记录不同重要程度的信息
  3. 结构化日志:使用结构化格式便于日志分析
  4. 敏感信息保护:避免在日志中记录敏感信息

10. 草稿应用列表查询流程图

GET /api/intelligence/draft/list?page=1&size=10↓
[API网关层] GetDraftIntelligenceList - 参数绑定和验证↓
[搜索服务层] SearchSVC.GetDraftIntelligenceList - 提取用户ID,构建查询条件↓
[领域服务层] AppService.SearchDraftAPPs - 执行分页查询↓
[数据库] SELECT * FROM app WHERE owner_id=? AND publish_status=0 ORDER BY updated_at_ms DESC LIMIT ?↓
[项目打包器] packIntelligenceData - 聚合应用信息、用户信息、资源信息↓
[响应返回] 返回打包后的应用列表

草稿应用列表查询详细处理流程

  • 用户进入项目开发页面
  • 前端发送GET请求到 /api/intelligence/draft/list
  • GetDraftIntelligenceList 处理器调用 SearchSVC.GetDraftIntelligenceList
  • 搜索服务根据 owner_idpublish_status=0 查询草稿应用列表
  • packIntelligenceData 聚合应用信息、用户信息、插件、知识库等资源信息
  • 返回完整的应用列表给前端

核心技术特点

1. 分层架构设计

  • 职责清晰:每层专注于特定的技术关注点
  • 松耦合:通过接口和依赖注入实现解耦
  • 可测试性:每层都可以独立进行单元测试
  • 可扩展性:新功能可以在不影响其他层的情况下添加

2. 事件驱动架构

  • 异步处理:通过事件总线实现异步操作
  • 解耦合:事件发布者和订阅者之间松耦合
  • 可扩展性:可以轻松添加新的事件处理器
  • 可靠性:事件处理失败不影响主流程

3. 领域驱动设计

  • 业务建模:通过领域实体和服务建模业务逻辑
  • 业务语言:使用业务术语命名类和方法
  • 业务规则:在领域层集中管理业务规则
  • 业务完整性:确保业务操作的原子性和一致性

4. 安全性保障

  • 身份验证:每个请求都需要验证用户身份
  • 权限控制:用户只能操作自己的项目
  • 数据验证:对所有输入进行严格验证
  • 操作审计:记录所有重要操作的日志

5. 高性能设计

  • 分页查询:支持高效的分页查询
  • 缓存策略:在适当的地方使用缓存提升性能
  • 异步处理:通过事件总线实现异步操作
  • 数据库优化:合理的索引设计和查询优化

6. 可维护性

  • 代码结构清晰:分层架构使代码结构清晰
  • 依赖注入:便于测试和维护
  • 错误处理:完善的错误处理机制
  • 日志记录:详细的日志记录便于问题排查

总结

Coze Studio的项目开发中的应用列表查询功能展现了现代Web应用后端架构的最佳实践:

  1. 清晰的分层架构:从API网关到数据存储,每层职责明确
  2. 事件驱动的设计:通过事件总线实现系统解耦和异步处理
  3. 领域驱动的建模:以业务为中心的领域建模和服务设计
  4. 完善的安全机制:用户身份验证和权限控制
  5. 高效的查询性能:优化的分页查询和数据聚合
  6. 可维护的代码结构:依赖注入和接口抽象提高可测试性

这种架构设计不仅保证了系统的性能和安全性,也为后续的功能扩展和维护奠定了坚实的基础。通过事件驱动架构,系统具备了良好的扩展性和可维护性,能够适应快速变化的业务需求。

http://www.xdnf.cn/news/19437.html

相关文章:

  • 传输层TCP 与 安全层SSL/TLS
  • shell之扩展
  • 接口自动化测试之设置断言思路
  • 什么是MIPS架构?RISC-V架构?有什么区别?【超详细初学者教程】
  • 深入Linux内核:IPC资源管理揭秘
  • 从 “对话” 到 “共创”:生成式 AI 如何重塑内容创作全流程,普通人也能掌握的高效工具指南
  • MongoDB 备份与恢复:mongodump 和 mongorestore 实战
  • Qt 的信号signal的参数是否会在内部被拷贝?
  • duilib中CTextUI控件使用技巧与问题总结(CTextUI控件自适应文字宽度特性)
  • 如何正确使用ChatGPT做数学建模比赛——数学建模AI使用技巧
  • 【macOS】垃圾箱中文件无法清理的“含特殊字符文件名”的方法
  • 开发使用mybatis是用混合模式还是全注解模式
  • 陕西凉拌西瓜皮,变废为宝的陕味美味~
  • JavaScript 性能优化实战技术
  • 【GIS图像处理】有哪些SOTA方法可以用于将1.5米分辨率遥感图像超分辨率至0.8米精度的?
  • mysql实例是什么?
  • Midscenejs自然语言写测试用例
  • 基于uni-app的校园综合服务平台开发实战
  • 大模型落地全流程实践:从技术选型到企业级部署
  • 警告:OPENCV_FFMPEG_READ_ATTEMPTS (current value is 4096)
  • flume接收处理器:构建高可用与高性能的数据链路
  • AR-LSAT 推理任务全解析:从逻辑推理到类比推理的挑战
  • Fabarta个人专属智能体赋能媒体:从过载信息到深度可控的创作体系
  • Claude AI 因编写勒索软件和开展勒索活动而被滥用
  • java基础1
  • DevExpress WinForms中文教程:Data Grid - 过滤编辑器
  • 【机器学习学习笔记】pandas基础
  • matlab-神经网络的语音识别
  • SHELL命令pr
  • 【C++】 Vector容器操作全解析