package realtime import ( "context" "encoding/json" "time" "img_infinite_canvas/internal/domain/design" "github.com/google/uuid" "github.com/zeromicro/go-zero/core/logx" ) type ProjectRepository struct { next design.Repository bus Bus } func NewProjectRepository(next design.Repository, bus Bus) *ProjectRepository { return &ProjectRepository{next: next, bus: bus} } func (r *ProjectRepository) List(ctx context.Context) ([]design.ProjectSummary, error) { return r.next.List(ctx) } func (r *ProjectRepository) ListPage(ctx context.Context, limit int64, offset int64) ([]design.ProjectSummary, error) { return r.next.ListPage(ctx, limit, offset) } func (r *ProjectRepository) Get(ctx context.Context, id string) (design.Project, error) { return r.next.Get(ctx, id) } func (r *ProjectRepository) Save(ctx context.Context, project design.Project) error { if err := r.next.Save(ctx, project); err != nil { return err } r.publish(ctx, project, "project.updated") return nil } func (r *ProjectRepository) SaveCanvas(ctx context.Context, project design.Project, baseVersion string) error { if next, ok := r.next.(design.CanvasRepository); ok { if err := next.SaveCanvas(ctx, project, baseVersion); err != nil { return err } r.publish(ctx, project, "canvas.snapshot.created") return nil } return r.Save(ctx, project) } func (r *ProjectRepository) Delete(ctx context.Context, id string) error { project, err := r.next.Get(ctx, id) if err != nil { return err } if err := r.next.Delete(ctx, id); err != nil { return err } r.publish(ctx, project, "project.deleted") return nil } func (r *ProjectRepository) SaveMockupBlob(ctx context.Context, blob design.MockupBlobData) error { next, ok := r.next.(design.MockupBlobRepository) if !ok { return nil } return next.SaveMockupBlob(ctx, blob) } func (r *ProjectRepository) GetMockupBlob(ctx context.Context, dataID string) (design.MockupBlobData, error) { next, ok := r.next.(design.MockupBlobRepository) if !ok { return design.MockupBlobData{}, design.ErrNotFound } return next.GetMockupBlob(ctx, dataID) } func (r *ProjectRepository) publish(ctx context.Context, project design.Project, eventType string) { if r.bus == nil { return } event := projectEvent(project, eventType) if err := r.bus.Publish(ctx, event); err != nil { logx.Errorf("publish realtime event failed: %v", err) } } func projectEvent(project design.Project, eventType string) Event { createdAt := project.UpdatedAt if createdAt.IsZero() { createdAt = time.Now() } eventID := uuid.NewString() payload, _ := json.Marshal(map[string]any{ "id": eventID, "type": eventType, "userId": project.UserID, "projectId": project.ID, "threadId": project.LastThreadID, "version": project.Version, "snapshotId": project.SnapshotID, "createdAt": createdAt.UTC().Format(time.RFC3339Nano), }) return Event{ ID: eventID, UserID: design.NormalizeUserID(project.UserID), ProjectID: project.ID, ThreadID: project.LastThreadID, Type: eventType, Payload: payload, CreatedAt: createdAt, } }