leaf-library-3/internal/services/document/document.go

262 lines
7.6 KiB
Go
Raw Permalink Normal View History

2024-12-06 18:44:32 +00:00
package document
import (
"context"
"crypto/sha256"
"encoding/hex"
"fmt"
"time"
"gorm.io/gorm"
2024-12-10 10:22:14 +00:00
"leafdev.top/Leaf/leaf-library-3/internal/dto"
2024-12-06 18:44:32 +00:00
"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()
}
2024-12-06 19:05:33 +00:00
// 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()
}