Files
WeiCHEN dca33c705d refactor(boundaries): split five oversized packages by use-case
Move bizdev/freedoc/intent/projectworkflow/squad giant files into
≤800-line use-case siblings without behavior change; short tests green.
2026-09-23 09:52:16 +08:00

190 lines
4.3 KiB
Go

package freedoc
import (
"context"
"errors"
"time"
"github.com/jackc/pgx/v5"
)
// ShareDocument 将私密文档共享至项目参考库(仅 owner 可操作)
func (s *Service) ShareDocument(ctx context.Context, userID int, docID string) (*FreeformDocument, error) {
if docID == "" {
return nil, ErrNotFound
}
if s.pool != nil {
var doc FreeformDocument
row := s.pool.QueryRow(ctx, `
SELECT doc_id, project_id, owner_user_id, space, is_deleted
FROM freeform_documents WHERE doc_id = $1
`, docID)
if err := row.Scan(&doc.DocID, &doc.ProjectID, &doc.OwnerUserID, &doc.Space, &doc.IsDeleted); err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return nil, ErrNotFound
}
return nil, err
}
if doc.IsDeleted {
return nil, ErrNotFound
}
if doc.OwnerUserID != userID {
return nil, ErrForbidden
}
now := time.Now()
_, err := s.pool.Exec(ctx, `
UPDATE freeform_documents
SET space = 'project_shared', updated_at = $1
WHERE doc_id = $2
`, now, docID)
if err != nil {
return nil, err
}
_, _ = s.pool.Exec(ctx, `
INSERT INTO document_access_audit (doc_id, user_id, action, success, created_at)
VALUES ($1, $2, 'share', TRUE, $3)
`, docID, userID, now)
doc.Space = "project_shared"
doc.UpdatedAt = now
return &doc, nil
}
// 内存模式
s.mu.Lock()
defer s.mu.Unlock()
doc, ok := s.memDocs[docID]
if !ok || doc.IsDeleted {
return nil, ErrNotFound
}
if doc.OwnerUserID != userID {
return nil, ErrForbidden
}
doc.Space = "project_shared"
doc.UpdatedAt = time.Now()
return doc, nil
}
// UnshareDocument 撤回共享,恢复为个人私密
func (s *Service) UnshareDocument(ctx context.Context, userID int, docID string) (*FreeformDocument, error) {
if docID == "" {
return nil, ErrNotFound
}
if s.pool != nil {
var doc FreeformDocument
row := s.pool.QueryRow(ctx, `
SELECT doc_id, project_id, owner_user_id, space, is_deleted
FROM freeform_documents WHERE doc_id = $1
`, docID)
if err := row.Scan(&doc.DocID, &doc.ProjectID, &doc.OwnerUserID, &doc.Space, &doc.IsDeleted); err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return nil, ErrNotFound
}
return nil, err
}
if doc.IsDeleted {
return nil, ErrNotFound
}
if doc.OwnerUserID != userID {
return nil, ErrForbidden
}
now := time.Now()
_, err := s.pool.Exec(ctx, `
UPDATE freeform_documents
SET space = 'my_private', updated_at = $1
WHERE doc_id = $2
`, now, docID)
if err != nil {
return nil, err
}
_, _ = s.pool.Exec(ctx, `
INSERT INTO document_access_audit (doc_id, user_id, action, success, created_at)
VALUES ($1, $2, 'unshare', TRUE, $3)
`, docID, userID, now)
doc.Space = "my_private"
doc.UpdatedAt = now
return &doc, nil
}
// 内存模式
s.mu.Lock()
defer s.mu.Unlock()
doc, ok := s.memDocs[docID]
if !ok || doc.IsDeleted {
return nil, ErrNotFound
}
if doc.OwnerUserID != userID {
return nil, ErrForbidden
}
doc.Space = "my_private"
doc.UpdatedAt = time.Now()
return doc, nil
}
// DeleteDocument 软删除文档(仅 owner 可操作)
func (s *Service) DeleteDocument(ctx context.Context, userID int, docID string) error {
if docID == "" {
return ErrNotFound
}
if s.pool != nil {
var doc FreeformDocument
row := s.pool.QueryRow(ctx, `
SELECT doc_id, owner_user_id, is_deleted
FROM freeform_documents WHERE doc_id = $1
`, docID)
if err := row.Scan(&doc.DocID, &doc.OwnerUserID, &doc.IsDeleted); err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return ErrNotFound
}
return err
}
if doc.IsDeleted {
return nil
}
if doc.OwnerUserID != userID {
return ErrForbidden
}
now := time.Now()
_, err := s.pool.Exec(ctx, `
UPDATE freeform_documents
SET is_deleted = TRUE, deleted_at = $1, space = 'trash'
WHERE doc_id = $2
`, now, docID)
if err != nil {
return err
}
_, _ = s.pool.Exec(ctx, `
INSERT INTO document_access_audit (doc_id, user_id, action, success, created_at)
VALUES ($1, $2, 'delete', TRUE, $3)
`, docID, userID, now)
return nil
}
// 内存模式
s.mu.Lock()
defer s.mu.Unlock()
doc, ok := s.memDocs[docID]
if !ok || doc.IsDeleted {
return ErrNotFound
}
if doc.OwnerUserID != userID {
return ErrForbidden
}
doc.IsDeleted = true
now := time.Now()
doc.DeletedAt = &now
doc.Space = "trash"
return nil
}