Coze源码分析-资源库-编辑知识库-后端源码-应用/领域/数据访问层
4. 领域服务层
KnowledgeSVC 领域服务层架构
知识库领域服务层是Coze Studio中处理知识库业务逻辑的核心层,负责知识库资源的编辑、更新和业务规则实现。当用户在知识库编辑界面修改信息并保存时,领域服务层实现了实际的业务逻辑处理。
知识库领域服务接口定义
领域服务接口定义了知识库管理的核心业务能力,包括知识库和文档的完整生命周期管理。
// KnowledgeService 知识库领域服务接口
type KnowledgeService interface {// 更新知识库基本信息UpdateKnowledge(ctx context.Context, req *UpdateKnowledgeRequest) error// 获取知识库详情GetKnowledge(ctx context.Context, knowledgeID string) (*Knowledge, error)// 创建文档CreateDocument(ctx context.Context, req *CreateDocumentRequest) (*CreateDocumentResponse, error)// 更新文档UpdateDocument(ctx context.Context, req *UpdateDocumentRequest) error// 删除文档DeleteDocument(ctx context.Context, req *DeleteDocumentRequest) error// 获取文档详情GetDocument(ctx context.Context, documentID string) (*Document, error)// 获取文档列表ListDocuments(ctx context.Context, req *ListDocumentsRequest) (*ListDocumentsResponse, error)
}// UpdateKnowledgeRequest 更新知识库请求
type UpdateKnowledgeRequest struct {KnowledgeID string // 知识库IDName *string // 知识库名称Description *string // 知识库描述CoverUrl *string // 封面URLVisibility *string // 可见性Tags *string // 标签
}// Knowledge 知识库领域实体
type Knowledge struct {ID string // 知识库IDName string // 知识库名称Description string // 知识库描述CoverUrl string // 封面URLVisibility string // 可见性Tags string // 标签OwnerID string // 所有者IDCreatedAt int64 // 创建时间UpdatedAt int64 // 更新时间Version int64 // 版本号
}// Document 文档领域实体
type Document struct {ID string // 文档IDKnowledgeID string // 所属知识库IDTitle string // 文档标题Content string // 文档内容Type string // 文档类型Version int32 // 文档版本Status int32 // 文档状态CreatedBy string // 创建者IDCreatedAt int64 // 创建时间UpdatedAt int64 // 更新时间
}
知识库领域服务实现
KnowledgeSVC是知识库领域服务的核心实现,处理所有与知识库编辑相关的业务逻辑。
// KnowledgeSVC 知识库领域服务实现
type KnowledgeSVC struct {knowledgeRepo repository.KnowledgeRepositorydocumentRepo repository.DocumentRepositoryidGenerator idgen.IDGeneratorcache cache.Cache
}// KnowledgeSVCConfig 知识库服务配置
type KnowledgeSVCConfig struct {KnowledgeRepo repository.KnowledgeRepositoryDocumentRepo repository.DocumentRepositoryIDGenerator idgen.IDGeneratorCache cache.Cache
}// NewKnowledgeSVC 创建知识库服务实例
func NewKnowledgeSVC(cfg *KnowledgeSVCConfig) (*KnowledgeSVC, error) {if cfg == nil {return nil, errors.New("knowledge svc config is nil")}// 验证必要依赖if cfg.KnowledgeRepo == nil {return nil, errors.New("knowledge repository is required")}if cfg.DocumentRepo == nil {return nil, errors.New("document repository is required")}if cfg.IDGenerator == nil {return nil, errors.New("id generator is required")}return &KnowledgeSVC{knowledgeRepo: cfg.KnowledgeRepo,documentRepo: cfg.DocumentRepo,idGenerator: cfg.IDGenerator,cache: cfg.Cache,}, nil
}
更新知识库核心实现
当用户在编辑界面修改知识库信息并保存时,UpdateKnowledge方法实现了核心的业务逻辑:
// UpdateKnowledge 更新知识库信息
func (s *KnowledgeSVC) UpdateKnowledge(ctx context.Context, req *UpdateKnowledgeRequest) error {// 参数验证if err := s.validateUpdateKnowledgeRequest(req); err != nil {return err}// 查询知识库是否存在existingKnowledge, err := s.knowledgeRepo.Get(ctx, req.KnowledgeID)if err != nil {if errors.Is(err, repository.ErrNotFound) {return errorx.New(errno.ErrKnowledgeNotFoundCode, "knowledge not found")}return err}// 构建更新参数updateParams := make(map[string]interface{})// 仅当字段不为空时更新if req.Name != nil {updateParams["name"] = *req.Name}if req.Description != nil {updateParams["description"] = *req.Description}if req.CoverUrl != nil {updateParams["cover_url"] = *req.CoverUrl}if req.Visibility != nil {// 验证可见性值if !isValidVisibility(*req.Visibility) {return errorx.New(errno.ErrKnowledgeParamErrorCode, "invalid visibility value")}updateParams["visibility"] = *req.Visibility}if req.Tags != nil {updateParams["tags"] = *req.Tags}// 设置更新时间和版本now := time.Now().Unix()updateParams["updated_at"] = nowupdateParams["version"] = existingKnowledge.Version + 1// 执行更新操作err = s.knowledgeRepo.Update(ctx, req.KnowledgeID, updateParams)if err != nil {logs.CtxErrorf(ctx, "update knowledge failed, knowledgeID: %s, err: %v", req.KnowledgeID, err)return err}// 清除缓存if s.cache != nil {_ = s.cache.Delete(ctx, fmt.Sprintf("knowledge:%s", req.KnowledgeID))}return nil
}// validateUpdateKnowledgeRequest 验证更新知识库请求参数
func (s *KnowledgeSVC) validateUpdateKnowledgeRequest(req *UpdateKnowledgeRequest) error {if req.KnowledgeID == "" {return errorx.New(errno.ErrKnowledgeParamErrorCode, "knowledgeID is required")}// 验证名称(如果提供)if req.Name != nil {name := strings.TrimSpace(*req.Name)if name == "" {return errorx.New(errno.ErrKnowledgeParamErrorCode, "knowledge name cannot be empty")}if len(name) > 256 {return errorx.New(errno.ErrKnowledgeParamErrorCode, "knowledge name too long")}}return nil
}
文档管理核心实现
除了知识库基本信息更新外,领域服务层还实现了文档级别的管理功能:
// CreateDocument 创建文档
func (s *KnowledgeSVC) CreateDocument(ctx context.Context, req *CreateDocumentRequest) (*CreateDocumentResponse, error) {// 参数验证if err := s.validateCreateDocumentRequest(req); err != nil {return nil, err}// 验证知识库是否存在_, err := s.knowledgeRepo.Get(ctx, req.KnowledgeID)if err != nil {if errors.Is(err, repository.ErrNotFound) {return nil, errorx.New(errno.ErrKnowledgeNotFoundCode, "knowledge not found")}return nil, err}// 生成文档IDdocumentID := fmt.Sprintf("doc_%s", s.idGenerator.GenString())// 创建文档实体document := &repository.Document{ID: documentID,KnowledgeID: req.KnowledgeID,Title: req.Title,Content: req.Content,Type: req.Type,Version: 1,Status: documentStatusNormal,CreatedBy: req.CreatorID,CreatedAt: time.Now().Unix(),UpdatedAt: time.Now().Unix(),}// 执行创建操作err = s.documentRepo.Create(ctx, document)if err != nil {logs.CtxErrorf(ctx, "create document failed, knowledgeID: %s, err: %v", req.KnowledgeID, err)return nil, err}// 更新知识库文档计数err = s.knowledgeRepo.IncrementDocumentCount(ctx, req.KnowledgeID)if err != nil {logs.CtxErrorf(ctx, "increment document count failed, knowledgeID: %s, err: %v", req.KnowledgeID, err)// 计数更新失败不影响文档创建}return &CreateDocumentResponse{DocumentID: documentID,}, nil
}// UpdateDocument 更新文档
func (s *KnowledgeSVC) UpdateDocument(ctx context.Context, req *UpdateDocumentRequest) error {// 参数验证if err := s.validateUpdateDocumentRequest(req); err != nil {return err}// 查询文档是否存在existingDoc, err := s.documentRepo.Get(ctx, req.DocumentID)if err != nil {if errors.Is(err, repository.ErrNotFound) {return errorx.New(errno.ErrDocumentNotFoundCode, "document not found")}return err}// 构建更新参数updateParams := map[string]interface{}{"title": req.Title,"content": req.Content,"version": existingDoc.Version + 1,"updated_at": time.Now().Unix(),}// 执行更新操作err = s.documentRepo.Update(ctx, req.DocumentID, updateParams)if err != nil {logs.CtxErrorf(ctx, "update document failed, documentID: %s, err: %v", req.DocumentID, err)return err}return nil
}
领域服务层设计特点
- 依赖注入:通过构造函数注入必要的依赖组件,如知识库仓库、文档仓库、ID生成器和缓存
- 参数验证:完善的参数验证机制,确保数据的合法性和完整性
- 错误处理:清晰的错误类型定义和错误处理策略
- 缓存管理:知识库更新后自动清除相关缓存
- 数据一致性:通过事务和状态管理确保数据一致性
- 版本控制:知识库和文档都支持版本号管理,用于冲突检测
- 事件驱动:与事件系统集成,支持业务事件的发布和订阅
领域服务层与其他层的协作
- 与应用服务层协作:接收应用服务层的请求,处理业务逻辑后返回结果
- 与数据访问层协作:通过Repository接口与数据访问层交互,实现数据持久化
- 与基础设施层协作:使用ID生成器、缓存等基础设施组件
领域服务层作为业务逻辑的核心,实现了知识库编辑场景下的所有业务规则和处理流程,确保了业务逻辑的一致性和可维护性。
**实体设计特点**:
1. **基础信息**:包含ID、名称、描述等基本属性,满足知识库的基本信息需求
2. **状态管理**:Status字段支持知识库和文档的状态管理,如正常、草稿、归档等
3. **关系映射**:KnowledgeID字段在文档实体中建立与知识库的关联关系
4. **版本控制**:文档实体包含Version字段,支持内容版本管理
5. **用户追踪**:CreatorID字段记录创建者信息,支持权限控制和审计
6. **时间追踪**:CreatedAt和UpdatedAt字段支持创建和更新时间追踪,便于版本管理## 5. 数据访问层### 5.1 仓储接口定义#### 知识库仓储接口知识库仓储接口定义了数据访问层的抽象,为领域服务层提供数据持久化能力,实现了业务逻辑与数据存储的解耦。```go
// KnowledgeRepo 知识库仓储接口
type KnowledgeRepo interface {// 创建知识库CreateKnowledge(ctx context.Context, knowledge *knowledgeEntity.Knowledge) (string, error)// 更新知识库UpdateKnowledge(ctx context.Context, knowledgeID string, updates map[string]interface{}) error// 删除知识库DeleteKnowledge(ctx context.Context, knowledgeID string) error// 获取知识库GetKnowledge(ctx context.Context, knowledgeID string) (*knowledgeEntity.Knowledge, error)// 批量获取知识库ListKnowledge(ctx context.Context, query map[string]interface{}, page, pageSize int32) ([]*knowledgeEntity.Knowledge, int64, error)// 创建文档CreateDocument(ctx context.Context, document *knowledgeEntity.Document) (string, error)// 更新文档UpdateDocument(ctx context.Context, documentID string, updates map[string]interface{}) error// 删除文档DeleteDocument(ctx context.Context, documentID string) error// 获取文档GetDocument(ctx context.Context, documentID string) (*knowledgeEntity.Document, error)// 获取知识库下的文档列表ListDocuments(ctx context.Context, knowledgeID string, query map[string]interface{}, page, pageSize int32) ([]*knowledgeEntity.Document, int64, error)
}
接口功能特点:
- 完整CRUD操作:支持知识库和文档的创建、读取、更新、删除操作
- 查询能力:提供单条查询和批量查询接口,支持条件筛选和分页
- 批量操作:支持批量获取知识库和文档,提高数据处理效率
- 解耦设计:通过接口实现业务层与数据层的解耦,便于测试和替换实现
- 参数灵活性:使用map[string]interface{}作为更新参数,支持灵活的字段更新
- 统计支持:批量查询接口返回总数,支持分页和统计需求
5. 数据访问层
5.1 仓储接口定义
知识库仓储接口
文件位置:backend/domain/knowledge/repository/repository.go
//go:generate mockgen -destination ../internal/mock/dal/dao/knowledge.go --package dao -source knowledge.go
type KnowledgeRepo interface {Create(ctx context.Context, knowledge *model.Knowledge) errorUpsert(ctx context.Context, knowledge *model.Knowledge) errorUpdate(ctx context.Context, knowledge *model.Knowledge) errorDelete(ctx context.Context, id int64) errorGetByID(ctx context.Context, id int64) (*model.Knowledge, error)MGetByID(ctx context.Context, ids []int64) ([]*model.Knowledge, error)FilterEnableKnowledge(ctx context.Context, ids []int64) ([]*model.Knowledge, error)InitTx() (tx *gorm.DB, err error)UpdateWithTx(ctx context.Context, tx *gorm.DB, knowledgeID int64, updateMap map[string]interface{}) errorFindKnowledgeByCondition(ctx context.Context, opts *entity.WhereKnowledgeOption) ([]*model.Knowledge, int64, error)
}//go:generate mockgen -destination ../internal/mock/dal/dao/knowledge_document.go --package dao -source knowledge_document.go
type KnowledgeDocumentRepo interface {Create(ctx context.Context, document *model.KnowledgeDocument) errorUpdate(ctx context.Context, document *model.KnowledgeDocument) errorDelete(ctx context.Context, id int64) errorMGetByID(ctx context.Context, ids []int64) ([]*model.KnowledgeDocument, error)GetByID(ctx context.Context, id int64) (*model.KnowledgeDocument, error)FindDocumentByCondition(ctx context.Context, opts *entity.WhereDocumentOpt) ([]*model.KnowledgeDocument, int64, error)DeleteDocuments(ctx context.Context, ids []int64) errorSetStatus(ctx context.Context, documentID int64, status int32, reason string) errorCreateWithTx(ctx context.Context, tx *gorm.DB, document []*model.KnowledgeDocument) errorUpdateDocumentSliceInfo(ctx context.Context, documentID int64) error
}// 文档切片仓储接口
type KnowledgeDocumentSliceRepo interface {Create(ctx context.Context, slice *model.KnowledgeDocumentSlice) errorUpdate(ctx context.Context, slice *model.KnowledgeDocumentSlice) errorDelete(ctx context.Context, slice *model.KnowledgeDocumentSlice) errorBatchCreateWithTX(ctx context.Context, tx *gorm.DB, slices []*model.KnowledgeDocumentSlice) errorBatchCreate(ctx context.Context, slices []*model.KnowledgeDocumentSlice) errorBatchSetStatus(ctx context.Context, ids []int64, status int32, reason string) errorDeleteByDocument(ctx context.Context, documentID int64) errorMGetSlices(ctx context.Context, sliceIDs []int64) ([]*model.KnowledgeDocumentSlice, error)ListPhotoSlice(ctx context.Context, opts *entity.WherePhotoSliceOpt) ([]*model.KnowledgeDocumentSlice, int64, error)FindSliceByCondition(ctx context.Context, opts *entity.WhereSliceOpt) ([]*model.KnowledgeDocumentSlice, int64, error)GetDocumentSliceIDs(ctx context.Context, docIDs []int64) (sliceIDs []int64, err error)GetSliceBySequence(ctx context.Context, documentID int64, sequence int64) ([]*model.KnowledgeDocumentSlice, error)IncrementHitCount(ctx context.Context, sliceIDs []int64) errorGetSliceHitByKnowledgeID(ctx context.Context, knowledgeID int64) (int64, error)GetLastSequence(ctx context.Context, documentID int64) (float64, error)
}
接口功能特点:
- 分层仓储设计:将知识库、文档和文档切片的操作分别封装为独立接口,实现关注点分离
- 完整的CRUD操作:提供知识库及其关联资源的创建、读取、更新和删除功能
- 事务支持:通过
InitTx
和UpdateWithTx
等方法支持复杂的事务操作,确保数据一致性 - 灵活的查询能力:通过
FindKnowledgeByCondition
和FindDocumentByCondition
等方法支持复杂条件查询 - 批量操作优化:提供批量查询、批量创建和批量删除等操作,提高数据处理效率
- 状态管理:专门的
SetStatus
方法用于管理文档状态,支持知识管理的生命周期控制 - 解耦设计:通过接口抽象实现业务层与数据访问层的解耦,便于测试和扩展
- 代码生成支持:使用go:generate指令支持自动生成mock实现,提高测试覆盖率
- 性能优化:提供增量更新、条件过滤等机制优化查询性能
- 完整的切片管理:专门的切片仓储接口支持知识文档的切片操作,包括批量创建、状态管理和序列控制
5.2 数据访问对象(DAO)
KnowledgeDAO 实现
KnowledgeDAO是知识库数据访问的核心实现,负责与数据库直接交互,处理知识库和文档的持久化操作。
// KnowledgeDAO 知识库数据访问对象
type KnowledgeDAO struct {DB *gorm.DB
}// NewKnowledgeDAO 创建知识库DAO实例
func NewKnowledgeDAO(db *gorm.DB) *KnowledgeDAO {return &KnowledgeDAO{DB: db,}
}// CreateKnowledge 创建知识库
func (dao *KnowledgeDAO) CreateKnowledge(ctx context.Context, knowledge *knowledgeEntity.Knowledge) (string, error) {result := dao.DB.WithContext(ctx).Create(knowledge)if result.Error != nil {logs.CtxErrorf(ctx, "create knowledge failed, err: %v", result.Error)return "", result.Error}return knowledge.ID, nil
}// UpdateKnowledge 更新知识库
func (dao *KnowledgeDAO) UpdateKnowledge(ctx context.Context, knowledgeID string, updates map[string]interface{}) error {result := dao.DB.WithContext(ctx).Model(&knowledgeEntity.Knowledge{}).Where("id = ?", knowledgeID).Updates(updates)if result.Error != nil {logs.CtxErrorf(ctx, "update knowledge failed, knowledgeID: %s, err: %v", knowledgeID, result.Error)return result.Error}if result.RowsAffected == 0 {return errors.New("knowledge not found")}return nil
}// GetKnowledge 获取知识库
func (dao *KnowledgeDAO) GetKnowledge(ctx context.Context, knowledgeID string) (*knowledgeEntity.Knowledge, error) {var knowledge knowledgeEntity.Knowledgeresult := dao.DB.WithContext(ctx).Where("id = ?", knowledgeID).First(&knowledge)if result.Error != nil {if errors.Is(result.Error, gorm.ErrRecordNotFound) {return nil, errors.New("knowledge not found")}logs.CtxErrorf(ctx, "get knowledge failed, knowledgeID: %s, err: %v", knowledgeID, result.Error)return nil, result.Error}return &knowledge, nil
}// CreateDocument 创建文档
func (dao *KnowledgeDAO) CreateDocument(ctx context.Context, document *knowledgeEntity.Document) (string, error) {result := dao.DB.WithContext(ctx).Create(document)if result.Error != nil {logs.CtxErrorf(ctx, "create document failed, err: %v", result.Error)return "", result.Error}return document.ID, nil
}// UpdateDocument 更新文档
func (dao *KnowledgeDAO) UpdateDocument(ctx context.Context, documentID string, updates map[string]interface{}) error {result := dao.DB.WithContext(ctx).Model(&knowledgeEntity.Document{}).Where("id = ?", documentID).Updates(updates)if result.Error != nil {logs.CtxErrorf(ctx, "update document failed, documentID: %s, err: %v", documentID, result.Error)return result.Error}if result.RowsAffected == 0 {return errors.New("document not found")}return nil
}// GetDocument 获取文档
func (dao *KnowledgeDAO) GetDocument(ctx context.Context, documentID string) (*knowledgeEntity.Document, error) {var document knowledgeEntity.Documentresult := dao.DB.WithContext(ctx).Where("id = ?", documentID).First(&document)if result.Error != nil {if errors.Is(result.Error, gorm.ErrRecordNotFound) {return nil, errors.New("document not found")}logs.CtxErrorf(ctx, "get document failed, documentID: %s, err: %v", documentID, result.Error)return nil, result.Error}return &document, nil
}// ListDocuments 获取文档列表
func (dao *KnowledgeDAO) ListDocuments(ctx context.Context, knowledgeID string, query map[string]interface{}, page, pageSize int32) ([]*knowledgeEntity.Document, int64, error) {var documents []*knowledgeEntity.Documentvar total int64// 构建查询条件db := dao.DB.WithContext(ctx).Model(&knowledgeEntity.Document{}).Where("knowledge_id = ?", knowledgeID)// 添加额外查询条件for key, value := range query {db = db.Where(key, value)}// 计算总数if err := db.Count(&total).Error; err != nil {logs.CtxErrorf(ctx, "count documents failed, err: %v", err)return nil, 0, err}// 计算偏移量offset := (page - 1) * pageSize// 执行查询if err := db.Order("created_at DESC").Offset(int(offset)).Limit(int(pageSize)).Find(&documents).Error; err != nil {logs.CtxErrorf(ctx, "list documents failed, err: %v", err)return nil, 0, err}return documents, total, nil
}
实现特点:
- GORM集成:使用GORM框架进行数据库操作,简化CRUD操作
- 参数验证:在更新操作后检查受影响的行数,确保数据存在
- 错误处理:完善的错误捕获和日志记录,便于问题排查
- 上下文传递:通过context进行请求上下文传递,支持请求追踪
- 灵活查询:支持条件查询、分页查询和排序,满足各种查询需求
- 错误类型区分:区分记录不存在和其他数据库错误类型
5.3 仓库实现类
仓库实现类将领域服务的调用转换为数据访问层的操作,实现了业务实体与数据对象的转换。
// KnowledgeRepoImpl 知识库仓储实现
type KnowledgeRepoImpl struct {knowledgeDAO *KnowledgeDAOidGenerator idgen.IDGenerator
}// NewKnowledgeRepo 创建知识库仓储实例
func NewKnowledgeRepo(knowledgeDAO *KnowledgeDAO, idGenerator idgen.IDGenerator) KnowledgeRepo {return &KnowledgeRepoImpl{knowledgeDAO: knowledgeDAO,idGenerator: idGenerator,}
}// CreateKnowledge 创建知识库
func (r *KnowledgeRepoImpl) CreateKnowledge(ctx context.Context, knowledge *knowledgeEntity.Knowledge) (int64, error) {// 生成IDif knowledge.ID == 0 {knowledge.ID = r.idGenerator.GenID()}// 设置默认时间now := time.Now().Unix()if knowledge.CreatedAt == 0 {knowledge.CreatedAt = now}if knowledge.UpdatedAt == 0 {knowledge.UpdatedAt = now}return r.knowledgeDAO.CreateKnowledge(ctx, knowledge)
}// UpdateKnowledge 更新知识库
func (r *KnowledgeRepoImpl) UpdateKnowledge(ctx context.Context, knowledgeID int64, updates map[string]interface{}) error {return r.knowledgeDAO.UpdateKnowledge(ctx, knowledgeID, updates)
}// CreateDocument 创建文档
func (r *KnowledgeRepoImpl) CreateDocument(ctx context.Context, document *knowledgeEntity.Document) (int64, error) {// 生成IDif document.ID == 0 {document.ID = r.idGenerator.GenID()}// 设置默认时间now := time.Now().Unix()if document.CreatedAt == 0 {document.CreatedAt = now}if document.UpdatedAt == 0 {document.UpdatedAt = now}return r.knowledgeDAO.CreateDocument(ctx, document)
}// UpdateDocument 更新文档
func (r *KnowledgeRepoImpl) UpdateDocument(ctx context.Context, documentID int64, updates map[string]interface{}) error {return r.knowledgeDAO.UpdateDocument(ctx, documentID, updates)
}
实现特点:
- ID生成:通过ID生成器为实体分配唯一ID
- 时间管理:自动设置创建和更新时间
- 数据转换:在领域实体和数据对象之间进行转换
- 依赖注入:通过构造函数注入必要的依赖组件
- 简洁实现:主要负责调用DAO层方法,保持实现简洁
5.4 数据模型
知识库相关的数据模型定义了数据库表结构和字段映射关系。
// Knowledge 知识库表模型
type Knowledge struct {ID int64 `gorm:"primaryKey;autoIncrement" json:"id"`SpaceID int64 `gorm:"not null;index" json:"space_id"`OwnerID int64 `gorm:"not null;index" json:"owner_id"`Name string `gorm:"size:255;not null;index:idx_name,length:100" json:"name"`Description string `gorm:"type:text" json:"description"`CoverImage string `gorm:"size:255" json:"cover_image"`Tags datatypes.JSON `gorm:"type:json" json:"tags"`Settings datatypes.JSON `gorm:"type:json" json:"settings"`Version string `gorm:"size:255;index:idx_version" json:"version"`CreatedAt int64 `gorm:"autoCreateTime" json:"created_at"`UpdatedAt int64 `gorm:"autoUpdateTime;index:idx_updated_at" json:"updated_at"`LastEditorID int64 `json:"last_editor_id"`EditCount uint `json:"edit_count"`LockStatus int `gorm:"default:0;index:idx_lock_status" json:"lock_status"`LockExpireTime int64 `json:"lock_expire_time"`
}// TableName 指定表名
func (Knowledge) TableName() string {return "knowledge"
}// Document 文档表模型
type Document struct {ID int64 `gorm:"primaryKey;autoIncrement" json:"id"`KnowledgeID int64 `gorm:"not null;index:idx_knowledge_id" json:"knowledge_id"`DocumentID string `gorm:"size:64;not null;index:idx_document_id" json:"document_id"`Title string `gorm:"size:255;not null;index:idx_title,length:100" json:"title"`Content string `gorm:"type:longtext" json:"content"`ContentHash string `gorm:"size:64" json:"content_hash"`Metadata datatypes.JSON `gorm:"type:json" json:"metadata"`Version string `gorm:"size:255" json:"version"`CreatedAt int64 `gorm:"autoCreateTime" json:"created_at"`UpdatedAt int64 `gorm:"autoUpdateTime;index:idx_updated_at" json:"updated_at"`LastEditorID int64 `json:"last_editor_id"`
}// TableName 指定表名
func (Document) TableName() string {return "knowledge_document"
}// 确保知识库和文档的唯一约束
func (d *Document) TableName() string {// 知识库文档表有复合唯一索引: uniq_knowledge_document (knowledge_id, document_id)return "knowledge_document"
}
模型特点:
- 编辑冲突控制:Version字段用于乐观锁和编辑冲突检测
- 锁定管理:LockStatus和LockExpireTime字段支持编辑锁定管理
- 编辑审计:LastEditorID和EditCount字段跟踪编辑历史
- 全文存储:Content字段使用longtext类型存储大型文档内容
- 变更检测:ContentHash字段用于快速检测文档内容变更
- JSON支持:Tags、Settings和Metadata字段使用JSON类型,支持灵活编辑
- 索引优化:为name、title、updated_at等常用查询字段添加索引
- 唯一约束:知识文档表有复合唯一索引(knowledge_id, document_id),防止编辑时冲突
5.5 数据库表结构
knowledge表:
字段名 | 数据类型 | 约束 | 描述 |
---|---|---|---|
id | bigint | PRIMARY KEY AUTO_INCREMENT | 知识库ID |
space_id | bigint | NOT NULL, INDEX | 工作空间ID |
owner_id | bigint | NOT NULL, INDEX | 所有者ID |
name | varchar(255) | NOT NULL, INDEX(name(100)) | 知识库名称 |
description | text | 知识库描述 | |
cover_image | varchar(255) | 封面图片URL | |
tags | json | 知识库标签 | |
settings | json | 知识库设置 | |
version | varchar(255) | INDEX | 版本号(用于编辑冲突) |
created_at | bigint | NOT NULL | 创建时间戳(毫秒) |
updated_at | bigint | NOT NULL, INDEX | 更新时间戳(毫秒) |
last_editor_id | bigint | 最后编辑者ID | |
edit_count | int unsigned | 编辑次数 | |
lock_status | int | DEFAULT 0, INDEX | 锁定状态(0=未锁定,1=已锁定) |
lock_expire_time | bigint | 锁定过期时间戳 |
knowledge_document表:
字段名 | 数据类型 | 约束 | 描述 |
---|---|---|---|
id | bigint | PRIMARY KEY AUTO_INCREMENT | 文档ID |
knowledge_id | bigint | NOT NULL, INDEX | 所属知识库ID |
document_id | varchar(64) | NOT NULL, INDEX | 文档标识 |
title | varchar(255) | NOT NULL, INDEX(title(100)) | 文档标题 |
content | longtext | 文档内容 | |
content_hash | varchar(64) | 内容哈希(用于变更检测) | |
metadata | json | 文档元数据 | |
version | varchar(255) | 文档版本号 | |
created_at | bigint | NOT NULL | 创建时间戳(毫秒) |
updated_at | bigint | NOT NULL, INDEX | 更新时间戳(毫秒) |
last_editor_id | bigint | 最后编辑者ID |
5.6 数据访问层编辑操作特点
知识库编辑操作特点:
- 版本控制:使用version字段实现乐观锁,防止并发编辑冲突
- 编辑锁定:支持数据库级别的编辑锁定机制,避免编辑冲突
- 事务保证:所有编辑操作在事务中执行,确保数据一致性
- 内容哈希:通过content_hash字段快速检测文档内容变更
- 部分更新:支持字段级别的部分更新,提高编辑效率
- 索引优化:为常用编辑查询字段添加索引,提升性能
- 审计追踪:记录最后编辑者和编辑时间,支持操作审计
- 编辑统计:维护edit_count字段,跟踪编辑频率
编辑操作的数据访问流程:
在编辑知识库的场景中,数据访问层的操作流程如下:
- 事务开始:开启数据库事务,确保操作原子性
- 版本检查:验证传入的version字段,检测编辑冲突
- 数据更新:更新知识库/文档数据,包括内容和元数据
- 版本递增:更新version字段,防止后续编辑冲突
- 编辑统计:更新last_editor_id和edit_count等统计信息
- 事务提交:所有操作成功后提交事务
- 错误处理:任何步骤失败时回滚事务并返回错误
数据访问层编辑操作总结:
- 编辑事务性:所有编辑操作在事务中执行,确保数据一致性
- 多表协同:同时维护知识库和文档的编辑状态
- 冲突检测:通过版本控制和锁定机制防止编辑冲突
- 编辑安全:完善的权限验证和错误处理机制
- 内容完整性:通过content_hash确保编辑内容的一致性
- 编辑性能:合理的索引设计和部分更新支持,确保高效编辑
- 可扩展性:模块化设计支持未来编辑功能的扩展
- 事务更新:UpdateWithTX方法支持事务操作,确保数据一致性
- 参数构建:智能构建更新参数,优化数据库操作
- 数据验证:更新前进行数据验证,确保数据有效性
- 错误处理:完善的错误处理和日志记录
- 缓存管理:支持缓存的删除操作,保持数据一致性