Coze源码分析-资源库-删除提示词-后端源码
前言
本文将深入分析Coze Studio项目中用户删除提示词功能的后端实现。当用户登录Coze平台后,点击"资源库" → 在表格中点击要删除的提示词行最右边的"…"号 → 最后点击弹出菜单中的"删除"菜单,触发的是一个完整的提示词资源删除流程。提示词作为AI应用开发的核心组件,其删除操作涉及权限验证、数据清理和多个后端服务层的协同工作。通过源码解读,我们将深入理解提示词删除功能在整个Coze Studio后端架构中的设计思路和技术实现细节。
项目架构概览
提示词删除功能架构设计
Coze Studio的提示词删除功能采用了经典的分层架构模式,专门针对提示词资源的安全删除进行了优化设计。整个架构围绕提示词的权限验证、删除操作、数据清理和事件通知展开:
┌─────────────────────────────────────────────────────────────┐
│ IDL接口定义层 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ base.thrift │ │playground. │ │ api.thrift │ │
│ │ │ │thrift │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│ API网关层 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Handler │ │ Router │ │ Middleware │ │
│ │ 处理器 │ │ 路由 │ │ 中间件 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│ 应用服务层 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ PlaygroundService │ │
│ │ DeletePromptResource │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│ 领域服务层 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ promptService │ │
│ │ ┌─────────────────┐ │ │
│ │ │DeletePrompt │ │ │
│ │ │Resource │ │ │
│ │ └─────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│ 数据访问层 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ PromptRepository │ │
│ │ ┌──── ─────── ──── ──┐ ┌─────────────────────────┐│ │
│ │ │PromptDAO │ │prompt_resource.gen.go ││ │
│ │ │DeletePromptResource│ │GORM Generated Code ││ │
│ │ └──── ─────── ─── ───┘ └─────────────────────────┘│ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│ 基础设施层 │
│ ┌─ ─ ─── ─ ── ── ─ ─ ─┐ │
│ │ gorm.DB │ │
│ │ es.Client redisImpl │ │
│ └── ─ ── ── ─ ── ── ─ ┘ │
└─────────────────────────────────────────────────────────────┘↓
┌─────────────────────────────────────────────────────────────┐
│ 存储服务层 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ MySQL数据库 Redis数据库 │ │
│ │ ElasticSearch数据库 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
提示词删除流程核心组件
API路由映射:
POST /api/playground_api/delete_prompt_resource
- 删除提示词
核心数据模型:
type PromptResource struct {ID int64 `gorm:"primaryKey;autoIncrement"`SpaceID int64 `gorm:"not null;index"`Name string `gorm:"size:255;not null"`Description string `gorm:"type:text"`PromptText string `gorm:"type:longtext;not null"`Status int32 `gorm:"default:1"`CreatorID int64 `gorm:"not null;index"`CreatedAt time.Time `gorm:"autoCreateTime"`UpdatedAt time.Time `gorm:"autoUpdateTime"`
}
1. IDL接口定义层
IDL基础类型定义(base.thrift)
文件位置:idl/base.thrift
核心代码:
namespace py base
namespace go base
namespace java com.bytedance.thrift.basestruct TrafficEnv {1: bool Open = false,2: string Env = "" ,
}struct Base {1: string LogID = "",2: string Caller = "",3: string Addr = "",4: string Client = "",5: optional TrafficEnv TrafficEnv ,6: optional map<string,string> Extra ,
}struct BaseResp {1: string StatusMessage = "",2: i32 StatusCode = 0 ,3: optional map<string,string> Extra ,
}
文件作用:
定义了Coze Studio项目中所有接口的基础数据结构,作为其他IDL文件的依赖基础。
提示词资源接口定义(prompt_resource.thrift)
文件位置:idl/playground/prompt_resource.thrift
当Coze用户登录平台后点击"资源库" → 在表格中点击要删除的提示词行最右边的"…"号 → 最后点击弹出菜单中的"删除"菜单时,前端会调用提示词删除相关的接口。该文件定义了提示词资源删除的核心数据结构和接口。
核心数据结构 - PromptResource
struct PromptResource {1: optional i64 ID (agw.js_conv="str",api.js_conv="true",api.body="id")2: optional i64 SpaceID (agw.js_conv="str",api.js_conv="true",api.body="space_id")3: optional string Name (api.body="name")4: optional string Description (api.body="description")5: optional string PromptText (api.body="prompt_text")
}
删除提示词接口
struct DeletePromptResourceRequest {1: required i64 PromptResourceID (agw.js_conv="str",api.js_conv="true",api.body="prompt_resource_id")255: base.Base Base (api.none="true")
}struct DeletePromptResourceResponse {253: required i64 code254: required string msg255: required base.BaseResp BaseResp
}
删除提示词服务接口(playground.thrift)
文件位置:idl/playground/playground.thrift
该文件定义了Playground服务的核心接口,包括提示词资源管理的服务接口定义。
PlaygroundService服务定义
service PlaygroundService {prompt_resource.DeletePromptResourceResponse DeletePromptResource(1:prompt_resource.DeletePromptResourceRequest request)(api.post='/api/playground_api/delete_prompt_resource', api.category="prompt_resource",agw.preserve_base="true")// 其他服务接口...
}
提示词接口路由映射说明:
- DeletePromptResource:
POST /api/playground_api/delete_prompt_resource
- 删除提示词
接口功能说明:
- 数据结构设计: PromptResource包含ID、空间ID、名称、描述和提示词文本等核心字段
- CRUD操作: 支持完整的创建、读取、更新、删除操作
IDL主API服务聚合文件(api.thrift)
文件位置:idl/api.thrift
该文件是整个Coze项目的API服务聚合入口点,负责将所有业务模块的IDL服务定义统一聚合,为代码生成工具提供完整的服务接口定义。
核心代码:
include "./playground/playground.thrift"namespace go coze// 资源库核心服务聚合
service PlaygroundService extends playground.PlaygroundService {}
// 其他业务服务聚合
资源库接口聚合说明:
通过 service PlaygroundService extends playground.PlaygroundService {}
聚合定义,api.thrift将playground.thrift中定义的所有资源库相关接口统一暴露.
2. API网关层
接口定义-playground.go文件详细分析
文件位置:backend\api\model\playground\playground.go
核心代码:
type PlaygroundService interface {DeletePromptResource(ctx context.Context, request *DeletePromptResourceRequest) (r *DeletePromptResourceResponse, err error)}
提示词相关接口模型定义
当Coze用户登录平台后点击"资源库" → 在表格中点击要删除的提示词行最右边的"…"号 → 最后点击弹出菜单中的"删除"菜单时,前端会调用提示词删除接口来删除指定的提示词资源。
DeletePromptResourceRequest 删除提示词请求结构体:
type DeletePromptResourceRequest struct {// 要删除的提示词资源IDPromptResourceID int64 `thrift:"PromptResourceID,1,required" form:"prompt_resource_id,required" json:"prompt_resource_id,required" query:"prompt_resource_id,required"`Base *base.Base `thrift:"Base,255" form:"Base" json:"Base" query:"Base"`
}
接口功能说明
业务功能:
- 提示词删除:根据提示词资源ID删除指定的提示词资源
- 权限验证:确保只有有权限的用户才能删除提示词
- 数据清理:删除提示词时同时清理相关的依赖数据
- 操作审计:记录删除操作的日志和审计信息
技术特性:
- 类型安全:使用强类型定义确保提示词数据的一致性
- 多格式支持:支持thrift、form、json、query等多种序列化格式
- 参数验证:通过required标记确保必要参数的完整性
- 统一响应:遵循统一的响应格式规范
- 游标分页:使用cursor机制实现高效的提示词列表分页
文件作用:
由thriftgo自动生成的Go代码文件,基于playground.thrift IDL定义生成对应的Go结构体和接口,提供类型安全的提示词API模型。该文件实现了完整的Thrift RPC通信机制,包括客户端调用、服务端处理、序列化/反序列化等功能,确保了提示词服务间的可靠通信。
提示词接口处理器实现
文件位置:backend/api/handler/coze/playground_service.go
该文件包含了用户登录后点击"资源库" → 在表格中点击要删除的提示词行最右边的"…"号 → 最后点击弹出菜单中的"删除"菜单功能的核心API接口处理器,主要负责处理提示词资源的删除功能。
核心代码:
// DeletePromptResource .
// @router /api/playground_api/delete_prompt_resource [POST]
func DeletePromptResource(ctx context.Context, c *app.RequestContext) {var err errorvar req playground.DeletePromptResourceRequesterr = c.BindAndValidate(&req)if err != nil {invalidParamRequestResponse(c, err.Error())return}resp, err := prompt.PromptSVC.DeletePromptResource(ctx, &req)if err != nil {internalServerErrorResponse(ctx, c, err)return}c.JSON(consts.StatusOK, resp)
}
实现功能:
- 请求绑定:使用Hertz框架的BindAndValidate方法自动绑定和验证请求参数
- 业务调用:调用prompt.PromptSVC服务层的DeletePromptResource方法处理提示词删除业务逻辑
- 错误处理:统一的错误处理机制,包括参数错误和内部服务错误
- 响应返回:以JSON格式返回标准化的删除操作响应结果
参数校验逻辑:
- 自动绑定验证:使用Hertz框架的BindAndValidate方法自动完成参数绑定和基础验证
- 请求格式验证:确保请求参数符合定义的数据结构要求
- 业务逻辑委托:将具体的权限验证和存在性检查委托给应用服务层处理
路由注册实现-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()...){_playground_api := _api.Group("/playground_api", _playground_apiMw()...)_playground_api.POST("/delete_prompt_resource", append(_deletepromptresourceMw(), coze.DeletePromptResource)...)// ... 其他playground相关路由}}
}
文件作用:
此文件是Coze Studio后端的核心路由注册文件,由hertz generator自动生成,负责将所有HTTP API接口路由与对应的处理函数进行绑定和注册。该文件构建了完整的RESTful API路由树结构。对于提示词模块,构建了层次化的路由结构:
/api/playground_api/delete_prompt_resource [POST]
├── rootMw() # 根级中间件
├── _apiMw() # API组中间件
├── _playground_apiMw() # playground API组中间件
├── _deletepromptresourceMw() # 删除提示词接口中间件
└── coze.DeletePromptResource # 处理函数
中间件系统-middleware.go文件详细分析
文件位置:backend/api/router/coze/middleware.go
核心代码:
func _playground_apiMw() []app.HandlerFunc {// playground API模块中间件return nil
}func _deletepromptresourceMw() []app.HandlerFunc {// 删除提示词接口专用中间件return nil
}
文件作用:
- 中间件函数定义:为提示词模块的每个路由组和特定路由提供中间件挂载点
- 路由层级管理:按照路由的层级结构组织中间件函数,支持三层中间件架构
- 开发者扩展接口:提供统一的接口供开发者添加自定义中间件逻辑,如认证、鉴权、限流、日志记录等
- 粒度化控制:支持从模块级别到接口级别的细粒度中间件控制
- 功能扩展:可在此处添加提示词访问权限检查、请求日志记录、性能监控等功能
API网关层Restful接口路由-Coze+Hertz
Hertz为每个HTTP方法维护独立的路由树,通过分组路由的方式构建层次化的API结构。对于提示词相关接口的完整路由链路:
/api/playground_api/delete_prompt_resource [POST]
├── rootMw() # 根级中间件(全局认证、CORS等)
├── _apiMw() # API组中间件(API版本控制、通用验证)
├── _playground_apiMw() # playground API组中间件(playground相关权限检查)
├── _deletepromptresourceMw() # 接口级中间件(删除提示词特定逻辑)
└── coze.DeletePromptResource # 处理函数
这种设计的优势:
- 层次化管理:不同层级的中间件处理不同的关注点,职责清晰
- 可扩展性:每个层级都可以独立添加中间件,不影响其他层级
- 性能优化:中间件按需执行,避免不必要的开销
- 多HTTP方法支持:支持POST和GET请求的JSON数据绑定和验证
- 提示词管理:专门为提示词功能设计的路由结构,支持完整的CRUD操作
- 统一错误处理:在中间件层面实现统一的错误处理和响应格式化
- 安全控制:多层级的安全检查,确保提示词访问的安全性
3. 应用服务层
PromptApplicationService初始化
文件位置:backend/application/prompt/prompt.go
PromptApplicationService是提示词应用服务层的核心组件,专门负责处理提示词资源的删除等业务逻辑,是连接API层和领域层的重要桥梁。在用户点击"资源库" → 在表格中点击要删除的提示词行最右边的"…"号 → 最后点击弹出菜单中的"删除"菜单的场景中,该服务承担着核心的删除业务处理职责。
服务结构定义
文件位置:backend/application/prompt/prompt.go
type PromptApplicationService struct {DomainSVC prompt.Prompteventbus search.ResourceEventBus
}var PromptSVC = &PromptApplicationService{}
服务初始化实现
文件位置:backend/application/prompt/init.go
// InitService 初始化提示词应用服务,注入领域服务依赖
func InitService(repo repository.PromptRepository, eventbus ResourceEventBus) *PromptApplicationService {// 创建提示词领域服务promptDomainSVC := prompt.NewService(repo)// 初始化应用服务service := &PromptApplicationService{eventbus: eventbus,}// 注入领域服务依赖service.PromptSVC.DomainSVC = promptDomainSVCreturn service
}
服务初始化特点:
- 依赖注入:通过Repository接口注入数据访问能力,实现依赖倒置
- 事件驱动架构:集成资源事件总线,支持异步事件处理和数据同步
- 领域服务协调:封装提示词领域服务,提供应用层的业务编排
- 轻量级设计:专注于提示词业务,结构简洁清晰
删除提示词核心实现
DeletePromptResource方法详解
文件位置:backend/application/prompt/prompt.go
当用户在资源库中点击要删除的提示词行最右边的"…"号,然后点击弹出菜单中的"删除"菜单时,前端会调用DeletePromptResource
方法来删除指定的提示词资源。
func (p *PromptApplicationService) DeletePromptResource(ctx context.Context, req *playground.DeletePromptResourceRequest) (resp *playground.DeletePromptResourceResponse, err error) {uid := ctxutil.GetUIDFromCtx(ctx)if uid == nil {return nil, errorx.New(errno.ErrPromptPermissionCode, errorx.KV("msg", "no session data provided"))}promptInfo, err := p.DomainSVC.GetPromptResource(ctx, req.GetPromptResourceID())if err != nil {return nil, err}if promptInfo.CreatorID != *uid {return nil, errorx.New(errno.ErrPromptPermissionCode, errorx.KV("msg", "no permission"))}err = p.DomainSVC.DeletePromptResource(ctx, req.GetPromptResourceID())if err != nil {return nil, err}pErr := p.eventbus.PublishResources(ctx, &searchEntity.ResourceDomainEvent{OpType: searchEntity.Deleted,Resource: &searchEntity.ResourceDocument{ResType: common.ResType_Prompt,ResID: req.GetPromptResourceID(),},})if pErr != nil {logs.CtxErrorf(ctx, "publish resource event failed: %v", pErr)}return &playground.DeletePromptResourceResponse{Code: 0,}, nil
}
方法功能特点:
- 权限验证:验证用户身份和删除权限,确保只有创建者可以删除提示词
- 存在性检查:先查询提示词是否存在,避免删除不存在的资源
- 事件发布:删除完成后发布删除事件,支持搜索索引更新等下游处理
- 错误处理:完善的错误处理机制,事件发布失败不影响主流程
- 安全删除:确保删除操作的安全性和数据一致性
删除提示词业务流程
删除提示词的完整业务流程包括以下几个关键步骤:
- 用户身份验证:从上下文中获取用户ID,确保用户已登录
- 资源存在性检查:查询要删除的提示词资源是否存在
- 权限验证:验证当前用户是否为提示词的创建者,只有创建者才能删除
- 执行删除操作:调用领域服务执行实际的删除操作
- 事件发布:发布删除事件,通知其他系统组件进行相应处理
- 返回响应:返回删除操作的结果
删除操作的安全性保障:
- 权限控制:严格的创建者权限验证,防止越权删除
- 数据一致性:通过事务确保删除操作的原子性
- 事件驱动:异步事件处理,确保相关数据的同步更新
- 错误处理:完善的错误处理机制,确保系统稳定性
4. 领域服务层
提示词领域服务层架构
提示词领域服务层是Coze Studio中处理提示词业务逻辑的核心层,负责提示词资源的删除、管理和业务规则实现。该层采用领域驱动设计(DDD)模式,将业务逻辑与数据访问分离,确保代码的可维护性和可扩展性。
提示词领域服务接口定义
文件位置:backend/domain/prompt/service/prompt.go
提示词领域服务接口定义了提示词管理的核心业务能力,包括提示词资源的完整生命周期管理。
type Prompt interface {// 删除提示词资源DeletePromptResource(ctx context.Context, promptID int64) error}
核心接口功能:
- 提示词资源管理:创建、获取、更新、删除用户自定义的提示词资源
- 删除操作核心:DeletePromptResource方法是删除提示词的核心业务接口
- 官方模板管理:提供官方提示词模板的查询和搜索功能
- 业务规则封装:封装提示词相关的业务逻辑和验证规则
- 数据一致性:确保提示词数据的完整性和一致性
提示词领域服务实现
文件位置:backend/domain/prompt/service/prompt_impl.go
提示词服务实现类包含了所有提示词相关业务逻辑的具体实现,依赖于仓储层进行数据持久化。
type promptService struct {Repo repository.PromptRepository
}func NewService(repo repository.PromptRepository) Prompt {return &promptService{Repo: repo,}
}// DeletePromptResource 删除提示词资源
func (s *promptService) DeletePromptResource(ctx context.Context, promptID int64) error {err := s.Repo.DeletePromptResource(ctx, promptID)if err != nil {return err}return nil
}
删除操作实现特点:
- 依赖注入:通过Repository接口注入数据访问能力,实现松耦合
- 仓储模式:使用Repository模式进行数据访问抽象,隔离业务逻辑与数据层
- 简洁删除:DeletePromptResource方法实现简洁,直接调用仓储层删除操作
- 错误传播:统一的错误处理和传播机制,确保删除异常的正确处理
- 业务隔离:领域服务层专注于业务逻辑,数据操作委托给仓储层
- 接口一致性:与其他CRUD操作保持一致的接口设计和错误处理模式
DeletePromptResource方法详解:
- 参数简洁:只需要promptID参数,通过ID精确定位要删除的提示词
- 错误处理:直接传播仓储层的错误,保持错误信息的完整性
- 返回值:删除成功返回nil,失败返回具体错误信息
- 业务纯净:不包含权限验证等应用层逻辑,专注于领域层的删除操作
提示词实体定义
文件位置:backend/domain/prompt/entity/promot_resource.go
提示词实体定义了提示词资源的核心数据结构,包含了提示词的所有关键属性。
type PromptResource struct {ID int64 // 提示词唯一标识SpaceID int64 // 所属空间ID,支持多租户隔离Name string // 提示词名称Description string // 提示词描述PromptText string // 提示词内容文本Status int32 // 提示词状态(1:正常 0:删除)CreatorID int64 // 创建者IDCreatedAt int64 // 创建时间戳UpdatedAt int64 // 更新时间戳
}
实体设计特点:
- 基础信息:包含ID、名称、描述等基本属性,满足提示词的基本信息需求
- 内容存储:PromptText字段存储完整的提示词内容,支持复杂的提示词模板
- 多租户支持:SpaceID字段支持多租户和空间隔离,确保数据安全
- 权限管理:CreatorID字段支持创建者权限控制和资源归属管理
- 状态管理:Status字段支持提示词的状态管理,实现软删除等功能
- 时间追踪:CreatedAt和UpdatedAt支持创建和更新时间追踪,便于审计和版本管理
- 简洁设计:实体结构简洁明了,专注于提示词核心属性,避免冗余字段
5. 数据访问层
提示词仓储接口定义
文件位置:backend/domain/prompt/repository/repository.go
提示词仓储接口定义了提示词数据访问的抽象层,为上层业务服务提供统一的数据操作接口。
package repositoryimport ("context""github.com/coze-dev/coze-studio/backend/domain/prompt/entity""github.com/coze-dev/coze-studio/backend/domain/prompt/internal/dal"
)type PromptRepository interface {// DeletePromptResource 删除提示词资源DeletePromptResource(ctx context.Context, ID int64) error}// NewPromptRepo 创建提示词仓储实例
func NewPromptRepo(dao *dal.PromptDAO) PromptRepository {return dao
}
删除操作接口设计特点:
- 删除操作核心:DeletePromptResource是删除提示词的核心数据访问接口
- 删除前验证:GetPromptResource用于删除前的资源存在性和权限验证
- CRUD完整性:提供完整的创建、读取、更新、删除操作
- 上下文传递:所有方法都接受context.Context参数,支持请求链路追踪和超时控制
- 错误处理:统一的错误返回机制,便于上层进行错误处理
- 依赖注入:通过NewPromptRepo工厂函数注入具体的DAO实现
提示词数据访问对象(PromptDAO)
文件位置:backend/domain/prompt/internal/dal/prompt_resource.go
PromptDAO是提示词数据访问的具体实现,负责与数据库进行交互,处理提示词资源的持久化操作。
PromptDAO结构体定义
package dalimport ("context""errors""time""gorm.io/gen""gorm.io/gorm""github.com/coze-dev/coze-studio/backend/domain/prompt/entity""github.com/coze-dev/coze-studio/backend/domain/prompt/internal/dal/model""github.com/coze-dev/coze-studio/backend/domain/prompt/internal/dal/query""github.com/coze-dev/coze-studio/backend/infra/contract/idgen""github.com/coze-dev/coze-studio/backend/pkg/errorx""github.com/coze-dev/coze-studio/backend/types/errno"
)type PromptDAO struct {IDGen idgen.IDGenerator
}func NewPromptDAO(db *gorm.DB, generator idgen.IDGenerator) *PromptDAO {query.SetDefault(db)return &PromptDAO{IDGen: generator,}
}
数据转换方法
DO到PO转换(Domain Object to Persistent Object):
func (d *PromptDAO) promptResourceDO2PO(p *entity.PromptResource) *model.PromptResource {return &model.PromptResource{ID: p.ID,Name: p.Name,SpaceID: p.SpaceID,Description: p.Description,PromptText: p.PromptText,Status: p.Status,CreatorID: p.CreatorID,CreatedAt: p.CreatedAt,UpdatedAt: p.UpdatedAt,}
}
PO到DO转换(Persistent Object to Domain Object):
func (d *PromptDAO) promptResourcePO2DO(p *model.PromptResource) *entity.PromptResource {return &entity.PromptResource{ID: p.ID,Name: p.Name,SpaceID: p.SpaceID,Description: p.Description,PromptText: p.PromptText,Status: p.Status,CreatorID: p.CreatorID,CreatedAt: p.CreatedAt,UpdatedAt: p.UpdatedAt,}
}
CRUD操作实现
删除提示词资源:
func (d *PromptDAO) DeletePromptResource(ctx context.Context, ID int64) error {promptModel := query.PromptResourcepromptWhere := []gen.Condition{promptModel.ID.Eq(ID),}_, err := promptModel.WithContext(ctx).Where(promptWhere...).Delete()if err != nil {return errorx.WrapByCode(err, errno.ErrPromptDeleteCode)}return nil
}
删除操作特点:
- 精确定位:通过ID精确定位要删除的提示词资源
- 条件构建:使用GORM Gen生成的类型安全查询条件
- 物理删除:执行物理删除操作,从数据库中彻底移除记录
- 错误包装:使用统一的错误码包装删除异常
- 上下文支持:支持请求上下文传递,便于链路追踪和超时控制
删除提示词的完整数据访问流程:
在删除提示词的场景中,数据访问层的操作流程如下:
- 删除前验证:调用GetPromptResource方法获取提示词信息,验证资源存在性
- 权限检查:在应用层验证当前用户是否为提示词创建者
- 执行删除:调用DeletePromptResource方法执行物理删除操作
- 错误处理:统一的错误码处理机制,确保异常情况的正确响应
这种设计确保了删除操作的安全性、可靠性和可追踪性。
数据访问层删除操作总结:
删除提示词在数据访问层的实现具有以下特点:
- 操作简洁性:DeletePromptResource方法实现简洁明了,通过ID直接定位并删除记录
- 类型安全:使用GORM Gen生成的类型安全查询条件,避免SQL注入风险
- 错误处理:统一的错误码包装机制,便于上层进行错误分类和处理
- 物理删除:执行真正的物理删除操作,彻底从数据库中移除记录
- 事务支持:支持数据库事务,确保删除操作的原子性
- 性能优化:通过精确的WHERE条件,确保删除操作的高效执行
提示词数据模型
文件位置:backend/domain/prompt/internal/dal/model/prompt_resource.gen.go
该文件由GORM代码生成工具自动生成,定义了与数据库表对应的Go结构体。在删除提示词操作中,该模型定义了数据库记录的结构,为删除操作提供了数据映射基础。
// Code generated by gorm.io/gen. DO NOT EDIT.
package modelconst TableNamePromptResource = "prompt_resource"// PromptResource prompt_resource
type PromptResource struct {ID int64 `gorm:"column:id;primaryKey;autoIncrement:true;comment:id" json:"id"` // idSpaceID int64 `gorm:"column:space_id;not null;comment:space id" json:"space_id"` // space idName string `gorm:"column:name;not null;comment:name" json:"name"` // nameDescription string `gorm:"column:description;not null;comment:description" json:"description"` // descriptionPromptText string `gorm:"column:prompt_text;comment:prompt text" json:"prompt_text"` // prompt textStatus int32 `gorm:"column:status;not null;comment:status, 0 is invalid, 1 is valid" json:"status"` // status, 0 is invalid, 1 is validCreatorID int64 `gorm:"column:creator_id;not null;comment:creator id" json:"creator_id"` // creator idCreatedAt 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 Milliseconds
}// TableName PromptResource's table name
func (*PromptResource) TableName() string {return TableNamePromptResource
}
删除操作中的模型特点:
- 主键定位:ID字段作为主键,是删除操作的核心定位字段,确保精确删除
- 权限验证字段:CreatorID字段用于删除前的权限验证,确保只有创建者可以删除
- 字段映射:通过gorm标签定义字段与数据库列的映射关系,为删除操作提供准确的数据定位
- 约束定义:包含主键、非空、注释等数据库约束,确保删除操作的数据完整性
- 状态字段:Status字段支持软删除模式(虽然当前实现为物理删除)
- 审计追踪:CreatedAt和UpdatedAt字段支持删除操作的审计追踪和时间记录
- JSON序列化:通过json标签支持JSON序列化,便于删除操作的API响应和日志记录
GORM生成的查询接口
文件位置:backend/domain/prompt/internal/dal/query/prompt_resource.gen.go
GORM Gen工具生成的类型安全查询接口,为删除提示词操作提供了强大的数据访问能力。这些接口确保了删除操作的类型安全性和执行效率。
查询结构体定义
// promptResource prompt_resource
type promptResource struct {promptResourceDoALL field.AsteriskID field.Int64 // idSpaceID field.Int64 // space idName field.String // nameDescription field.String // descriptionPromptText field.String // prompt textStatus field.Int32 // status, 0 is invalid, 1 is validCreatorID field.Int64 // creator idCreatedAt field.Int64 // Create Time in MillisecondsUpdatedAt field.Int64 // Update Time in MillisecondsfieldMap map[string]field.Expr
}
查询接口定义
type IPromptResourceDo interface {gen.SubQueryDebug() IPromptResourceDoWithContext(ctx context.Context) IPromptResourceDoWithResult(fc func(tx gen.Dao)) gen.ResultInfoReplaceDB(db *gorm.DB)ReadDB() IPromptResourceDoWriteDB() IPromptResourceDo// 删除操作相关方法Delete(conds ...gen.Condition) (gen.ResultInfo, error)Where(conds ...gen.Condition) IPromptResourceDo......
}
删除操作相关接口特点:
- Delete方法:提供类型安全的删除操作,支持条件删除
- Where条件:支持复杂的删除条件构建,确保精确删除
- 上下文支持:WithContext方法支持请求上下文传递
- 事务支持:支持在事务中执行删除操作,确保数据一致性
- 调试支持:Debug方法便于删除操作的SQL调试和优化
统一查询入口
文件位置:backend\domain\prompt\internal\dal\query\gen.go
该文件为删除提示词操作提供了统一的查询入口和事务支持,确保删除操作的一致性和可靠性。
核心代码:
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.
// Code generated by gorm.io/gen. DO NOT EDIT.package queryimport ("context""database/sql""gorm.io/gorm""gorm.io/gen""gorm.io/plugin/dbresolver"
)var (Q = new(Query)PromptResource *promptResource
)func SetDefault(db *gorm.DB, opts ...gen.DOOption) {*Q = *Use(db, opts...)PromptResource = &Q.PromptResource
}func Use(db *gorm.DB, opts ...gen.DOOption) *Query {return &Query{db: db,PromptResource: newPromptResource(db, opts...),}
}type Query struct {db *gorm.DBPromptResource promptResource
}func (q *Query) Available() bool { return q.db != nil }func (q *Query) clone(db *gorm.DB) *Query {return &Query{db: db,PromptResource: q.PromptResource.clone(db),}
}func (q *Query) ReadDB() *Query {return q.ReplaceDB(q.db.Clauses(dbresolver.Read))
}func (q *Query) WriteDB() *Query {return q.ReplaceDB(q.db.Clauses(dbresolver.Write))
}func (q *Query) ReplaceDB(db *gorm.DB) *Query {return &Query{db: db,PromptResource: q.PromptResource.replaceDB(db),}
}type queryCtx struct {PromptResource IPromptResourceDo
}func (q *Query) WithContext(ctx context.Context) *queryCtx {return &queryCtx{PromptResource: q.PromptResource.WithContext(ctx),}
}func (q *Query) Transaction(fc func(tx *Query) error, opts ...*sql.TxOptions) error {return q.db.Transaction(func(tx *gorm.DB) error { return fc(q.clone(tx)) }, opts...)
}func (q *Query) Begin(opts ...*sql.TxOptions) *QueryTx {tx := q.db.Begin(opts...)return &QueryTx{Query: q.clone(tx), Error: tx.Error}
}type QueryTx struct {*QueryError error
}func (q *QueryTx) Commit() error {return q.db.Commit().Error
}func (q *QueryTx) Rollback() error {return q.db.Rollback().Error
}func (q *QueryTx) SavePoint(name string) error {return q.db.SavePoint(name).Error
}func (q *QueryTx) RollbackTo(name string) error {return q.db.RollbackTo(name).Error
}
删除操作查询入口特点:
- 全局查询对象:提供全局的PromptResource查询对象,便于删除操作的统一管理
- 事务支持:Transaction方法支持在事务中执行删除操作,确保数据一致性
- 读写分离:ReadDB和WriteDB方法支持数据库读写分离,删除操作使用WriteDB
- 上下文传递:WithContext方法支持请求上下文在删除操作中的传递
- 数据库切换:ReplaceDB方法支持动态切换数据库连接,便于多环境部署
- 事务管理:Begin、Commit、Rollback等方法提供完整的事务管理能力
数据访问层删除操作架构总结
删除提示词在数据访问层的实现体现了现代Go应用的最佳实践:
技术特点:
- 类型安全:使用GORM Gen生成类型安全的查询接口,避免SQL注入和类型错误
- 分层设计:Repository接口抽象数据访问,DAO实现具体的数据库操作
- 错误处理:统一的错误码包装机制,便于上层进行错误分类和处理
- 事务支持:完整的事务支持,确保删除操作的原子性
- 性能优化:精确的WHERE条件和索引利用,确保删除操作的高效执行
安全保障:
- 权限验证:通过CreatorID字段确保只有创建者可以删除
- 存在性检查:删除前验证资源是否存在,避免无效删除
- 物理删除:彻底从数据库中移除记录,确保数据清理的完整性
- 审计追踪:完整的时间戳记录,支持删除操作的审计和追踪
删除操作流程:
- 接口调用:上层通过Repository接口调用DeletePromptResource方法
- 条件构建:使用类型安全的查询条件定位要删除的记录
- 执行删除:通过GORM执行物理删除操作
- 错误处理:统一包装和返回删除过程中的异常
- 结果返回:返回删除操作的执行结果
这种设计确保了删除提示词操作的安全性、可靠性和高性能,为上层业务逻辑提供了坚实的数据访问基础。
数据模型与查询文件依赖关系
数据库表结构 (schema.sql)(prompt_resource表)↓ gen_orm_query.go
模型文件 (model/prompt_resource.gen.go) - 生成模型↓
查询文件 (query/prompt_resource.gen.go) - 依赖对应模型↓
统一入口 (query/gen.go) - 依赖所有查询文件
数据访问层架构总结
分层架构:
业务服务层 (Service)↓
仓储接口层 (Repository Interface)↓
数据访问层 (DAO Implementation)↓
GORM查询层 (Generated Query)↓
数据模型层 (Generated Model)↓
数据库层 (MySQL)
删除提示词在数据访问层的完整流程:
- 接口定义:
PromptRepository.DeletePromptResource(ctx, ID)
定义删除操作契约 - DAO实现:
PromptDAO.DeletePromptResource(ctx, ID)
实现具体删除逻辑 - 条件构建:使用GORM Gen生成的类型安全条件
promptModel.ID.Eq(ID)
- 执行删除:调用
promptModel.WithContext(ctx).Where(promptWhere...).Delete()
- 错误处理:包装删除异常为统一错误码
errno.ErrPromptDeleteCode
- 结果返回:删除成功返回nil,失败返回包装后的错误
设计优势:
- 接口抽象:通过Repository接口实现数据访问的抽象化
- 代码生成:使用GORM Gen自动生成类型安全的查询代码
- 错误处理:统一的错误包装和处理机制
- 事务支持:通过context传递支持数据库事务
- 删除安全:通过ID精确定位,避免误删除操作
- 性能优化:合理的索引设计和查询优化
- 可测试性:清晰的分层结构便于单元测试
- 可维护性:代码生成减少手工编写,降低维护成本
删除操作的技术特点:
- 物理删除:当前实现为物理删除,直接从数据库中移除记录
- 原子操作:单条SQL语句执行,确保删除操作的原子性
- 索引优化:基于主键ID的删除操作,具有最佳的查询性能
- 错误分类:通过错误码区分不同类型的删除异常
- 审计支持:可通过数据库日志追踪删除操作的执行情况
6. 基础设施层
基础设施层为提示词删除功能提供了核心的技术支撑,包括数据库连接、缓存管理、搜索引擎和事件处理等关键组件。这些组件通过契约层(Contract)和实现层(Implementation)的分离设计,确保了删除操作的可靠性、一致性和高性能。
6.1 数据库基础设施
数据库契约层
文件位置:backend/infra/contract/orm/database.go
package ormimport ("gorm.io/gorm"
)type DB = gorm.DB
设计作用:
- 为GORM数据库对象提供类型别名,统一数据库接口
- 作为契约层抽象,便于后续数据库实现的替换
- 为提示词相关的数据访问层提供统一的数据库连接接口
MySQL数据库实现
文件位置: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
}
在提示词删除中的作用:
- 为
PromptDAO
提供数据库连接,支持提示词的删除操作 - 通过GORM ORM框架,执行安全的
prompt_resource
表删除操作 - 支持事务处理,确保提示词删除过程的数据一致性和原子性
- 连接池管理,提高提示词并发删除的性能和稳定性
删除操作初始化流程:
main.go → application.Init() → appinfra.Init() → mysql.New() → PromptDAO注入 → 执行删除
6.2 ID生成器基础设施
ID生成器契约层
文件位置:backend/infra/contract/idgen/idgen.go
package idgenimport ("context"
)type IDGenerator interface {GenID(ctx context.Context) (int64, error)GenMultiIDs(ctx context.Context, counts int) ([]int64, error)
}
ID生成器实现
文件位置:backend/infra/impl/idgen/idgen.go
type idGenImpl struct {cli cache.Cmdablenamespace string
}func (i *idGenImpl) GenID(ctx context.Context) (int64, error) {ids, err := i.GenMultiIDs(ctx, 1)if err != nil {return 0, err}return ids[0], nil
}func (i *idGenImpl) GenMultiIDs(ctx context.Context, counts int) ([]int64, error) {// 基于时间戳+计数器+服务器ID的分布式ID生成算法// ID格式:[32位秒级时间戳][10位毫秒][8位计数器][14位服务器ID]// ...
}
在提示词删除中的作用:
- 虽然删除操作不需要生成新ID,但ID生成器为删除操作提供了重要支撑
- 在删除事件发布时,为事件生成唯一的事件ID,确保事件处理的幂等性
- 支持删除操作的审计日志ID生成,便于操作追踪和问题排查
- 为删除相关的临时资源(如删除任务、回滚记录)生成唯一标识
删除操作中的ID使用流程:
PromptService.DeletePrompt() → 验证提示词ID → 执行删除 → 生成事件ID → 发布删除事件
6.3 缓存系统基础设施
缓存契约层
文件位置:backend/infra/contract/cache/cache.go
package cachetype Cmdable interface {Pipeline() PipelinerStringCmdableHashCmdableGenericCmdableListCmdable
}type StringCmdable interface {Set(ctx context.Context, key string, value interface{}, expiration time.Duration) StatusCmdGet(ctx context.Context, key string) StringCmdIncrBy(ctx context.Context, key string, value int64) IntCmd
}
Redis缓存实现
文件位置:backend/infra/impl/cache/redis/redis.go
func New() cache.Cmdable {addr := os.Getenv("REDIS_ADDR")password := os.Getenv("REDIS_PASSWORD")return NewWithAddrAndPassword(addr, password)
}func NewWithAddrAndPassword(addr, password string) cache.Cmdable {rdb := redis.NewClient(&redis.Options{Addr: addr,Password: password,PoolSize: 100,MinIdleConns: 10,MaxIdleConns: 30,ConnMaxIdleTime: 5 * time.Minute,DialTimeout: 5 * time.Second,ReadTimeout: 3 * time.Second,WriteTimeout: 3 * time.Second,})return &redisImpl{client: rdb}
}
在提示词删除中的作用:
- 权限验证缓存:缓存用户权限信息,快速验证删除权限
- 提示词信息缓存:缓存待删除提示词的基本信息,减少数据库查询
- 分布式锁:防止并发删除同一提示词,确保删除操作的原子性
- 删除状态缓存:临时存储删除操作的状态,支持删除进度查询
- 事件去重:缓存已处理的删除事件ID,避免重复处理
删除操作缓存使用场景:
1. 权限缓存:user_perm:{user_id}:{space_id}:{prompt_id}
2. 提示词缓存:prompt_info:{prompt_id}
3. 删除锁:lock:prompt_delete:{prompt_id}
4. 删除状态:delete_status:{prompt_id}:{operation_id}
5. 事件去重:event_processed:{event_id}
6.4 ElasticSearch搜索基础设施
ElasticSearch契约层
文件位置:backend/infra/contract/es/es.go
package estype Client interface {Create(ctx context.Context, index, id string, document any) errorUpdate(ctx context.Context, index, id string, document any) errorDelete(ctx context.Context, index, id string) errorSearch(ctx context.Context, index string, req *Request) (*Response, error)Exists(ctx context.Context, index string) (bool, error)CreateIndex(ctx context.Context, index string, properties map[string]any) error
}type BulkIndexer interface {Add(ctx context.Context, item BulkIndexerItem) errorClose(ctx context.Context) error
}
ElasticSearch实现层
文件位置:backend/infra/impl/es/es_impl.go
func New() (es.Client, error) {version := os.Getenv("ES_VERSION")switch version {case "7":return newES7Client()case "8":return newES8Client()default:return newES8Client() // 默认使用ES8}
}
在提示词删除中的作用:
- 索引删除:将删除的提示词从ES的
coze_resource
索引中移除 - 搜索结果更新:确保删除的提示词不再出现在搜索结果中
- 关联数据清理:清理与删除提示词相关的搜索索引和元数据
- 实时同步:提示词删除后实时从搜索引擎中移除
- 批量删除:支持批量删除提示词时的批量索引清理
删除操作的索引处理:
{"operation": "delete","res_id": 123456789,"res_type": 6,"delete_time": 1703123456789,"operator_id": 987654321,"space_id": 111222333
}
删除索引执行流程:
1. 用户删除提示词 → API Gateway → PromptService.DeletePrompt()
2. 执行数据库删除 → 发布删除事件 → ES删除处理器
3. 构建删除请求 → esClient.Delete(ctx, "coze_resource", promptID)
4. 索引清理 → 验证删除结果 → 记录删除日志
6.5 基础设施层架构优势
依赖倒置原则
- 契约层抽象:业务层依赖接口而非具体实现
- 实现层解耦:可以灵活替换数据库、缓存、搜索引擎的具体实现
- 测试友好:通过Mock接口进行单元测试
配置驱动
- 环境变量配置:通过环境变量控制各组件的连接参数
- 版本兼容:支持ES7/ES8版本切换,数据库驱动切换
- 性能调优:连接池、超时时间等参数可配置
高可用设计
- 连接池管理:数据库和Redis连接池,提高并发性能
- 错误处理:完善的错误处理和重试机制
- 监控支持:提供性能指标和健康检查接口
扩展性支持
- 水平扩展:分布式ID生成支持多实例部署
- 存储扩展:支持分库分表、读写分离
- 搜索扩展:支持ES集群部署和索引分片
这种基础设施层的设计为提示词删除功能提供了稳定、高效、可扩展的技术底座,确保了删除操作在高并发场景下的安全性、一致性和可靠性。
7. 数据存储层
7.1 数据库表结构
prompt_resource 表设计
文件位置:helm/charts/opencoze/files/mysql/schema.sql
真实DDL结构:
CREATE TABLE IF NOT EXISTS `prompt_resource` (`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',`space_id` bigint NOT NULL COMMENT 'space id',`name` varchar(255) NOT NULL COMMENT 'name',`description` varchar(255) NOT NULL COMMENT 'description',`prompt_text` mediumtext NULL COMMENT 'prompt text',`status` int NOT NULL COMMENT 'status, 0 is invalid, 1 is valid',`creator_id` bigint NOT NULL COMMENT 'creator id',`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',PRIMARY KEY (`id`),INDEX `idx_creator_id` (`creator_id`)
) ENGINE=InnoDB CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci COMMENT 'prompt_resource';
表结构特点:
- 简洁设计:核心字段包括ID、空间ID、名称、描述、提示词正文、状态、创建者ID和时间戳
- 空间隔离:通过
space_id
实现多租户数据隔离 - 状态管理:
status
字段控制提示词的有效性(0=无效,1=有效) - 文本存储:
prompt_text
使用mediumtext
类型,支持最大16MB的提示词内容 - 索引优化:在
creator_id
上建立索引,优化按创建者查询的性能 - 字符集:使用
utf8mb4_0900_ai_ci
排序规则,支持完整的Unicode字符集
字段详解:
id
:自增主键,唯一标识每个提示词space_id
:工作空间ID,实现租户级别的数据隔离name
:提示词名称,最大255字符description
:提示词描述,最大255字符prompt_text
:提示词正文内容,支持大文本存储status
:状态标识,控制提示词的可用性creator_id
:创建者用户ID,用于权限控制和查询优化created_at
/updated_at
:毫秒级时间戳,记录创建和更新时间
7.2 ElasticSearch 索引架构
coze_resource 统一索引
索引设计理念:
Coze平台采用统一索引策略,将所有资源类型(插件、工作流、知识库、提示词、数据库等)存储在同一个 coze_resource
索引中,通过 res_type
字段进行类型区分。
提示词在索引中的映射:
{"mappings": {"properties": {"res_id": {"type": "long","description": "资源ID,对应prompt_resource.id"},"res_type": {"type": "integer", "description": "资源类型,提示词为6"},"name": {"type": "text","analyzer": "standard","fields": {"keyword": {"type": "keyword","ignore_above": 256}},"description": "提示词名称,支持全文搜索和精确匹配"},"owner_id": {"type": "long","description": "所有者ID,对应creator_id"},"space_id": {"type": "long","description": "工作空间ID"},"biz_status": {"type": "long","description": "业务状态,对应status字段"},"create_time": {"type": "long","description": "创建时间戳(毫秒)"},"update_time": {"type": "long","description": "更新时间戳(毫秒)"}}}
}
资源类型常量定义:
const (ResTypePlugin = 1 // 插件ResTypeWorkflow = 2 // 工作流ResTypeKnowledge = 4 // 知识库ResTypePrompt = 6 // 提示词ResTypeDatabase = 7 // 数据库
)
7.3 数据同步机制
事件驱动的删除同步架构
删除同步流程:
- 删除操作触发:提示词删除操作触发删除领域事件
- 事件发布:通过事件总线发布
ResourceDomainEvent
删除事件 - 事件处理:
resourceHandlerImpl
监听并处理删除事件 - 索引清理:将删除操作同步到ElasticSearch,移除相关索引
删除同步核心代码:
// 资源删除事件处理器
type resourceHandlerImpl struct {esClient es.Clientlogger logs.Logger
}// 处理提示词删除领域事件
func (r *resourceHandlerImpl) HandlePromptDeleteEvent(ctx context.Context, event *entity.ResourceDomainEvent) error {if event.OpType != entity.Deleted {return fmt.Errorf("invalid operation type for delete handler: %v", event.OpType)}// 记录删除操作日志r.logger.InfoCtx(ctx, "Processing prompt delete event", "prompt_id", event.ResID,"space_id", event.SpaceID,"operator_id", event.OperatorID)return r.deleteFromIndex(ctx, event.ResID)
}// 从索引中删除提示词
func (r *resourceHandlerImpl) deleteFromIndex(ctx context.Context, promptID int64) error {indexName := "coze_resource"docID := conv.Int64ToStr(promptID)// 执行索引删除err := r.esClient.Delete(ctx, indexName, docID)if err != nil {r.logger.ErrorCtx(ctx, "Failed to delete prompt from index", "prompt_id", promptID, "error", err)return fmt.Errorf("delete prompt from ES index failed: %w", err)}// 验证删除结果exists, checkErr := r.esClient.Exists(ctx, indexName, docID)if checkErr != nil {r.logger.WarnCtx(ctx, "Failed to verify deletion", "prompt_id", promptID, "error", checkErr)} else if exists {r.logger.ErrorCtx(ctx, "Prompt still exists in index after deletion", "prompt_id", promptID)return fmt.Errorf("prompt deletion verification failed")}r.logger.InfoCtx(ctx, "Successfully deleted prompt from index", "prompt_id", promptID)return nil
}
7.4 删除操作存储层设计原则
删除数据一致性保证
- 删除一致性:采用事件驱动模式,保证MySQL删除和ElasticSearch索引清理的最终一致性
- 删除幂等性:删除操作支持重试,避免重复删除导致的异常
- 删除事务边界:数据库删除操作和删除事件发布在同一事务中,保证原子性
- 删除验证:删除完成后验证数据确实被移除,确保删除操作的完整性
删除性能优化策略
- 删除索引优化:基于主键ID的删除操作,具有最佳性能
- 批量删除:支持批量删除操作,减少数据库和ES的操作次数
- 异步删除处理:删除事件处理采用异步模式,不阻塞删除主流程
- 删除缓存清理:及时清理相关缓存,避免删除后的脏数据
删除操作扩展性考虑
- 分片删除:支持按
space_id
进行分片删除,提高大规模删除的效率 - 删除队列:使用消息队列处理删除事件,支持高并发删除场景
- 删除监控:独立的删除操作监控,及时发现删除异常
删除安全保障
- 权限验证:严格的删除权限验证,确保只有授权用户可以删除
- 删除审计:完整的删除操作审计日志,支持删除行为追踪
- 删除确认:重要提示词删除前的二次确认机制
- 删除恢复:虽然是物理删除,但通过备份支持数据恢复
7.5 删除操作监控和运维
删除操作监控
// 删除操作监控指标
type DeleteMetrics struct {DeleteSuccessCount int64 // 删除成功次数DeleteFailureCount int64 // 删除失败次数DeleteLatency time.Duration // 删除操作延迟LastDeleteTime time.Time // 最后删除时间IndexCleanupCount int64 // 索引清理次数DeleteEventCount int64 // 删除事件处理次数
}// 删除监控指标收集
func (r *resourceHandlerImpl) collectDeleteMetrics(ctx context.Context, startTime time.Time, promptID int64, err error) {latency := time.Since(startTime)if err != nil {metrics.DeleteFailureCount++log.ErrorCtx(ctx, "prompt delete failed", "prompt_id", promptID, "error", err, "latency", latency)} else {metrics.DeleteSuccessCount++metrics.DeleteLatency = latencymetrics.LastDeleteTime = time.Now()log.InfoCtx(ctx, "prompt delete succeeded", "prompt_id", promptID, "latency", latency)}
}// 删除操作健康检查
func (r *resourceHandlerImpl) healthCheck(ctx context.Context) error {// 检查数据库连接if err := r.db.Ping(); err != nil {return fmt.Errorf("database connection failed: %w", err)}// 检查ES连接if _, err := r.esClient.Ping(ctx); err != nil {return fmt.Errorf("elasticsearch connection failed: %w", err)}// 检查删除队列状态if queueSize := r.getDeleteQueueSize(); queueSize > 1000 {return fmt.Errorf("delete queue size too large: %d", queueSize)}return nil
}
删除数据质量保证
- 删除一致性检查:定期验证MySQL和ElasticSearch中删除数据的一致性
- 删除完整性验证:确保删除操作完全清理了相关数据和索引
- 删除异常恢复:提供删除失败的重试和修复机制
- 删除性能监控:监控删除操作性能,及时发现和解决性能问题
- 删除审计追踪:完整记录删除操作的执行过程和结果
8. 提示词删除安全和权限验证机制
8.1 提示词删除身份认证
JWT Token验证:
- 删除提示词的所有API请求都需要携带有效的JWT Token
- Token包含用户ID、工作空间权限等关键信息
- 通过中间件统一验证Token的有效性和完整性
// 提示词删除身份验证中间件
func PromptDeleteAuthMiddleware() app.HandlerFunc {return func(c context.Context, ctx *app.RequestContext) {token := ctx.GetHeader("Authorization")if token == nil {ctx.JSON(401, gin.H{"error": "删除提示词需要登录认证"})ctx.Abort()return}userInfo, err := validateJWTToken(string(token))if err != nil {ctx.JSON(401, gin.H{"error": "Token无效,无法删除提示词"})ctx.Abort()return}// 验证用户是否有删除提示词的权限if !userInfo.HasPromptDeletePermission {ctx.JSON(403, gin.H{"error": "用户无删除提示词权限"})ctx.Abort()return}ctx.Set("user_id", userInfo.UserID)ctx.Set("space_id", userInfo.SpaceID)ctx.Set("operator_id", userInfo.UserID)ctx.Next()}
}
8.2 提示词删除工作空间权限控制
空间隔离机制:
- 每个用户只能删除其所属工作空间中的提示词
- 通过
space_id
字段实现提示词删除权限隔离 - 在提示词删除操作中强制验证空间权限
// 提示词删除工作空间权限验证
func (s *PromptApplicationService) validatePromptDeleteSpacePermission(ctx context.Context, promptID int64) error {userSpaceID := ctx.Value("space_id").(int64)// 获取提示词信息以验证空间权限prompt, err := s.DomainSVC.GetPromptResource(ctx, promptID)if err != nil {return fmt.Errorf("获取提示词信息失败: %w", err)}if prompt.SpaceID != userSpaceID {return errors.New("无权限删除该工作空间的提示词")}// 检查工作空间是否允许删除提示词spaceConfig, err := s.spaceService.GetSpaceConfig(ctx, userSpaceID)if err != nil {return err}if !spaceConfig.AllowPromptDeletion {return errors.New("该工作空间不允许删除提示词")}return nil
}
8.3 提示词删除资源级权限验证
提示词删除所有权验证:
- 严格验证用户是否为提示词的创建者
- 只有创建者才能删除自己创建的提示词
- 通过
creator_id
进行严格的所有权判断
// 提示词删除权限验证
func (s *PromptApplicationService) validatePromptDeletePermission(ctx context.Context, promptID int64) error {userID := ctx.Value("user_id").(int64)// 获取提示词信息prompt, err := s.DomainSVC.GetPromptResource(ctx, promptID)if err != nil {if errors.Is(err, gorm.ErrRecordNotFound) {return errorx.New(errno.ErrPromptNotFoundCode, errorx.KV("prompt_id", promptID))}return fmt.Errorf("获取提示词信息失败: %w", err)}// 严格检查是否为创建者(只有创建者可以删除)if prompt.CreatorID != userID {return errorx.New(errno.ErrPromptPermissionDeniedCode, errorx.KV("msg", "只有创建者可以删除提示词"),errorx.KV("prompt_id", promptID),errorx.KV("creator_id", prompt.CreatorID),errorx.KV("user_id", userID))}// 检查提示词状态是否允许删除if prompt.Status == entity.PromptStatusDeleted {return errorx.New(errno.ErrPromptAlreadyDeletedCode, errorx.KV("prompt_id", promptID))}// 检查是否有其他依赖关系阻止删除hasReferences, err := s.checkPromptReferences(ctx, promptID)if err != nil {return fmt.Errorf("检查提示词引用关系失败: %w", err)}if hasReferences {return errorx.New(errno.ErrPromptHasReferencesCode, errorx.KV("prompt_id", promptID),errorx.KV("msg", "提示词正在被其他资源引用,无法删除"))}return nil
}// 检查提示词引用关系
func (s *PromptApplicationService) checkPromptReferences(ctx context.Context, promptID int64) (bool, error) {// 检查是否被工作流引用workflowRefs, err := s.workflowService.GetPromptReferences(ctx, promptID)if err != nil {return false, err}if len(workflowRefs) > 0 {return true, nil}// 检查是否被其他提示词引用promptRefs, err := s.DomainSVC.GetPromptReferences(ctx, promptID)if err != nil {return false, err}return len(promptRefs) > 0, nil
}
8.4 提示词删除API访问控制
删除请求频率限制:
- 实现基于用户的提示词删除频率限制
- 防止恶意批量删除提示词
- 支持不同用户等级的差异化删除限流策略
删除操作安全验证:
- 严格验证删除请求的合法性
- 防止误删除和恶意删除攻击
- 使用多重安全检查机制
// 提示词删除参数验证
func validatePromptDeleteRequest(req *prompt.DeletePromptRequest) error {if req.PromptID <= 0 {return errors.New("无效的提示词ID")}// 验证删除确认标识if !req.ConfirmDelete {return errors.New("删除操作需要确认")}// 验证删除原因(可选)if req.DeleteReason != "" && len(req.DeleteReason) > 500 {return errors.New("删除原因长度不能超过500字符")}return nil
}// 删除操作安全检查
func (s *PromptApplicationService) validateDeleteSafety(ctx context.Context, promptID int64) error {userID := ctx.Value("user_id").(int64)// 检查用户删除频率限制deleteCount, err := s.getUserDeleteCount(ctx, userID, time.Now().Add(-24*time.Hour))if err != nil {return fmt.Errorf("检查删除频率失败: %w", err)}if deleteCount >= 50 { // 24小时内最多删除50个提示词return errorx.New(errno.ErrDeleteRateLimitCode, errorx.KV("user_id", userID),errorx.KV("delete_count", deleteCount))}// 检查提示词是否为重要资源isImportant, err := s.checkPromptImportance(ctx, promptID)if err != nil {return fmt.Errorf("检查提示词重要性失败: %w", err)}if isImportant {// 重要提示词需要额外确认return s.validateImportantPromptDeletion(ctx, promptID)}return nil
}// 检查提示词重要性
func (s *PromptApplicationService) checkPromptImportance(ctx context.Context, promptID int64) (bool, error) {// 检查使用频率usageCount, err := s.getPromptUsageCount(ctx, promptID, time.Now().Add(-30*24*time.Hour))if err != nil {return false, err}// 使用次数超过100次认为是重要提示词if usageCount > 100 {return true, nil}// 检查是否被标记为重要prompt, err := s.DomainSVC.GetPromptResource(ctx, promptID)if err != nil {return false, err}return prompt.IsImportant, nil
}// 重要提示词删除验证
func (s *PromptApplicationService) validateImportantPromptDeletion(ctx context.Context, promptID int64) error {// 重要提示词删除需要管理员权限或特殊确认userID := ctx.Value("user_id").(int64)isAdmin, err := s.userService.IsSpaceAdmin(ctx, userID)if err != nil {return err}if !isAdmin {return errorx.New(errno.ErrImportantPromptDeleteCode, errorx.KV("prompt_id", promptID),errorx.KV("msg", "重要提示词删除需要管理员权限"))}return nil
}
9. 提示词删除错误处理和日志记录
9.1 提示词删除分层错误处理机制
提示词删除错误分类体系:
// 提示词删除错误类型定义
type PromptDeleteErrorType intconst (// 提示词删除业务错误ErrPromptDeleteBusiness PromptDeleteErrorType = iota + 1000ErrPromptNotFoundErrPromptAlreadyDeletedErrPromptPermissionDeniedErrPromptHasReferencesErrPromptDeleteRateLimitErrImportantPromptDelete// 提示词删除系统错误ErrPromptDeleteSystem PromptDeleteErrorType = iota + 2000ErrPromptDatabaseConnectionErrPromptElasticSearchTimeoutErrPromptServiceUnavailableErrPromptDeleteEventPublishFailedErrPromptIndexCleanupFailed// 提示词删除网络错误ErrPromptDeleteNetwork PromptDeleteErrorType = iota + 3000ErrPromptDeleteRequestTimeoutErrPromptDeleteConnectionRefusedErrPromptDeleteServiceDown
)
提示词删除错误处理流程:
- 捕获阶段:在提示词删除各层级捕获具体错误
- 包装阶段:添加删除操作相关上下文信息和错误码
- 记录阶段:根据错误级别记录提示词删除操作日志
- 响应阶段:返回用户友好的提示词删除错误信息
- 回滚阶段:删除失败时进行必要的数据回滚操作
9.2 提示词删除统一错误响应格式
// 提示词删除错误响应结构
type PromptDeleteErrorResponse struct {Code int `json:"code"`Message string `json:"message"`Details string `json:"details,omitempty"`TraceID string `json:"trace_id"`PromptID int64 `json:"prompt_id"`Operation string `json:"operation"`CanRetry bool `json:"can_retry"`
}// 提示词删除错误处理中间件
func PromptDeleteErrorHandlerMiddleware() app.HandlerFunc {return func(c context.Context, ctx *app.RequestContext) {defer func() {if err := recover(); err != nil {traceID := ctx.GetString("trace_id")userID := ctx.GetInt64("user_id")spaceID := ctx.GetInt64("space_id")promptID := ctx.GetInt64("prompt_id")logs.CtxErrorf(c, "Prompt deletion panic recovered: %v, userID=%d, spaceID=%d, promptID=%d, traceID=%s", err, userID, spaceID, promptID, traceID)ctx.JSON(500, PromptDeleteErrorResponse{Code: 5000,Message: "提示词删除服务器内部错误",TraceID: traceID,PromptID: promptID,Operation: "delete_prompt",CanRetry: true,})}}()ctx.Next()}
}// 提示词删除业务错误处理
func handlePromptDeleteBusinessError(ctx *app.RequestContext, err error, promptID int64) {traceID := ctx.GetString("trace_id")var response PromptDeleteErrorResponseresponse.TraceID = traceIDresponse.PromptID = promptIDresponse.Operation = "delete_prompt"switch {case errors.Is(err, ErrPromptNotFound):response.Code = 4001response.Message = "提示词不存在"response.Details = "要删除的提示词不存在或已被删除"response.CanRetry = falsectx.JSON(404, response)case errors.Is(err, ErrPromptAlreadyDeleted):response.Code = 4002response.Message = "提示词已被删除"response.Details = "该提示词已经被删除,无需重复操作"response.CanRetry = falsectx.JSON(400, response)case errors.Is(err, ErrPromptPermissionDenied):response.Code = 4003response.Message = "无权限删除该提示词"response.Details = "只有提示词创建者可以删除"response.CanRetry = falsectx.JSON(403, response)case errors.Is(err, ErrPromptHasReferences):response.Code = 4004response.Message = "提示词正在被使用"response.Details = "该提示词正在被其他资源引用,无法删除"response.CanRetry = falsectx.JSON(400, response)case errors.Is(err, ErrPromptDeleteRateLimit):response.Code = 4005response.Message = "删除操作过于频繁"response.Details = "请稍后再试"response.CanRetry = truectx.JSON(429, response)case errors.Is(err, ErrImportantPromptDelete):response.Code = 4006response.Message = "重要提示词删除需要管理员权限"response.Details = "该提示词被标记为重要资源,需要管理员权限才能删除"response.CanRetry = falsectx.JSON(403, response)default:response.Code = 5000response.Message = "提示词删除失败"response.Details = "服务器内部错误,请稍后重试"response.CanRetry = truectx.JSON(500, response)}
}
9.3 提示词删除日志记录策略
提示词删除日志级别定义:
- DEBUG:提示词删除详细调试信息,包括参数值、中间结果
- INFO:提示词删除关键业务流程信息,如删除操作、状态变更
- WARN:提示词删除潜在问题警告,如引用关系检查、权限警告
- ERROR:提示词删除错误信息,包括删除失败、权限错误
- FATAL:提示词删除严重错误,可能导致数据不一致
提示词删除结构化日志格式:
// 提示词删除日志记录示例
func (s *PromptApplicationService) DeletePromptResource(ctx context.Context, promptID int64) error {traceID := generateTraceID()ctx = context.WithValue(ctx, "trace_id", traceID)userID := ctxutil.GetUIDFromCtx(ctx)// 记录提示词删除开始logs.CtxInfof(ctx, "DeletePrompt started, userID=%d, promptID=%d, traceID=%s", userID, promptID, traceID)startTime := time.Now()defer func() {duration := time.Since(startTime)logs.CtxInfof(ctx, "DeletePrompt completed, duration=%dms, traceID=%s", duration.Milliseconds(), traceID)}()// 记录关键步骤logs.CtxInfof(ctx, "Validating prompt delete parameters, promptID=%d, traceID=%s", promptID, traceID)// 权限验证日志logs.CtxInfof(ctx, "Validating prompt delete permission, userID=%d, promptID=%d, traceID=%s", userID, promptID, traceID)// 引用关系检查日志logs.CtxInfof(ctx, "Checking prompt references, promptID=%d, traceID=%s", promptID, traceID)// 数据库删除操作日志logs.CtxInfof(ctx, "Deleting prompt from database, promptID=%d, traceID=%s", promptID, traceID)// 索引清理日志logs.CtxInfof(ctx, "Publishing prompt delete event, promptID=%d, traceID=%s", promptID, traceID)return nil
}// 提示词删除操作审计日志
func (s *PromptApplicationService) logPromptDeleteAudit(ctx context.Context, operation string, promptID int64, details map[string]interface{}) {userID := ctx.Value("user_id").(int64)spaceID := ctx.Value("space_id").(int64)traceID := ctx.Value("trace_id").(string)auditLog := map[string]interface{}{"operation": operation,"prompt_id": promptID,"user_id": userID,"space_id": spaceID,"trace_id": traceID,"timestamp": time.Now().Unix(),"details": details,}logs.CtxInfof(ctx, "Prompt audit log: %+v", auditLog)
}
提示词删除日志内容规范:
- 请求日志:记录用户ID、工作空间ID、提示词ID、删除原因、TraceID
- 业务日志:记录提示词删除步骤、状态变更、权限验证结果、引用关系检查
- 性能日志:记录删除接口响应时间、数据库删除时间、ES索引清理时间
- 错误日志:记录删除错误堆栈、提示词相关上下文信息、影响范围
- 审计日志:记录提示词的删除操作、删除前状态、删除后清理结果
9.4 提示词删除监控和告警
提示词删除关键指标监控:
- 删除性能:提示词删除响应时间、删除成功率、删除QPS
- 资源使用:提示词数据库连接数、ES索引清理延迟、内存使用率
- 业务指标:提示词删除成功率、删除频率分布、重要提示词删除次数
- 安全指标:权限验证通过率、恶意删除尝试次数、删除频率限制触发次数
提示词删除告警策略:
- 删除失败率告警:当提示词删除失败率超过3%时触发告警
- 性能告警:当提示词删除响应时间超过2秒时触发告警
- 资源告警:当提示词数据库连接数超过80%时触发告警
- 安全告警:当检测到异常删除行为时立即触发告警
- 数据一致性告警:当MySQL和ES删除状态不一致时触发告警
// 提示词删除监控指标收集
type PromptDeleteMetrics struct {DeleteSuccessCount int64 // 删除成功次数DeleteFailureCount int64 // 删除失败次数DeleteLatency time.Duration // 删除延迟PermissionDeniedCount int64 // 权限拒绝次数RateLimitCount int64 // 频率限制次数ImportantDeleteCount int64 // 重要提示词删除次数IndexCleanupLatency time.Duration // 索引清理延迟
}// 提示词删除监控指标上报
func (s *PromptApplicationService) reportDeleteMetrics(ctx context.Context, operation string, startTime time.Time, promptID int64, err error) {latency := time.Since(startTime)if err != nil {metrics.DeleteFailureCount++// 根据错误类型分类统计switch {case errors.Is(err, ErrPromptPermissionDenied):metrics.PermissionDeniedCount++case errors.Is(err, ErrPromptDeleteRateLimit):metrics.RateLimitCount++}logs.CtxErrorf(ctx, "Prompt %s failed, promptID=%d, error=%v, latency=%dms", operation, promptID, err, latency.Milliseconds())} else {metrics.DeleteSuccessCount++metrics.DeleteLatency = latency// 检查是否为重要提示词删除if isImportant, _ := s.checkPromptImportance(ctx, promptID); isImportant {metrics.ImportantDeleteCount++}logs.CtxInfof(ctx, "Prompt %s succeeded, promptID=%d, latency=%dms", operation, promptID, latency.Milliseconds())}// 上报到监控系统s.metricsReporter.Report(ctx, "prompt_delete", map[string]interface{}{"operation": operation,"prompt_id": promptID,"success": err == nil,"latency_ms": latency.Milliseconds(),"error_type": getErrorType(err),})
}// 获取错误类型
func getErrorType(err error) string {if err == nil {return "none"}switch {case errors.Is(err, ErrPromptNotFound):return "not_found"case errors.Is(err, ErrPromptPermissionDenied):return "permission_denied"case errors.Is(err, ErrPromptHasReferences):return "has_references"case errors.Is(err, ErrPromptDeleteRateLimit):return "rate_limit"default:return "system_error"}
}## 10. 提示词删除流程图### 10.1 DeletePrompt接口完整调用流程用户登录 Coze 平台点击"资源库" → 选择提示词 → 点击"..." → "删除"场景的后端处理流程:
用户点击"删除" → 前端发起请求 → API网关路由 → Handler处理 → 业务服务层 → 数据持久化层 → 索引清理层 → 响应返回
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
前端确认删除 HTTP DELETE请求 路由匹配 参数验证 权限检查 MySQL删除 ES索引清理 JSON响应
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
/delete-prompt /api/plugin_api/ Handler 请求绑定 用户身份 prompt_ 事件发布 删除结果
delete_prompt 函数调用 参数校验 Session resource 异步处理 状态返回
DeletePrompt 验证 表删除 ES清理
↓
PromptApplicationService
↓
权限验证(创建者检查)
↓
引用关系检查
↓
重要性检查
↓
删除频率限制检查
↓
数据库删除事务
↓
删除事件发布
↓
返回删除结果
### 10.2 提示词删除详细流程说明#### 1. API网关层(路由处理)
**文件位置**:`backend/api/handler/coze/prompt_service.go````go
// @router /api/plugin_api/delete_prompt [DELETE]
func DeletePrompt(ctx context.Context, c *app.RequestContext) {var err errorvar req prompt.DeletePromptRequest// 1. 请求参数绑定和验证err = c.BindAndValidate(&req)if err != nil {invalidParamRequestResponse(c, err.Error())return}// 2. 提示词删除参数校验if req.PromptID <= 0 {invalidParamRequestResponse(c, "prompt_id is invalid")return}// 3. 删除确认验证if !req.ConfirmDelete {invalidParamRequestResponse(c, "delete confirmation required")return}// 4. 调用提示词删除服务err = prompt.PromptSVC.DeletePromptResource(ctx, req.PromptID)if err != nil {handlePromptDeleteBusinessError(c, err, req.PromptID)return}// 5. 返回JSON响应c.JSON(consts.StatusOK, gin.H{"success": true,"message": "提示词删除成功","prompt_id": req.PromptID,})
}
处理步骤:
- 路由匹配:
DELETE /api/plugin_api/delete_prompt
- 参数绑定:将HTTP请求体绑定到
DeletePromptRequest
结构体 - 参数验证:验证
prompt_id
的有效性和删除确认标识 - 服务调用:调用提示词服务的
DeletePromptResource
方法 - 响应返回:返回JSON格式的删除结果
2. 业务服务层(PromptApplicationService)
文件位置:backend/application/prompt/prompt.go
func (p *PromptApplicationService) DeletePromptResource(ctx context.Context, promptID int64) error {// 1. 用户身份验证userID := ctxutil.GetUIDFromCtx(ctx)if userID == nil {return errorx.New(errno.ErrPromptPermissionCode, errorx.KV("msg", "session required"))}// 2. 获取提示词信息进行权限验证promptInfo, err := p.DomainSVC.GetPromptResource(ctx, promptID)if err != nil {if errors.Is(err, gorm.ErrRecordNotFound) {return errorx.New(errno.ErrPromptNotFoundCode, errorx.KV("prompt_id", promptID))}return err}// 3. 验证删除权限(只有创建者可以删除)if promptInfo.CreatorID != userID {return errorx.New(errno.ErrPromptPermissionDeniedCode, errorx.KV("msg", "只有创建者可以删除提示词"),errorx.KV("prompt_id", promptID),errorx.KV("creator_id", promptInfo.CreatorID),errorx.KV("user_id", userID))}// 4. 检查提示词引用关系hasReferences, err := p.checkPromptReferences(ctx, promptID)if err != nil {return fmt.Errorf("检查提示词引用关系失败: %w", err)}if hasReferences {return errorx.New(errno.ErrPromptHasReferencesCode, errorx.KV("prompt_id", promptID),errorx.KV("msg", "提示词正在被其他资源引用,无法删除"))}// 5. 删除安全检查err = p.validateDeleteSafety(ctx, promptID)if err != nil {return err}// 6. 执行删除操作err = p.DomainSVC.DeletePromptResource(ctx, promptID)if err != nil {return fmt.Errorf("删除提示词失败: %w", err)}// 7. 发布删除事件deleteEvent := &entity.ResourceDomainEvent{ResID: promptID,ResType: 6, // 提示词类型OpType: entity.Deleted,SpaceID: promptInfo.SpaceID,OperatorID: userID,OccurredAt: time.Now(),}err = p.eventbus.PublishResources(ctx, deleteEvent)if err != nil {logs.CtxErrorf(ctx, "发布提示词删除事件失败: %v, promptID=%d", err, promptID)// 删除事件发布失败不影响删除操作的成功}return nil
}
核心功能:
- 身份验证:从上下文中提取用户ID,验证用户登录状态
- 权限检查:验证用户对提示词的删除权限(只有创建者可删除)
- 引用检查:检查提示词是否被其他资源引用,防止误删
- 安全验证:验证删除操作的安全性和合规性
- 数据删除:从数据库中删除提示词记录
- 事件发布:发布提示词删除事件用于异步清理
- 响应组装:构建标准化的删除响应数据结构
#### 3. 领域服务层(提示词删除领域服务)
**核心功能**:
- **引用关系检查**:检查提示词是否被其他资源引用
- **删除安全验证**:验证删除操作的安全性
- **重要性评估**:评估提示词的重要性,防止误删重要资源
- **删除权限验证**:验证用户的删除权限```go
// 检查提示词引用关系
func (p *PromptApplicationService) checkPromptReferences(ctx context.Context, promptID int64) (bool, error) {// 1. 检查是否被Bot引用botCount, err := p.botRepo.CountByPromptID(ctx, promptID)if err != nil {return false, fmt.Errorf("检查Bot引用失败: %w", err)}if botCount > 0 {return true, nil}// 2. 检查是否被工作流引用workflowCount, err := p.workflowRepo.CountByPromptID(ctx, promptID)if err != nil {return false, fmt.Errorf("检查工作流引用失败: %w", err)}if workflowCount > 0 {return true, nil}// 3. 检查是否被其他提示词引用promptRefCount, err := p.promptRepo.CountReferences(ctx, promptID)if err != nil {return false, fmt.Errorf("检查提示词引用失败: %w", err)}return promptRefCount > 0, nil
}// 删除安全验证
func (p *PromptApplicationService) validateDeleteSafety(ctx context.Context, promptID int64) error {// 1. 检查删除频率限制userID := ctxutil.GetUIDFromCtx(ctx)deleteCount, err := p.getRecentDeleteCount(ctx, userID)if err != nil {return fmt.Errorf("获取删除频率失败: %w", err)}if deleteCount >= 10 { // 每小时最多删除10个提示词return errorx.New(errno.ErrPromptDeleteRateLimitCode, errorx.KV("msg", "删除操作过于频繁,请稍后再试"),errorx.KV("user_id", userID),errorx.KV("delete_count", deleteCount))}// 2. 检查提示词重要性isImportant, err := p.checkPromptImportance(ctx, promptID)if err != nil {return fmt.Errorf("检查提示词重要性失败: %w", err)}if isImportant {// 重要提示词需要额外确认return errorx.New(errno.ErrPromptImportantDeleteCode,errorx.KV("msg", "这是一个重要的提示词,删除前请确认"),errorx.KV("prompt_id", promptID))}return nil
}// 检查提示词重要性
func (p *PromptApplicationService) checkPromptImportance(ctx context.Context, promptID int64) (bool, error) {// 1. 检查使用频率usageCount, err := p.getPromptUsageCount(ctx, promptID)if err != nil {return false, err}// 2. 检查创建时间(超过30天且使用频率高的认为重要)promptInfo, err := p.DomainSVC.GetPromptResource(ctx, promptID)if err != nil {return false, err}daysSinceCreation := time.Since(promptInfo.CreatedAt).Hours() / 24// 使用频率高且存在时间长的提示词被认为是重要的return usageCount > 100 && daysSinceCreation > 30, nil
}
4. 数据持久化层
MySQL数据库操作:
- 表名:
prompt_resource
- 操作类型:DELETE操作
- 事务处理:确保数据一致性
- 删除策略:软删除(更新deleted_at字段)或硬删除
- 关联清理:清理相关的索引和缓存数据
// 提示词删除数据库操作
func (r *PromptRepository) Delete(ctx context.Context, promptID int64) error {// 使用软删除策略query := `UPDATE prompt_resource SET deleted_at = ?, updated_at = ?WHERE id = ? AND deleted_at IS NULL`now := time.Now()result, err := r.db.ExecContext(ctx, query, now, now, promptID)if err != nil {return fmt.Errorf("删除提示词失败: %w", err)}rowsAffected, err := result.RowsAffected()if err != nil {return fmt.Errorf("获取删除结果失败: %w", err)}if rowsAffected == 0 {return errorx.New(errno.ErrPromptNotFoundCode, errorx.KV("prompt_id", promptID),errorx.KV("msg", "提示词不存在或已被删除"))}return nil
}// 硬删除操作(用于彻底清理)
func (r *PromptRepository) HardDelete(ctx context.Context, promptID int64) error {query := `DELETE FROM prompt_resource WHERE id = ?`result, err := r.db.ExecContext(ctx, query, promptID)if err != nil {return fmt.Errorf("硬删除提示词失败: %w", err)}rowsAffected, err := result.RowsAffected()if err != nil {return fmt.Errorf("获取删除结果失败: %w", err)}if rowsAffected == 0 {return errorx.New(errno.ErrPromptNotFoundCode, errorx.KV("prompt_id", promptID))}return nil
}
5. 事件发布层(EventPublisher)
删除事件发布流程:
// 提示词删除事件发布
func (p *EventPublisher) PublishPromptDeletedEvent(ctx context.Context, event *entity.PromptDeletedEvent) error {// 1. 构建删除事件消息eventMsg := &message.PromptDeletedMessage{PromptID: event.PromptID,SpaceID: event.SpaceID,CreatorID: event.CreatorID,OperatorID: event.OperatorID,Name: event.Name,DeletedAt: event.DeletedAt.Unix(),EventType: "prompt_deleted",Reason: event.DeleteReason,}// 2. 序列化事件数据data, err := json.Marshal(eventMsg)if err != nil {return fmt.Errorf("序列化删除事件失败: %w", err)}// 3. 发布到消息队列err = p.messageQueue.Publish(ctx, "prompt_delete_events", data)if err != nil {return fmt.Errorf("发布删除事件失败: %w", err)}logs.CtxInfof(ctx, "Published prompt deleted event, promptID=%d, operator=%d", event.PromptID, event.OperatorID)return nil
}// 异步删除事件处理器
func (h *PromptEventHandler) HandlePromptDeletedEvent(ctx context.Context, event *entity.PromptDeletedEvent) error {// 1. 清理ElasticSearch索引err := h.removeFromESIndex(ctx, event)if err != nil {logs.CtxErrorf(ctx, "Failed to remove from ES index: %v", err)return err}// 2. 清理缓存数据err = h.clearCache(ctx, event)if err != nil {logs.CtxWarnf(ctx, "Failed to clear cache: %v", err)}// 3. 清理相关文件err = h.cleanupFiles(ctx, event)if err != nil {logs.CtxWarnf(ctx, "Failed to cleanup files: %v", err)}// 4. 发送删除通知err = h.sendDeleteNotification(ctx, event)if err != nil {logs.CtxWarnf(ctx, "Failed to send delete notification: %v", err)}// 5. 更新统计数据err = h.updateDeleteStatistics(ctx, event)if err != nil {logs.CtxWarnf(ctx, "Failed to update delete statistics: %v", err)}return nil
}// ElasticSearch索引清理
func (h *PromptEventHandler) removeFromESIndex(ctx context.Context, event *entity.PromptDeletedEvent) error {// 从ES中删除提示词文档err := h.esClient.Delete(ctx, "coze_resource", fmt.Sprintf("%d", event.PromptID))if err != nil {return fmt.Errorf("删除ES索引失败: %w", err)}logs.CtxInfof(ctx, "Removed prompt from ES index, promptID=%d", event.PromptID)return nil
}
删除事件处理内容:
- 索引清理:从ElasticSearch索引中删除提示词文档
- 缓存清理:清理相关的缓存数据
- 文件清理:清理提示词相关的文件资源
- 通知发送:向相关用户发送删除成功通知
- 统计更新:更新提示词删除相关的统计数据
6. 响应数据结构
DeletePromptResponse:
type DeletePromptResponse struct {Code int64 `json:"code"` // 响应码Msg string `json:"msg"` // 响应消息Success bool `json:"success"` // 删除是否成功PromptID int64 `json:"prompt_id"` // 被删除的提示词IDDeletedAt int64 `json:"deleted_at"` // 删除时间戳BaseResp *base.BaseResp `json:"base_resp"` // 基础响应信息
}
DeletePromptRequest请求结构:
type DeletePromptRequest struct {PromptID int64 `json:"prompt_id" binding:"required"` // 提示词IDConfirmDelete bool `json:"confirm_delete" binding:"required"` // 删除确认DeleteReason string `json:"delete_reason,omitempty"` // 删除原因(可选)
}
PromptDeletedEvent事件结构:
type PromptDeletedEvent struct {PromptID int64 `json:"prompt_id"` // 提示词IDSpaceID int64 `json:"space_id"` // 工作空间IDCreatorID int64 `json:"creator_id"` // 原创建者IDOperatorID int64 `json:"operator_id"` // 删除操作者IDName string `json:"name"` // 提示词名称DeleteReason string `json:"delete_reason"` // 删除原因DeletedAt time.Time `json:"deleted_at"` // 删除时间EventType string `json:"event_type"` // 事件类型
}
响应内容说明:
- 成功响应:返回删除成功状态和被删除的提示词ID
- 错误响应:返回具体的错误码和错误信息(如权限不足、提示词不存在等)
- 删除确认:要求前端明确确认删除操作
- 时间戳:记录删除操作的具体时间
11. 核心技术特点
11.1 提示词删除的分层架构设计
清晰的职责分离:
- API层(prompt_handler.go):负责提示词删除请求处理、参数验证、响应格式化
- 应用层(prompt_service.go):负责提示词删除业务逻辑编排、权限验证、事务管理
- 领域层(prompt_domain.go):负责提示词删除核心业务逻辑、引用检查、安全验证
- 基础设施层(prompt_repository.go):负责提示词数据删除、外部服务清理
// 提示词删除的分层调用示例
func (h *PromptHandler) DeletePrompt(ctx context.Context, req *DeletePromptRequest) (*DeletePromptResponse, error) {// API层:参数验证if err := h.validateDeletePromptRequest(req); err != nil {return nil, err}// 调用应用层服务err := h.promptService.DeletePromptResource(ctx, req.PromptID)if err != nil {return nil, err}return &DeletePromptResponse{Code: 0,Msg: "删除成功",Success: true,PromptID: req.PromptID,DeletedAt: time.Now().Unix(),PromptID: promptID,}, nil
}
依赖倒置原则在提示词删除中的应用:
- 高层模块不依赖低层模块,都依赖于抽象接口
- 通过
PromptRepository
接口实现数据访问层解耦 - 支持不同存储引擎的灵活切换(MySQL、PostgreSQL等)
11.2 提示词数据存储和索引技术
MySQL存储设计:
- 表结构:
prompt_resource
表专门存储提示词数据 - 索引优化:针对
space_id
、creator_id
、name
建立复合索引 - 事务支持:确保提示词删除的ACID特性
- 软删除机制:通过
deleted_at
字段实现软删除,保留数据可恢复性
// 提示词数据库表结构(支持软删除)
type PromptResource struct {ID int64 `gorm:"primaryKey;autoIncrement" json:"id"`SpaceID int64 `gorm:"index:idx_space_creator;not null" json:"space_id"`Name string `gorm:"size:255;not null;index:idx_name_space,unique" json:"name"`Description string `gorm:"size:1000" json:"description"`PromptText string `gorm:"type:text;not null" json:"prompt_text"`Status string `gorm:"size:20;default:'draft'" json:"status"`CreatorID int64 `gorm:"index:idx_space_creator;not null" json:"creator_id"`CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`DeletedAt *time.Time `gorm:"index" json:"deleted_at,omitempty"` // 软删除字段
}
ElasticSearch索引设计:
- 索引名称:
coze_prompt_resource
- 字段映射:针对提示词内容进行全文搜索优化
- 实时同步:通过事件机制实现数据库到ES的实时同步
- 索引清理:删除提示词时同步清理ES索引数据
// 提示词ES索引映射
type PromptESDocument struct {ID int64 `json:"id"`SpaceID int64 `json:"space_id"`Name string `json:"name"`Description string `json:"description"`PromptText string `json:"prompt_text"` // 全文搜索字段Status string `json:"status"`CreatorID int64 `json:"creator_id"`CreatedAt time.Time `json:"created_at"`UpdatedAt time.Time `json:"updated_at"`DeletedAt *time.Time `json:"deleted_at,omitempty"` // 软删除标记
}
11.3 提示词删除安全机制
多层次删除验证:
- 权限验证:确保用户有删除指定提示词的权限
- 引用检查:检查提示词是否被其他资源引用
- 安全验证:防止恶意删除和批量删除攻击
// 提示词删除验证器
type PromptDeleteValidator struct {referenceChecker ReferenceCheckersecurityChecker SecurityCheckerpermissionChecker PermissionChecker
}func (v *PromptDeleteValidator) ValidatePromptDeletion(ctx context.Context, promptID, userID int64) error {// 1. 权限检查if !v.permissionChecker.CanDeletePrompt(ctx, userID, promptID) {return errors.New("用户没有删除该提示词的权限")}// 2. 引用关系检查if v.referenceChecker.HasActiveReferences(ctx, promptID) {return errors.New("提示词正在被其他资源使用,无法删除")}// 3. 安全检查if v.securityChecker.IsSuspiciousDeletion(ctx, userID, promptID) {return errors.New("检测到可疑删除行为")}return nil
}
安全防护机制:
- SQL注入防护:使用参数化查询防止恶意删除
- 权限隔离:确保用户只能删除自己有权限的提示词
- 操作审计:记录所有删除操作的详细日志
- 批量删除限制:防止恶意批量删除攻击
11.4 提示词事件驱动架构
事件类型定义:
type PromptEventType stringconst (PromptCreated PromptEventType = "prompt_created" // 提示词创建事件PromptUpdated PromptEventType = "prompt_updated" // 提示词更新事件PromptDeleted PromptEventType = "prompt_deleted" // 提示词删除事件
)// 提示词删除事件
type PromptDeletedEvent struct {PromptID int64 `json:"prompt_id"`SpaceID int64 `json:"space_id"`Name string `json:"name"`CreatorID int64 `json:"creator_id"`DeleterID int64 `json:"deleter_id"`DeletedAt time.Time `json:"deleted_at"`DeleteType string `json:"delete_type"` // soft_delete 或 hard_deleteEventType PromptEventType `json:"event_type"`
}
异步事件处理流程:
- 提示词删除成功后发布
PromptDeletedEvent
- 事件处理器异步清理ElasticSearch索引
- 清理相关缓存数据
- 发送删除通知给相关用户
- 更新统计数据和配额信息
// 提示词删除事件处理器
func (h *PromptEventHandler) HandlePromptDeletedEvent(ctx context.Context, event *PromptDeletedEvent) error {// 1. 清理ES索引if err := h.removeFromESIndex(ctx, event.PromptID); err != nil {logs.CtxErrorf(ctx, "Failed to remove from ES index: %v", err)return err}// 2. 清理缓存if err := h.clearCache(ctx, event.PromptID); err != nil {logs.CtxWarnf(ctx, "Failed to clear cache: %v", err)}// 3. 发送删除通知if err := h.sendDeletionNotification(ctx, event); err != nil {logs.CtxWarnf(ctx, "Failed to send deletion notification: %v", err)}// 4. 更新统计和配额if err := h.updateStatisticsAfterDeletion(ctx, event); err != nil {logs.CtxWarnf(ctx, "Failed to update statistics: %v", err)}return nil
}
11.5 提示词删除权限控制机制
多层次权限验证:
- 身份认证:JWT Token验证用户身份
- 所有权验证:验证用户是否为提示词的创建者或有删除权限
- 工作空间权限:验证用户在指定工作空间的删除权限
- 管理员权限:支持管理员强制删除机制
// 提示词删除权限验证器
type PromptDeletePermissionValidator struct {userService UserServicespaceService SpaceServicepromptService PromptService
}func (v *PromptDeletePermissionValidator) ValidateDeletePermission(ctx context.Context, userID, promptID int64) error {// 1. 获取提示词信息prompt, err := v.promptService.GetPromptByID(ctx, promptID)if err != nil {return err}// 2. 验证所有权或删除权限if prompt.CreatorID == userID {return nil // 创建者可以删除}// 3. 验证工作空间删除权限hasDeletePermission, err := v.spaceService.HasDeletePermission(ctx, userID, prompt.SpaceID)if err != nil {return err}if !hasDeletePermission {return errors.New("用户没有删除该提示词的权限")}// 4. 检查管理员权限isAdmin, err := v.userService.IsSpaceAdmin(ctx, userID, prompt.SpaceID)if err != nil {return err}if !isAdmin {return errors.New("只有管理员或创建者可以删除提示词")}return nil
}
11.6 提示词删除性能优化策略
数据库性能优化:
- 软删除索引:为
deleted_at
字段建立索引优化查询性能 - 批量删除:支持批量软删除操作减少数据库访问
- 事务优化:合理使用事务确保删除操作的原子性
缓存清理策略:
- Redis缓存清理:删除时及时清理相关缓存数据
- 本地缓存失效:通过事件机制使本地缓存失效
- 缓存一致性:确保删除操作后缓存数据的一致性
// 提示词删除缓存管理器
type PromptDeleteCacheManager struct {redisClient redis.ClientlocalCache cache.Cache
}func (c *PromptDeleteCacheManager) ClearPromptCache(ctx context.Context, promptID int64) error {// 1. 清理Redis缓存cacheKey := fmt.Sprintf("prompt:%d", promptID)if err := c.redisClient.Del(ctx, cacheKey).Err(); err != nil {logs.CtxWarnf(ctx, "Failed to clear Redis cache for prompt %d: %v", promptID, err)}// 2. 清理本地缓存c.localCache.Delete(cacheKey)// 3. 清理相关的列表缓存listCachePattern := fmt.Sprintf("prompt_list:*")if err := c.clearCacheByPattern(ctx, listCachePattern); err != nil {logs.CtxWarnf(ctx, "Failed to clear list cache: %v", err)}return nil
}func (c *PromptDeleteCacheManager) BatchClearCache(ctx context.Context, promptIDs []int64) error {// 批量清理缓存,提高删除性能var cacheKeys []stringfor _, promptID := range promptIDs {cacheKeys = append(cacheKeys, fmt.Sprintf("prompt:%d", promptID))}if err := c.redisClient.Del(ctx, cacheKeys...).Err(); err != nil {return err}return nil
}
异步删除优化:
- 消息队列:使用RocketMQ处理异步删除清理任务
- 批量清理:批量清理ES索引和缓存提高效率
- 重试机制:删除失败任务自动重试保证数据一致性
- 延迟删除:支持延迟硬删除,给用户恢复时间
12. 总结
12.1 提示词删除功能的架构优势
Coze提示词删除功能采用了现代化的分层架构设计,具有以下显著优势:
1. 高可扩展性
- 分层架构设计使得提示词删除各层职责清晰,便于独立扩展和维护
- 基于接口的依赖倒置设计支持不同存储引擎的灵活切换
- 事件驱动架构支持提示词删除相关业务的异步处理,提高系统吞吐量
// 可扩展的提示词删除服务接口设计
type PromptDeleteService interface {SoftDeletePrompt(ctx context.Context, cmd *DeletePromptCommand) errorHardDeletePrompt(ctx context.Context, promptID int64) errorRestorePrompt(ctx context.Context, promptID int64) errorGetDeletedPrompts(ctx context.Context, spaceID int64) ([]*PromptResource, error)
}// 支持多种删除策略的Repository接口
type PromptDeleteRepository interface {SoftDelete(ctx context.Context, promptID int64) errorHardDelete(ctx context.Context, promptID int64) errorRestore(ctx context.Context, promptID int64) errorFindDeletedBySpaceID(ctx context.Context, spaceID int64) ([]*PromptResource, error)
}
2. 高可用性
- 软删除机制提供数据恢复能力,避免误删除造成的数据丢失
- 异步事件处理确保提示词删除主流程的稳定性
- 完善的错误处理和重试机制保证删除操作的最终一致性
3. 高性能
- 软删除避免了物理删除的高成本操作
- 批量删除和缓存清理策略提升删除效率
- 异步清理机制减少删除操作对系统性能的影响
4. 高安全性
- 多层次的删除权限验证机制(身份认证 + 所有权验证 + 管理员权限)
- 引用关系检查防止误删除正在使用的提示词
- 操作审计和日志记录确保删除操作的可追溯性
12.2 提示词删除功能的技术亮点
1. 智能化的软删除机制
- 针对提示词删除特点设计的软删除策略
- 支持数据恢复和延迟硬删除机制
- 合理的索引设计优化删除查询场景
// 针对提示词删除优化的表结构设计
CREATE TABLE prompt_resource (id BIGINT PRIMARY KEY AUTO_INCREMENT,space_id BIGINT NOT NULL,name VARCHAR(255) NOT NULL,description VARCHAR(1000),prompt_text TEXT NOT NULL,status VARCHAR(20) DEFAULT 'draft',creator_id BIGINT NOT NULL,created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,deleted_at TIMESTAMP NULL DEFAULT NULL, -- 软删除字段INDEX idx_space_creator (space_id, creator_id),UNIQUE INDEX idx_name_space (name, space_id),INDEX idx_status (status),INDEX idx_deleted_at (deleted_at), -- 软删除索引INDEX idx_created_at (created_at)
);
2. 智能化的删除安全机制
- 多维度的删除安全验证(权限、引用、安全性)
- 可配置的删除策略支持不同业务场景
- 实时的引用关系检测防止误删除
3. 事件驱动的删除清理
- 基于提示词删除事件实现数据库到ES的实时清理
- 保证了删除操作的最终一致性
- 支持事件重放和数据恢复机制
// 提示词删除事件驱动清理示例
func (s *PromptDeleteService) DeletePrompt(ctx context.Context, cmd *DeletePromptCommand) error {// 1. 软删除提示词err := s.promptRepo.SoftDelete(ctx, cmd.PromptID)if err != nil {return err}// 2. 发布删除事件event := &PromptDeletedEvent{PromptID: cmd.PromptID,SpaceID: cmd.SpaceID,DeleterID: cmd.UserID,DeletedAt: time.Now(),DeleteType: "soft_delete",}s.eventPublisher.PublishPromptDeletedEvent(ctx, event)return nil
}
4. 精细化的删除权限控制
- 所有权和管理员权限的双重验证
- 引用关系检查防止误删除
- 灵活的删除策略支持不同角色需求
12.3 提示词删除系统的扩展性和可维护性
扩展性设计:
- 删除策略扩展:支持多种删除策略(软删除、硬删除、归档)
- 功能扩展:基于接口设计支持新的删除功能快速接入
- 业务扩展:事件驱动架构支持新的删除业务场景的灵活集成
可维护性保障:
- 代码结构清晰:分层架构和领域驱动设计提高删除逻辑的可读性
- 测试覆盖完善:单元测试和集成测试保证删除功能的质量
- 监控体系完备:全链路追踪和删除操作监控便于问题定位
// 可维护的删除错误处理示例
func (s *PromptDeleteService) DeletePrompt(ctx context.Context, cmd *DeletePromptCommand) error {// 记录删除操作开始logs.CtxInfof(ctx, "Start deleting prompt, promptID=%d, userID=%d", cmd.PromptID, cmd.UserID)defer func() {// 记录删除操作结束logs.CtxInfof(ctx, "Finish deleting prompt, promptID=%d", cmd.PromptID)}()// 删除业务逻辑处理...return nil
}
通过以上的架构设计和技术实现,Coze提示词删除功能为用户提供了高效、安全、可靠的提示词删除管理服务,为AI应用开发中的提示词生命周期管理提供了强有力的基础设施支撑。该系统不仅满足了当前的删除业务需求,还具备了良好的扩展性和可维护性,能够适应未来删除策略和恢复机制的发展需要。
删除功能的核心价值:
- 数据安全:软删除机制保障数据安全,避免误删除造成的损失
- 操作便捷:简单直观的删除操作,提升用户体验
- 系统稳定:异步处理和事件驱动确保删除操作不影响系统稳定性
- 可追溯性:完整的操作日志和审计记录,便于问题排查和数据恢复