公司网站如何制作设计,手机网站 兼容,门户网站免费建设,wordpress 拍卖插件前言
本文将深入分析Coze Studio项目中用户登录后点击项目开发功能的后端实现#xff0c;通过源码解读来理解整个智能体项目管理系统的架构设计和技术实现。
项目架构概览
整体架构设计
Coze Studio后端采用了经典的分层架构模式#xff0c;将项目开发功能划分为…前言
本文将深入分析Coze Studio项目中用户登录后点击项目开发功能的后端实现通过源码解读来理解整个智能体项目管理系统的架构设计和技术实现。
项目架构概览
整体架构设计
Coze Studio后端采用了经典的分层架构模式将项目开发功能划分为以下几个核心层次
┌─────────────────────────────────────────────────────────────┐
│ IDL接口定义层 │
│ ┌─────────────┐ ┌───────────────── ┐ ┌─────────────┐ │
│ │ base.thrift │ │openapiauth.thrift│ │ api.thrift │ │
│ └─────────────┘ └───────────────── ┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│ API网关层 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Handler │ │ Router │ │ Middleware │ │
│ │ 处理器 │ │ 路由 │ │ 中间件 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│ 应用服务层 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ SearchApplicationService │ │
│ │ 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 mapstring,string Extra ,
}struct BaseResp {1: string StatusMessage ,2: i32 StatusCode 0 ,3: optional mapstring,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.categorysearch,agw.preserve_basetrue)项目开发查询结构体定义search.thrift
文件位置idl/app/search.thrift
请求结构体
struct GetDraftIntelligenceListRequest {1: required i64 space_id (agw.js_convstr, api.js_convtrue),2: optional string name,3: optional bool has_published,4: optional listintelligence_common_struct.IntelligenceStatus status,5: optional listintelligence_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: listIntelligenceData 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.nonetrue),
}项目开发查询公共结构体定义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_convstr, api.js_convtrue),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: listConnectorInfo 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_convstr, api.js_convtrue),2: string name,3: string description,4: string icon_uri,5: string icon_url,6: i64 space_id (agw.js_convstr, api.js_convtrue),7: i64 owner_id (agw.js_convstr, api.js_convtrue),8: i64 create_time (agw.js_convstr, api.js_convtrue),9: i64 update_time (agw.js_convstr, api.js_convtrue),10: IntelligenceStatus status,11: i64 publish_time (agw.js_convstr, api.js_convtrue),12: optional string enterprise_id,13: optional i64 organization_id,
}
IDL主API服务聚合文件api.thrift
文件位置idl/api.thrift
该文件是整个Coze项目的API服务聚合入口点负责将所有业务模块的IDL服务定义统一聚合为代码生成工具提供完整的服务接口定义。
核心代码include ./app/intelligence.thriftnamespace go coze// 项目开发核心服务聚合
service IntelligenceService extends intelligence.IntelligenceService {}
// 其他业务服务聚合
项目开发接口聚合说明
通过 service IntelligenceService extends intelligence.IntelligenceService {} 聚合定义api.thrift将intelligence.thrift中定义的所有项目开发相关接口统一暴露包括
文件作用
服务聚合中心: 统一管理所有业务模块的服务接口定义代码生成入口: 作为Hertz框架代码生成的主要入口文件接口统一暴露: 将分散在各个模块的接口统一暴露给客户端依赖管理: 通过include语句管理各模块间的依赖关系命名空间管理: 统一设置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,requiredName *string thrift:name,2,optional form:name json:name,omitempty query:nameHasPublished *bool thrift:has_published,3,optional form:has_published json:has_published,omitempty query:has_publishedStatus []common.IntelligenceStatus thrift:status,4,optional form:status json:status,omitempty query:statusTypes []common.IntelligenceType thrift:types,5,optional form:types json:types,omitempty query:typesSearchScope *SearchScope thrift:search_scope,6,optional form:search_scope json:search_scope,omitempty query:search_scopeIsFav *bool thrift:is_fav,51,optional form:is_fav json:is_fav,omitempty query:is_favRecentlyOpen *bool thrift:recently_open,52,optional form:recently_open json:recently_open,omitempty query:recently_openOption *GetDraftIntelligenceListOption thrift:option,99,optional form:option json:option,omitempty query:optionOrderBy *OrderBy thrift:order_by,100,optional form:order_by json:order_by,omitempty query:order_byCursorID *string thrift:cursor_id,101,optional form:cursor_id json:cursor_id,omitempty query:cursor_idSize *int32 thrift:size,102,optional form:size json:size,omitempty query:sizeBase *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:dataCode int32 thrift:code,253 form:code json:code query:codeMsg string thrift:msg,254 form:msg json:msg query:msgBaseResp *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)
}
实现功能
项目列表获取获取用户的草稿智能体列表支持分页和搜索
路由注册实现-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
}
文件作用
中间件函数定义为智能体项目开发模块的每个路由组和特定路由提供中间件挂载点路由层级管理按照路由的层级结构组织中间件函数支持三层中间件架构开发者扩展接口提供统一的接口供开发者添加自定义中间件逻辑如认证、鉴权、限流、日志记录等粒度化控制支持从模块级别到接口级别的细粒度中间件控制
API网关层Restful接口路由-CozeHertz
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. 应用服务层
SearchApplicationService初始化
文件位置backend/application/search/resource_search.go 和 backend/application/search/init.go
SearchApplicationService是搜索应用服务层的核心组件负责处理项目和资源的搜索、获取、收藏等业务逻辑是连接API层和领域层的重要桥梁。
服务结构定义
文件位置backend/application/search/resource_search.go
// SearchApplicationService 搜索应用服务处理项目和资源搜索的核心业务逻辑
var SearchSVC SearchApplicationService{}type SearchApplicationService struct {*ServiceComponents // 嵌入服务组件依赖DomainSVC search.Search // 搜索领域服务
}// 资源类型到默认图标的映射
var resType2iconURI map[common.ResType]string{common.ResType_Plugin: consts.DefaultPluginIcon,common.ResType_Workflow: consts.DefaultWorkflowIcon,common.ResType_Knowledge: consts.DefaultDatasetIcon,common.ResType_Prompt: consts.DefaultPromptIcon,common.ResType_Database: consts.DefaultDatabaseIcon,
}服务组件依赖
文件位置backend/application/search/init.go
// ServiceComponents 定义搜索服务所需的所有依赖组件
type ServiceComponents struct {DB *gorm.DB // 数据库连接Cache cache.Cmdable // 缓存服务TOS storage.Storage // 对象存储服务ESClient es.Client // Elasticsearch客户端ProjectEventBus ProjectEventBus // 项目事件总线ResourceEventBus ResourceEventBus // 资源事件总线SingleAgentDomainSVC singleagent.SingleAgent // 单智能体领域服务APPDomainSVC app.AppService // APP领域服务KnowledgeDomainSVC knowledge.Knowledge // 知识库领域服务PluginDomainSVC service.PluginService // 插件领域服务WorkflowDomainSVC workflow.Service // 工作流领域服务UserDomainSVC user.User // 用户领域服务ConnectorDomainSVC connector.Connector // 连接器领域服务PromptDomainSVC prompt.Prompt // 提示词领域服务DatabaseDomainSVC database.Database // 数据库领域服务
}服务初始化实现
文件位置backend/application/search/init.go
// InitService 初始化搜索应用服务注入所有依赖并设置消息队列消费者
func InitService(ctx context.Context, s *ServiceComponents) (*SearchApplicationService, error) {// 创建搜索领域服务searchDomainSVC : search.NewDomainService(ctx, s.ESClient)// 注入依赖到全局服务实例SearchSVC.DomainSVC searchDomainSVCSearchSVC.ServiceComponents s// 设置项目搜索消费者searchConsumer : search.NewProjectHandler(ctx, s.ESClient)logs.Infof(start search domain consumer...)nameServer : os.Getenv(consts.MQServer)// 注册项目事件消费者err : eventbus.DefaultSVC().RegisterConsumer(nameServer, consts.RMQTopicApp, consts.RMQConsumeGroupApp, searchConsumer)if err ! nil {return nil, fmt.Errorf(register search consumer failed, err%w, err)}// 设置资源搜索消费者searchResourceConsumer : search.NewResourceHandler(ctx, s.ESClient)// 注册资源事件消费者err eventbus.DefaultSVC().RegisterConsumer(nameServer, consts.RMQTopicResource, consts.RMQConsumeGroupResource, searchResourceConsumer)if err ! nil {return nil, fmt.Errorf(register search consumer failed, err%w, err)}return SearchSVC, nil
}// 事件总线类型别名
type (ResourceEventBus search.ResourceEventBusProjectEventBus search.ProjectEventBus
)// NewResourceEventBus 创建资源事件总线
func NewResourceEventBus(p eventbus.Producer) search.ResourceEventBus {return search.NewResourceEventBus(p)
}// NewProjectEventBus 创建项目事件总线
func NewProjectEventBus(p eventbus.Producer) search.ProjectEventBus {return search.NewProjectEventBus(p)
}服务初始化特点
依赖注入通过ServiceComponents结构体注入15个不同的领域服务实现完整的业务功能支持Elasticsearch集成使用ES客户端提供强大的全文搜索和索引功能事件驱动架构集成项目和资源事件总线支持异步事件处理和数据同步消息队列消费者自动注册项目和资源的MQ消费者实现实时数据更新多领域服务协调整合智能体、APP、知识库、插件、工作流等多个领域服务存储服务集成支持数据库持久化、缓存加速和对象存储
应用搜索服务核心实现
SearchApplicationService实现了搜索相关的核心业务逻辑主要包括项目搜索、资源搜索、收藏管理等功能。
项目列表获取功能
文件位置backend/application/search/project_search.go
// GetDraftIntelligenceList 获取草稿智能体项目列表
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))// 并发处理除第一个项目外的所有项目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 结构
主要处理步骤
基础信息构建
创建 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 (contextgithub.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 (contextstrconvgithub.com/bytedance/sonicmodel github.com/coze-dev/coze-studio/backend/api/model/crossdomain/searchsearchEntity github.com/coze-dev/coze-studio/backend/domain/search/entitygithub.com/coze-dev/coze-studio/backend/infra/contract/esgithub.com/coze-dev/coze-studio/backend/pkg/lang/convgithub.com/coze-dev/coze-studio/backend/pkg/lang/ptrgithub.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)}
领域服务特点
业务抽象定义了智能体应用开发的核心业务操作包括CRUD和发布管理状态管理管理应用从草稿到发布的状态转换和生命周期版本控制支持应用的版本管理和发布历史记录权限控制通过OwnerID确保用户只能操作自己的应用资源管理管理应用相关的资源如图标、配置等连接器集成支持应用发布时的连接器配置和管理
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:idSpaceID 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 // 发布时间(毫秒)
}
实体设计特点
状态管理通过PublishStatus字段管理草稿、发布成功、发布失败等状态工作空间隔离通过SpaceID实现多工作空间的数据隔离权限控制通过OwnerID确保应用的所有权管理版本管理支持应用的版本号和发布记录管理连接器集成通过ConnectorIDs字段管理应用的连接器配置时间追踪精确到毫秒的创建、更新、发布时间记录扩展信息通过PublishExtraInfo字段存储发布相关的额外信息
单Agent领域服务接口
文件位置backend\domain\agent\singleagent\service\single_agent.go
核心代码
package singleagentimport (contextgithub.com/cloudwego/eino/schemagithub.com/coze-dev/coze-studio/backend/api/model/playgroundgithub.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接口功能分析
接口职责根据应用ID获取草稿状态的智能体应用信息参数验证接收上下文和应用ID作为参数数据查询通过仓储层调用DAO层查询草稿应用数据存在性检查验证应用是否存在不存在时返回特定错误错误处理统一的错误处理和包装机制返回结果返回完整的APP实体对象
设计特点
分层架构严格遵循领域驱动设计服务层调用仓储层错误处理使用统一的错误包装机制提供清晰的错误信息数据完整性确保返回的应用数据完整且有效接口简洁接口设计简洁明了职责单一类型安全使用强类型参数和返回值确保类型安全
数据访问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
}代码功能
数据库查询 - 使用GORM查询框架从 AppDraft 表中根据ID查找记录错误处理 - 区分处理两种情况
记录不存在返回 (nil, false, nil)其他数据库错误返回 (nil, false, err)
数据转换 - 将数据库持久化对象PO转换为领域实体DO
appDraftPO(*res).ToDO() 执行PO到DO的转换
成功返回 - 找到记录时返回 (app, true, nil)
SingleAgentRepo仓储接口定义
文件位置backend\domain\agent\singleagent\repository\repository.go
package repositoryimport (contextgorm.io/gormgithub.com/coze-dev/coze-studio/backend/domain/agent/singleagent/entitygithub.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 (contextdatabase/sqlgorm.io/gormgorm.io/gengorm.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
}
代码作用
初始化管理
SetDefault() : 设置全局默认查询实例Use() : 创建新的查询实例初始化各表的查询器
数据库连接管理
Available() : 检查数据库连接是否可用clone() : 克隆查询实例ReadDB() / WriteDB() : 支持读写分离ReplaceDB() : 替换数据库连接
上下文支持
WithContext() : 为查询操作添加上下文返回带接口的查询上下文
事务管理
Transaction() : 执行事务操作Begin() : 开始事务返回 QueryTxQueryTx : 事务查询器支持 Commit、Rollback、SavePoint 等操作
这是典型的 Repository 模式 实现为 app 领域的数据访问提供了统一、类型安全的接口支持事务、读写分离等高级数据库功能。
AppRepo-app_draft数据查询
文件位置backend\domain\app\internal\dal\query\app_draft.gen.go
package queryimport (contextgorm.io/gormgorm.io/gorm/clausegorm.io/gorm/schemagorm.io/gengorm.io/gen/fieldgorm.io/plugin/dbresolvergithub.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 AppDrafts 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 (contextdatabase/sqlgorm.io/gormgorm.io/gengorm.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 (contextgorm.io/gormgorm.io/gorm/clausegorm.io/gorm/schemagorm.io/gengorm.io/gen/fieldgorm.io/plugin/dbresolvergithub.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_commongorm.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 SingleAgentDrafts 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 (fmtosgorm.io/driver/mysqlgorm.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 接口包含 Search、Create、Update、Delete、CreateIndex 等方法Types 接口定义属性类型创建方法BulkIndexer 接口批量操作接口model.go: 定义数据模型
Request搜索请求结构体包含查询条件、分页、排序等Response搜索响应结构体包含命中结果和元数据Hit单个搜索结果BulkIndexerItem批量操作项query.go: 定义查询相关结构
Query查询结构体支持多种查询类型QueryType 常量equal、match、multi_match、not_exists、contains、inBoolQuery布尔查询支持 must、should、filter、must_not各种查询构造函数NewEqualQuery、NewMatchQuery 等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查询执行流程
业务层调用backend/domain/search/service/search.go 中的 SearchProjects 方法构建查询创建 es.Request 对象设置查询条件、排序、分页等执行查询调用 s.esClient.Search(ctx, projectIndexName, searchReq)版本适配根据 ES_VERSION 环境变量自动选择 ES7 或 ES8 实现查询转换
ES7将抽象查询转换为 JSON 格式ES8将抽象查询转换为类型化结构体
结果处理将 ES 响应转换为统一的 Response 结构体
索引使用
项目索引projectIndexName project_draft 存储项目草稿信息资源索引resourceIndexName coze_resource 存储各类资源信息
设计优势
版本兼容同时支持 ES7 和 ES8通过环境变量切换接口统一业务代码无需关心具体 ES 版本类型安全ES8 使用类型化 API减少运行时错误查询抽象提供统一的查询构建方式支持复杂的布尔查询易于扩展新增查询类型只需在 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)
) ENGINEInnoDB DEFAULT CHARSETutf8mb4 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)
) ENGINEInnoDB 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: 智能体类型枚举值1Bot, 2Project},status: {type: integer,description: 项目状态枚举值1Using(使用中), 2Deleted(已删除), 3Banned(已禁用), 4MoveFailed(迁移失败), 5Copying(复制中), 6CopyFailed(复制失败)},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是}}}
}字段说明
该索引主要用于存储项目草稿的元数据信息支持以下功能
基础信息项目ID、名称、类型、状态权限管理工作空间ID、所有者ID时间追踪创建时间、更新时间、发布时间、最近打开时间、收藏时间状态标记发布状态、收藏状态、最近打开状态搜索支持项目名称支持全文搜索和精确匹配
该索引与 MySQL 中的 app_draft 表类似但专门用于 ElasticSearch 的高效搜索和查询功能支持复杂的搜索条件、排序和分页操作。
8. 安全和权限验证机制
用户身份验证流程
在项目开发功能中系统需要验证用户身份以确保数据安全。整个身份验证流程如下
会话验证通过 ctxutil.GetUIDFromCtx(ctx) 从请求上下文中提取用户ID工作空间隔离确保用户只能访问所属工作空间的应用所有者权限验证验证用户对特定应用的所有权和操作权限资源权限控制验证用户对应用关联资源的访问权限
权限验证实现
文件位置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
}安全机制特点
身份验证每个请求都需要验证用户身份权限隔离用户只能操作自己的项目操作审计记录所有项目操作的日志数据验证对输入参数进行严格验证
9. 错误处理和日志记录
错误处理机制
在项目开发功能中系统采用了分层错误处理机制
参数验证错误通过 invalidParamRequestResponse 处理参数绑定和验证错误返回400状态码权限验证错误专门处理权限验证失败的错误返回403状态码资源不存在错误处理项目不存在等资源错误返回404状态码业务逻辑错误通过 internalServerErrorResponse 处理业务逻辑错误返回422状态码系统内部错误处理数据库连接失败、第三方服务异常等返回500状态码
日志记录机制
系统在关键节点记录日志便于问题排查和系统监控
操作日志记录项目创建、更新、删除等操作错误日志记录错误信息和堆栈跟踪性能日志记录关键操作的执行时间安全审计日志记录权限验证和敏感操作
操作日志示例
// 应用创建成功日志
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))
}()日志特点
上下文关联使用 CtxInfof 和 CtxErrorf 记录带请求上下文的日志结构化信息包含用户ID、应用ID、工作空间ID等关键业务标识操作追踪记录完整的操作链路便于问题排查和性能分析错误详情详细记录错误信息、参数和调用栈业务监控记录关键业务指标支持运营分析和系统监控安全审计记录权限验证、敏感操作等安全相关事件
日志记录实现
文件位置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)
}日志机制特点
上下文追踪使用 logs.CtxInfof 和 logs.CtxErrorf 记录带上下文的日志分级记录根据日志级别记录不同重要程度的信息结构化日志使用结构化格式便于日志分析敏感信息保护避免在日志中记录敏感信息
10. 草稿应用列表查询流程图
GET /api/intelligence/draft/list?page1size10↓
[API网关层] GetDraftIntelligenceList - 参数绑定和验证↓
[搜索服务层] SearchSVC.GetDraftIntelligenceList - 提取用户ID构建查询条件↓
[领域服务层] AppService.SearchDraftAPPs - 执行分页查询↓
[数据库] SELECT * FROM app WHERE owner_id? AND publish_status0 ORDER BY updated_at_ms DESC LIMIT ?↓
[项目打包器] packIntelligenceData - 聚合应用信息、用户信息、资源信息↓
[响应返回] 返回打包后的应用列表草稿应用列表查询详细处理流程
用户进入项目开发页面前端发送GET请求到 /api/intelligence/draft/listGetDraftIntelligenceList 处理器调用 SearchSVC.GetDraftIntelligenceList搜索服务根据 owner_id 和 publish_status0 查询草稿应用列表packIntelligenceData 聚合应用信息、用户信息、插件、知识库等资源信息返回完整的应用列表给前端
核心技术特点
1. 分层架构设计
职责清晰每层专注于特定的技术关注点松耦合通过接口和依赖注入实现解耦可测试性每层都可以独立进行单元测试可扩展性新功能可以在不影响其他层的情况下添加
2. 事件驱动架构
异步处理通过事件总线实现异步操作解耦合事件发布者和订阅者之间松耦合可扩展性可以轻松添加新的事件处理器可靠性事件处理失败不影响主流程
3. 领域驱动设计
业务建模通过领域实体和服务建模业务逻辑业务语言使用业务术语命名类和方法业务规则在领域层集中管理业务规则业务完整性确保业务操作的原子性和一致性
4. 安全性保障
身份验证每个请求都需要验证用户身份权限控制用户只能操作自己的项目数据验证对所有输入进行严格验证操作审计记录所有重要操作的日志
5. 高性能设计
分页查询支持高效的分页查询缓存策略在适当的地方使用缓存提升性能异步处理通过事件总线实现异步操作数据库优化合理的索引设计和查询优化
6. 可维护性
代码结构清晰分层架构使代码结构清晰依赖注入便于测试和维护错误处理完善的错误处理机制日志记录详细的日志记录便于问题排查
总结
Coze Studio的项目开发中的应用列表查询功能展现了现代Web应用后端架构的最佳实践
清晰的分层架构从API网关到数据存储每层职责明确事件驱动的设计通过事件总线实现系统解耦和异步处理领域驱动的建模以业务为中心的领域建模和服务设计完善的安全机制用户身份验证和权限控制高效的查询性能优化的分页查询和数据聚合可维护的代码结构依赖注入和接口抽象提高可测试性
这种架构设计不仅保证了系统的性能和安全性也为后续的功能扩展和维护奠定了坚实的基础。通过事件驱动架构系统具备了良好的扩展性和可维护性能够适应快速变化的业务需求。