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()
|
|
|
|
}
|