Files
moteva/server/internal/infrastructure/memory/project_repository.go
T

220 lines
5.3 KiB
Go
Raw Normal View History

package memory
import (
"context"
"sync"
"img_infinite_canvas/internal/domain/design"
)
type ProjectRepository struct {
mu sync.RWMutex
projects map[string]design.Project
mockupBlobs map[string]design.MockupBlobData
}
func NewProjectRepository() *ProjectRepository {
return &ProjectRepository{
projects: make(map[string]design.Project),
mockupBlobs: make(map[string]design.MockupBlobData),
}
}
func (r *ProjectRepository) List(ctx context.Context) ([]design.ProjectSummary, error) {
if err := ctx.Err(); err != nil {
return nil, err
}
r.mu.RLock()
defer r.mu.RUnlock()
items := make([]design.ProjectSummary, 0, len(r.projects))
userID := design.UserIDFromContext(ctx)
for _, project := range r.projects {
if project.UserID == "" {
project.UserID = design.DefaultUserID
}
if project.UserID != userID {
continue
}
items = append(items, design.ProjectSummary{
ID: project.ID,
UserID: project.UserID,
Title: project.Title,
Brief: project.Brief,
Status: project.Status,
Thumbnail: project.Thumbnail,
UpdatedAt: project.UpdatedAt,
})
}
return items, nil
}
func (r *ProjectRepository) ListPage(ctx context.Context, limit int64, offset int64) ([]design.ProjectSummary, error) {
items, err := r.List(ctx)
if err != nil {
return nil, err
}
design.SortSummaries(items)
if offset < 0 {
offset = 0
}
if limit <= 0 {
limit = int64(len(items))
}
start := int(offset)
if start >= len(items) {
return []design.ProjectSummary{}, nil
}
end := start + int(limit)
if end > len(items) {
end = len(items)
}
return items[start:end], nil
}
func (r *ProjectRepository) Get(ctx context.Context, id string) (design.Project, error) {
if err := ctx.Err(); err != nil {
return design.Project{}, err
}
r.mu.RLock()
defer r.mu.RUnlock()
project, ok := r.projects[id]
if !ok {
return design.Project{}, design.ErrNotFound
}
if project.UserID == "" {
project.UserID = design.DefaultUserID
}
if project.UserID != design.UserIDFromContext(ctx) {
return design.Project{}, design.ErrNotFound
}
return cloneProject(project), nil
}
func (r *ProjectRepository) Save(ctx context.Context, project design.Project) error {
if err := ctx.Err(); err != nil {
return err
}
r.mu.Lock()
defer r.mu.Unlock()
project.UserID = repositoryUserID(ctx, project.UserID)
r.projects[project.ID] = cloneProject(project)
return nil
}
func (r *ProjectRepository) SaveCanvas(ctx context.Context, project design.Project, baseVersion string) error {
if err := ctx.Err(); err != nil {
return err
}
r.mu.Lock()
defer r.mu.Unlock()
userID := repositoryUserID(ctx, project.UserID)
current, ok := r.projects[project.ID]
if !ok {
return design.ErrNotFound
}
if current.UserID == "" {
current.UserID = design.DefaultUserID
}
if current.UserID != userID {
return design.ErrNotFound
}
if baseVersion != "" && current.Version != "" && baseVersion != current.Version {
return design.ErrConflict
}
current.UserID = userID
current.Title = project.Title
current.Brief = project.Brief
current.Status = project.Status
current.Thumbnail = project.Thumbnail
current.UpdatedAt = project.UpdatedAt
current.Version = project.Version
current.SnapshotID = project.SnapshotID
current.Canvas = project.Canvas
current.LastThreadID = project.LastThreadID
current.Viewport = project.Viewport
current.Nodes = append([]design.Node(nil), project.Nodes...)
current.Connections = append([]design.Connection(nil), project.Connections...)
r.projects[project.ID] = cloneProject(current)
return nil
}
func (r *ProjectRepository) Delete(ctx context.Context, id string) error {
if err := ctx.Err(); err != nil {
return err
}
r.mu.Lock()
defer r.mu.Unlock()
if _, ok := r.projects[id]; !ok {
return design.ErrNotFound
}
project := r.projects[id]
if project.UserID == "" {
project.UserID = design.DefaultUserID
}
if project.UserID != design.UserIDFromContext(ctx) {
return design.ErrNotFound
}
delete(r.projects, id)
return nil
}
func (r *ProjectRepository) SaveMockupBlob(ctx context.Context, blob design.MockupBlobData) error {
if err := ctx.Err(); err != nil {
return err
}
if blob.DataID == "" {
return design.ErrInvalidInput
}
r.mu.Lock()
defer r.mu.Unlock()
r.mockupBlobs[blob.DataID] = cloneMockupBlob(blob)
return nil
}
func (r *ProjectRepository) GetMockupBlob(ctx context.Context, dataID string) (design.MockupBlobData, error) {
if err := ctx.Err(); err != nil {
return design.MockupBlobData{}, err
}
r.mu.RLock()
defer r.mu.RUnlock()
blob, ok := r.mockupBlobs[dataID]
if !ok {
return design.MockupBlobData{}, design.ErrNotFound
}
return cloneMockupBlob(blob), nil
}
func repositoryUserID(ctx context.Context, userID string) string {
if userID != "" {
return design.NormalizeUserID(userID)
}
return design.UserIDFromContext(ctx)
}
func cloneProject(project design.Project) design.Project {
project.UserID = repositoryUserID(context.Background(), project.UserID)
project.Nodes = append([]design.Node(nil), project.Nodes...)
project.Connections = append([]design.Connection(nil), project.Connections...)
project.Messages = append([]design.Message(nil), project.Messages...)
return project
}
func cloneMockupBlob(blob design.MockupBlobData) design.MockupBlobData {
blob.Data = append([]byte(nil), blob.Data...)
return blob
}