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
}
领域服务层设计特点
- 依赖注入:通过构造函数注入必要的依赖组件,如知识库仓库、文档仓库、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) 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)
}
接口功能特点:
- 分层仓储设计:将知识库、文档和文档切片的操作分别封装为独立接口,实现关注点分离
- 完整的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.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
}
实现特点:
- GORM集成:使用GORM框架进行数据库操作,简化CRUD操作
- 参数验证:在更新操作后检查受影响的行数,确保数据存在
- 错误处理:完善的错误捕获和日志记录,便于问题排查
- 上下文传递:通过context进行请求上下文传递,支持请求追踪
- 灵活查询:支持条件查询、分页查询和排序,满足各种查询需求
- 错误类型区分:区分记录不存在和其他数据库错误类型
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)
}
实现特点:
- 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方法支持事务操作,确保数据一致性
- 参数构建:智能构建更新参数,优化数据库操作
- 数据验证:更新前进行数据验证,确保数据有效性
- 错误处理:完善的错误处理和日志记录
- 缓存管理:支持缓存的删除操作,保持数据一致性
浙公网安备 33010602011771号