package repository import ( "apskel-pos-be/internal/models" "context" "encoding/json" "fmt" "time" "github.com/google/uuid" "github.com/redis/go-redis/v9" ) const ( sessionKeyPrefix = "self_order:session:" tableSessionKeyPrefix = "self_order:table_session:" sessionTTL = 24 * time.Hour sessionStatusActive = "active" sessionStatusClosed = "closed" ) type SessionRepository interface { Create(ctx context.Context, session *models.SelfOrderSession) error GetByID(ctx context.Context, sessionID string) (*models.SelfOrderSession, error) GetActiveByTableID(ctx context.Context, tableID uuid.UUID) (*models.SelfOrderSession, error) Close(ctx context.Context, sessionID string) error CloseByTableID(ctx context.Context, tableID uuid.UUID) error } type sessionRepository struct { client *redis.Client } func NewSessionRepository(client *redis.Client) SessionRepository { return &sessionRepository{client: client} } func (r *sessionRepository) Create(ctx context.Context, session *models.SelfOrderSession) error { if session.ID == "" { session.ID = uuid.New().String() } session.Status = sessionStatusActive session.CreatedAt = time.Now() data, err := json.Marshal(session) if err != nil { return fmt.Errorf("failed to marshal session: %w", err) } sessionKey := sessionKeyPrefix + session.ID tableSessionKey := tableSessionKeyPrefix + session.TableID.String() pipe := r.client.Pipeline() pipe.Set(ctx, sessionKey, data, sessionTTL) pipe.Set(ctx, tableSessionKey, session.ID, sessionTTL) if _, err := pipe.Exec(ctx); err != nil { return fmt.Errorf("failed to store session in redis: %w", err) } return nil } func (r *sessionRepository) GetByID(ctx context.Context, sessionID string) (*models.SelfOrderSession, error) { data, err := r.client.Get(ctx, sessionKeyPrefix+sessionID).Bytes() if err != nil { if err == redis.Nil { return nil, nil } return nil, fmt.Errorf("failed to get session: %w", err) } var session models.SelfOrderSession if err := json.Unmarshal(data, &session); err != nil { return nil, fmt.Errorf("failed to unmarshal session: %w", err) } return &session, nil } func (r *sessionRepository) GetActiveByTableID(ctx context.Context, tableID uuid.UUID) (*models.SelfOrderSession, error) { sessionID, err := r.client.Get(ctx, tableSessionKeyPrefix+tableID.String()).Result() if err != nil { if err == redis.Nil { return nil, nil } return nil, fmt.Errorf("failed to get session for table: %w", err) } session, err := r.GetByID(ctx, sessionID) if err != nil { return nil, err } if session != nil && session.Status != sessionStatusActive { return nil, nil } return session, nil } func (r *sessionRepository) Close(ctx context.Context, sessionID string) error { session, err := r.GetByID(ctx, sessionID) if err != nil { return err } if session == nil { return fmt.Errorf("session not found") } now := time.Now() session.Status = sessionStatusClosed session.ClosedAt = &now data, err := json.Marshal(session) if err != nil { return fmt.Errorf("failed to marshal session: %w", err) } pipe := r.client.Pipeline() pipe.Set(ctx, sessionKeyPrefix+session.ID, data, sessionTTL) pipe.Del(ctx, tableSessionKeyPrefix+session.TableID.String()) if _, err := pipe.Exec(ctx); err != nil { return fmt.Errorf("failed to close session: %w", err) } return nil } func (r *sessionRepository) CloseByTableID(ctx context.Context, tableID uuid.UUID) error { session, err := r.GetActiveByTableID(ctx, tableID) if err != nil { return err } if session == nil { return nil } return r.Close(ctx, session.ID) }