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  // 知识库ID
Name        *string // 知识库名称
Description *string // 知识库描述
CoverUrl    *string // 封面URL
Visibility  *string // 可见性
Tags        *string // 标签
}
// Knowledge 知识库领域实体
type Knowledge struct {
ID          string    // 知识库ID
Name        string    // 知识库名称
Description string    // 知识库描述
CoverUrl    string    // 封面URL
Visibility  string    // 可见性
Tags        string    // 标签
OwnerID     string    // 所有者ID
CreatedAt   int64     // 创建时间
UpdatedAt   int64     // 更新时间
Version     int64     // 版本号
}
// Document 文档领域实体
type Document struct {
ID          string    // 文档ID
KnowledgeID string    // 所属知识库ID
Title       string    // 文档标题
Content     string    // 文档内容
Type        string    // 文档类型
Version     int32     // 文档版本
Status      int32     // 文档状态
CreatedBy   string    // 创建者ID
CreatedAt   int64     // 创建时间
UpdatedAt   int64     // 更新时间
}

知识库领域服务实现

KnowledgeSVC是知识库领域服务的核心实现,处理所有与知识库编辑相关的业务逻辑。

// KnowledgeSVC 知识库领域服务实现
type KnowledgeSVC struct {
knowledgeRepo repository.KnowledgeRepository
documentRepo  repository.DocumentRepository
idGenerator   idgen.IDGenerator
cache         cache.Cache
}
// KnowledgeSVCConfig 知识库服务配置
type KnowledgeSVCConfig struct {
KnowledgeRepo repository.KnowledgeRepository
DocumentRepo  repository.DocumentRepository
IDGenerator   idgen.IDGenerator
Cache         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"] = now
updateParams["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
}
// 生成文档ID
documentID := 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
}

领域服务层设计特点

  1. 依赖注入:通过构造函数注入必要的依赖组件,如知识库仓库、文档仓库、ID生成器和缓存
  2. 参数验证:完善的参数验证机制,确保数据的合法性和完整性
  3. 错误处理:清晰的错误类型定义和错误处理策略
  4. 缓存管理:知识库更新后自动清除相关缓存
  5. 数据一致性:通过事务和状态管理确保数据一致性
  6. 版本控制:知识库和文档都支持版本号管理,用于冲突检测
  7. 事件驱动:与事件系统集成,支持业务事件的发布和订阅

领域服务层与其他层的协作

  1. 与应用服务层协作:接收应用服务层的请求,处理业务逻辑后返回结果
  2. 与数据访问层协作:通过Repository接口与数据访问层交互,实现数据持久化
  3. 与基础设施层协作:使用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)
}

接口功能特点

  1. 完整CRUD操作:支持知识库和文档的创建、读取、更新、删除操作
  2. 查询能力:提供单条查询和批量查询接口,支持条件筛选和分页
  3. 批量操作:支持批量获取知识库和文档,提高数据处理效率
  4. 解耦设计:通过接口实现业务层与数据层的解耦,便于测试和替换实现
  5. 参数灵活性:使用map[string]interface{}作为更新参数,支持灵活的字段更新
  6. 统计支持:批量查询接口返回总数,支持分页和统计需求

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) error
Upsert(ctx context.Context, knowledge *model.Knowledge) error
Update(ctx context.Context, knowledge *model.Knowledge) error
Delete(ctx context.Context, id int64) error
GetByID(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{}) error
FindKnowledgeByCondition(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) error
Update(ctx context.Context, document *model.KnowledgeDocument) error
Delete(ctx context.Context, id int64) error
MGetByID(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) error
SetStatus(ctx context.Context, documentID int64, status int32, reason string) error
CreateWithTx(ctx context.Context, tx *gorm.DB, document []*model.KnowledgeDocument) error
UpdateDocumentSliceInfo(ctx context.Context, documentID int64) error
}
// 文档切片仓储接口
type KnowledgeDocumentSliceRepo interface {
Create(ctx context.Context, slice *model.KnowledgeDocumentSlice) error
Update(ctx context.Context, slice *model.KnowledgeDocumentSlice) error
Delete(ctx context.Context, slice *model.KnowledgeDocumentSlice) error
BatchCreateWithTX(ctx context.Context, tx *gorm.DB, slices []*model.KnowledgeDocumentSlice) error
BatchCreate(ctx context.Context, slices []*model.KnowledgeDocumentSlice) error
BatchSetStatus(ctx context.Context, ids []int64, status int32, reason string) error
DeleteByDocument(ctx context.Context, documentID int64) error
MGetSlices(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) error
GetSliceHitByKnowledgeID(ctx context.Context, knowledgeID int64) (int64, error)
GetLastSequence(ctx context.Context, documentID int64) (float64, error)
}

接口功能特点

  1. 分层仓储设计:将知识库、文档和文档切片的操作分别封装为独立接口,实现关注点分离
  2. 完整的CRUD操作:提供知识库及其关联资源的创建、读取、更新和删除功能
  3. 事务支持:通过InitTxUpdateWithTx等方法支持复杂的事务操作,确保数据一致性
  4. 灵活的查询能力:通过FindKnowledgeByConditionFindDocumentByCondition等方法支持复杂条件查询
  5. 批量操作优化:提供批量查询、批量创建和批量删除等操作,提高数据处理效率
  6. 状态管理:专门的SetStatus方法用于管理文档状态,支持知识管理的生命周期控制
  7. 解耦设计:通过接口抽象实现业务层与数据访问层的解耦,便于测试和扩展
  8. 代码生成支持:使用go:generate指令支持自动生成mock实现,提高测试覆盖率
  9. 性能优化:提供增量更新、条件过滤等机制优化查询性能
  10. 完整的切片管理:专门的切片仓储接口支持知识文档的切片操作,包括批量创建、状态管理和序列控制

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.Knowledge
result := 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.Document
result := 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.Document
var 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
}

实现特点

  1. GORM集成:使用GORM框架进行数据库操作,简化CRUD操作
  2. 参数验证:在更新操作后检查受影响的行数,确保数据存在
  3. 错误处理:完善的错误捕获和日志记录,便于问题排查
  4. 上下文传递:通过context进行请求上下文传递,支持请求追踪
  5. 灵活查询:支持条件查询、分页查询和排序,满足各种查询需求
  6. 错误类型区分:区分记录不存在和其他数据库错误类型

5.3 仓库实现类

仓库实现类将领域服务的调用转换为数据访问层的操作,实现了业务实体与数据对象的转换。

// KnowledgeRepoImpl 知识库仓储实现
type KnowledgeRepoImpl struct {
knowledgeDAO *KnowledgeDAO
idGenerator  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) {
// 生成ID
if 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) {
// 生成ID
if 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)
}

实现特点

  1. ID生成:通过ID生成器为实体分配唯一ID
  2. 时间管理:自动设置创建和更新时间
  3. 数据转换:在领域实体和数据对象之间进行转换
  4. 依赖注入:通过构造函数注入必要的依赖组件
  5. 简洁实现:主要负责调用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"
}

模型特点

  1. 编辑冲突控制:Version字段用于乐观锁和编辑冲突检测
  2. 锁定管理:LockStatus和LockExpireTime字段支持编辑锁定管理
  3. 编辑审计:LastEditorID和EditCount字段跟踪编辑历史
  4. 全文存储:Content字段使用longtext类型存储大型文档内容
  5. 变更检测:ContentHash字段用于快速检测文档内容变更
  6. JSON支持:Tags、Settings和Metadata字段使用JSON类型,支持灵活编辑
  7. 索引优化:为name、title、updated_at等常用查询字段添加索引
  8. 唯一约束:知识文档表有复合唯一索引(knowledge_id, document_id),防止编辑时冲突

5.5 数据库表结构

knowledge表

字段名数据类型约束描述
idbigintPRIMARY KEY AUTO_INCREMENT知识库ID
space_idbigintNOT NULL, INDEX工作空间ID
owner_idbigintNOT NULL, INDEX所有者ID
namevarchar(255)NOT NULL, INDEX(name(100))知识库名称
descriptiontext知识库描述
cover_imagevarchar(255)封面图片URL
tagsjson知识库标签
settingsjson知识库设置
versionvarchar(255)INDEX版本号(用于编辑冲突)
created_atbigintNOT NULL创建时间戳(毫秒)
updated_atbigintNOT NULL, INDEX更新时间戳(毫秒)
last_editor_idbigint最后编辑者ID
edit_countint unsigned编辑次数
lock_statusintDEFAULT 0, INDEX锁定状态(0=未锁定,1=已锁定)
lock_expire_timebigint锁定过期时间戳

knowledge_document表

字段名数据类型约束描述
idbigintPRIMARY KEY AUTO_INCREMENT文档ID
knowledge_idbigintNOT NULL, INDEX所属知识库ID
document_idvarchar(64)NOT NULL, INDEX文档标识
titlevarchar(255)NOT NULL, INDEX(title(100))文档标题
contentlongtext文档内容
content_hashvarchar(64)内容哈希(用于变更检测)
metadatajson文档元数据
versionvarchar(255)文档版本号
created_atbigintNOT NULL创建时间戳(毫秒)
updated_atbigintNOT NULL, INDEX更新时间戳(毫秒)
last_editor_idbigint最后编辑者ID

5.6 数据访问层编辑操作特点

知识库编辑操作特点

  1. 版本控制:使用version字段实现乐观锁,防止并发编辑冲突
  2. 编辑锁定:支持数据库级别的编辑锁定机制,避免编辑冲突
  3. 事务保证:所有编辑操作在事务中执行,确保数据一致性
  4. 内容哈希:通过content_hash字段快速检测文档内容变更
  5. 部分更新:支持字段级别的部分更新,提高编辑效率
  6. 索引优化:为常用编辑查询字段添加索引,提升性能
  7. 审计追踪:记录最后编辑者和编辑时间,支持操作审计
  8. 编辑统计:维护edit_count字段,跟踪编辑频率

编辑操作的数据访问流程

在编辑知识库的场景中,数据访问层的操作流程如下:

  1. 事务开始:开启数据库事务,确保操作原子性
  2. 版本检查:验证传入的version字段,检测编辑冲突
  3. 数据更新:更新知识库/文档数据,包括内容和元数据
  4. 版本递增:更新version字段,防止后续编辑冲突
  5. 编辑统计:更新last_editor_id和edit_count等统计信息
  6. 事务提交:所有操作成功后提交事务
  7. 错误处理:任何步骤失败时回滚事务并返回错误

数据访问层编辑操作总结

  1. 编辑事务性:所有编辑操作在事务中执行,确保数据一致性
  2. 多表协同:同时维护知识库和文档的编辑状态
  3. 冲突检测:通过版本控制和锁定机制防止编辑冲突
  4. 编辑安全:完善的权限验证和错误处理机制
  5. 内容完整性:通过content_hash确保编辑内容的一致性
  6. 编辑性能:合理的索引设计和部分更新支持,确保高效编辑
  7. 可扩展性:模块化设计支持未来编辑功能的扩展
  8. 事务更新:UpdateWithTX方法支持事务操作,确保数据一致性
  9. 参数构建:智能构建更新参数,优化数据库操作
  10. 数据验证:更新前进行数据验证,确保数据有效性
  11. 错误处理:完善的错误处理和日志记录
  12. 缓存管理:支持缓存的删除操作,保持数据一致性