package document import ( "context" "crypto/sha256" "encoding/hex" "fmt" "time" "gorm.io/gorm" "leafdev.top/Leaf/leaf-library-3/internal/dto" "leafdev.top/Leaf/leaf-library-3/internal/entity" ) // Get 获取文档 func (s *Service) Get(ctx context.Context, id dto.EntityId) (*entity.Document, error) { return s.dao.Document.Where(s.dao.Document.ID.Eq(uint(id))).First() } // Create 创建文档 func (s *Service) Create(ctx context.Context, name string, workspaceId, collectionId dto.EntityId, parentId *dto.EntityId) (*entity.Document, error) { doc := &entity.Document{ Name: name, WorkspaceId: workspaceId, CollectionId: collectionId, ParentId: parentId, } err := s.dao.Document.Create(doc) return doc, err } // Update 更新文档 func (s *Service) Update(ctx context.Context, id dto.EntityId, name string) (*entity.Document, error) { doc, err := s.Get(ctx, id) if err != nil { return nil, err } doc.Name = name err = s.dao.Document.Save(doc) return doc, err } // Delete 删除文档 func (s *Service) Delete(ctx context.Context, id dto.EntityId) error { _, err := s.dao.Document.Where(s.dao.Document.ID.Eq(uint(id))).Delete() return err } // ListDocuments 列出文档树 func (s *Service) ListDocuments(ctx context.Context, collectionId dto.EntityId, parentId *dto.EntityId) ([]*entity.Document, error) { q := s.dao.WithContext(ctx).Document. Where(s.dao.Document.CollectionId.Eq(collectionId.Uint())). Order(s.dao.Document.CreatedAt) if parentId == nil { // 获取根文档 q = q.Where(s.dao.Document.ParentId.IsNull()) } else { // 获取指定父文档下的子文档 q = q.Where(s.dao.Document.ParentId.Eq(parentId.Uint())) } return q.Find() } // generateBlockHash 生成块的哈希值 func (s *Service) generateBlockHash(documentId dto.EntityId, blockType string, content string, timestamp time.Time) string { // 组合所有相关信息 data := fmt.Sprintf("%d:%s:%s:%d", documentId, blockType, content, timestamp.UnixNano()) // 计算 SHA-256 摘要 hash := sha256.Sum256([]byte(data)) // 转换为十六进制字符串 return hex.EncodeToString(hash[:]) } // CreateBlock 创建文档块 func (s *Service) CreateBlock(ctx context.Context, documentId dto.EntityId, blockType string, content string, afterBlockId *dto.EntityId) (*entity.DocumentBlock, error) { // 生成块的哈希值 now := time.Now() hash := s.generateBlockHash(documentId, blockType, content, now) var order float64 if afterBlockId == nil { order = 1000.0 } else { // 获取前一个块 prevBlock, err := s.dao.WithContext(ctx).DocumentBlock.Where(s.dao.DocumentBlock.ID.Eq(afterBlockId.Uint())).First() if err != nil { return nil, err } // 获取前一个块后面的第一个块 nextBlock, err := s.dao.WithContext(ctx).DocumentBlock. Where(s.dao.DocumentBlock.DocumentId.Eq(uint(documentId))). Where(s.dao.DocumentBlock.Order_.Gt(prevBlock.Order)). Order(s.dao.DocumentBlock.Order_). First() if err == gorm.ErrRecordNotFound { // 如果没有后续块,则在前一个块的序号上加 1000 order = prevBlock.Order + 1000.0 } else if err != nil { return nil, err } else { // 如果有后续块,则取前一个块和后一个块序号的中间值 order = (prevBlock.Order + nextBlock.Order) / 2 } } block := &entity.DocumentBlock{ DocumentId: documentId, Type: blockType, Content: content, Order: order, Hash: hash, } err := s.dao.WithContext(ctx).DocumentBlock.Create(block) return block, err } // UpdateBlock 更新文档块 func (s *Service) UpdateBlock(ctx context.Context, blockId dto.EntityId, content string) (*entity.DocumentBlock, error) { block, err := s.dao.WithContext(ctx).DocumentBlock.Where(s.dao.DocumentBlock.ID.Eq(blockId.Uint())).First() if err != nil { return nil, err } // 更新内容和哈希 now := time.Now() block.Content = content block.Hash = s.generateBlockHash(block.DocumentId, block.Type, content, now) err = s.dao.WithContext(ctx).DocumentBlock.Save(block) return block, err } // DeleteBlock 删除文档块 func (s *Service) DeleteBlock(ctx context.Context, blockId dto.EntityId) error { _, err := s.dao.WithContext(ctx).DocumentBlock.Where(s.dao.DocumentBlock.ID.Eq(blockId.Uint())).Delete() return err } // ListBlocks 列出文档的所有块 func (s *Service) ListBlocks(ctx context.Context, documentId dto.EntityId) ([]*entity.DocumentBlock, error) { return s.dao.WithContext(ctx).DocumentBlock. Where(s.dao.DocumentBlock.DocumentId.Eq(uint(documentId))). Order(s.dao.DocumentBlock.Order_). Find() } // MoveBlock 移动文档块 func (s *Service) MoveBlock(ctx context.Context, blockId dto.EntityId, afterBlockId *dto.EntityId) error { block, err := s.dao.WithContext(ctx).DocumentBlock.Where(s.dao.DocumentBlock.ID.Eq(blockId.Uint())).First() if err != nil { return err } var newOrder float64 if afterBlockId == nil { newOrder = 1000.0 } else { // 获取目标位置的前一个块 prevBlock, err := s.dao.WithContext(ctx).DocumentBlock.Where(s.dao.DocumentBlock.ID.Eq(afterBlockId.Uint())).First() if err != nil { return err } // 获取目标位置的后一个块 nextBlock, err := s.dao.WithContext(ctx).DocumentBlock. Where(s.dao.DocumentBlock.DocumentId.Eq(uint(block.DocumentId))). Where(s.dao.DocumentBlock.Order_.Gt(prevBlock.Order)). Order(s.dao.DocumentBlock.Order_). First() if err == gorm.ErrRecordNotFound { newOrder = prevBlock.Order + 1000.0 } else if err != nil { return err } else { newOrder = (prevBlock.Order + nextBlock.Order) / 2 } } block.Order = newOrder // 移动后重新生成 Hash now := time.Now() block.Hash = s.generateBlockHash(block.DocumentId, block.Type, block.Content, now) return s.dao.WithContext(ctx).DocumentBlock.Save(block) } // ReorderBlocks 重新排序文档块 func (s *Service) ReorderBlocks(ctx context.Context, documentId dto.EntityId) error { blocks, err := s.ListBlocks(ctx, documentId) if err != nil { return err } // 重新设置每个块的顺序 for i, block := range blocks { block.Order = float64((i + 1) * 1000) // 重新生成 Hash now := time.Now() block.Hash = s.generateBlockHash(block.DocumentId, block.Type, block.Content, now) if err := s.dao.WithContext(ctx).DocumentBlock.Save(block); err != nil { return err } } return nil } // HasChildren 检查文档是否有子文档 func (s *Service) HasChildren(ctx context.Context, id dto.EntityId) (bool, error) { count, err := s.dao.Document.Where(s.dao.Document.ParentId.Eq(id.Uint())).Count() if err != nil { return false, err } return count > 0, nil } // ToDTO 将文档实体转换为 DTO func (s *Service) ToDTO(ctx context.Context, doc *entity.Document) (*dto.DocumentDTO, error) { hasChildren, err := s.HasChildren(ctx, doc.ID) if err != nil { return nil, err } return &dto.DocumentDTO{ BaseDTO: dto.BaseDTO{ ID: doc.ID, CreatedAt: doc.CreatedAt, UpdatedAt: doc.UpdatedAt, }, Name: doc.Name, WorkspaceID: doc.WorkspaceId, CollectionID: doc.CollectionId, ParentID: doc.ParentId, HasChildren: hasChildren, }, nil } // List 列出文档 func (s *Service) List(ctx context.Context, collectionId dto.EntityId, parentId *dto.EntityId) ([]*entity.Document, error) { q := s.dao.Document.Where(s.dao.Document.CollectionId.Eq(uint(collectionId))) if parentId != nil { q = q.Where(s.dao.Document.ParentId.Eq(uint(*parentId))) } else { q = q.Where(s.dao.Document.ParentId.IsNull()) } return q.Find() } // GetBlock 获取文档块 func (s *Service) GetBlock(ctx context.Context, blockId dto.EntityId) (*entity.DocumentBlock, error) { return s.dao.DocumentBlock.Where(s.dao.DocumentBlock.ID.Eq(blockId.Uint())).First() }