package postgres import ( "context" "database/sql" "time" "github.com/jmoiron/sqlx" "google.golang.org/protobuf/types/known/timestamppb" pb "datarush/pkg/api/achievements" ) type AchievementRepository struct { db *sqlx.DB } func NewAchievementRepository(db *sqlx.DB) *AchievementRepository { return &AchievementRepository{db: db} } func (r *AchievementRepository) CreateAchievement(ctx context.Context, achievement *pb.Achievement) (*pb.Achievement, error) { query := `INSERT INTO achievements (name, description, icon_url) VALUES ($1, $2, $3) RETURNING id` var createdAchievement pb.Achievement err := r.db.QueryRowContext(ctx, query, achievement.Name, achievement.Description, achievement.IconUrl). Scan(&createdAchievement.Id) if err != nil { return nil, err } createdAchievement.Name = achievement.Name createdAchievement.Description = achievement.Description createdAchievement.IconUrl = achievement.IconUrl return &createdAchievement, nil } func (r *AchievementRepository) GetAchievement(ctx context.Context, id string) (*pb.Achievement, error) { query := `SELECT id, name, description, icon_url FROM achievements WHERE id = $1` var achievement pb.Achievement err := r.db.QueryRowContext(ctx, query, id). Scan(&achievement.Id, &achievement.Name, &achievement.Description, &achievement.IconUrl) if err != nil { if err == sql.ErrNoRows { return nil, sql.ErrNoRows } return nil, err } return &achievement, nil } func (r *AchievementRepository) EditAchievement(ctx context.Context, achievement *pb.Achievement) (*pb.Achievement, error) { query := `UPDATE achievements SET name = $1, description = $2, icon_url = $3, updated_at = NOW() WHERE id = $4` _, err := r.db.ExecContext(ctx, query, achievement.Name, achievement.Description, achievement.IconUrl, achievement.Id) if err != nil { return nil, err } return achievement, nil } func (r *AchievementRepository) DeleteAchievement(ctx context.Context, id string) error { _, err := r.db.ExecContext(ctx, "DELETE FROM achievements WHERE id = $1", id) return err } func (r *AchievementRepository) ListAchievements(ctx context.Context) ([]*pb.Achievement, error) { query := `SELECT id, name, description, icon_url FROM achievements ORDER BY created_at DESC` rows, err := r.db.QueryContext(ctx, query) if err != nil { return nil, err } defer rows.Close() var achievements []*pb.Achievement for rows.Next() { var achievement pb.Achievement err := rows.Scan(&achievement.Id, &achievement.Name, &achievement.Description, &achievement.IconUrl) if err != nil { return nil, err } achievements = append(achievements, &achievement) } if err = rows.Err(); err != nil { return nil, err } return achievements, nil } func (r *AchievementRepository) GetUserAchievements(ctx context.Context, userID string) ([]*pb.AchievementUser, error) { query := `SELECT a.id, a.name, a.description, a.icon_url, ua.received_at FROM user_achievements ua INNER JOIN achievements a ON ua.achievement_id = a.id WHERE ua.user_id = $1 ORDER BY ua.received_at DESC` rows, err := r.db.QueryContext(ctx, query, userID) if err != nil { return nil, err } defer rows.Close() var userAchievements []*pb.AchievementUser for rows.Next() { var achievement pb.Achievement var receivedAt time.Time err := rows.Scan( &achievement.Id, &achievement.Name, &achievement.Description, &achievement.IconUrl, &receivedAt, ) if err != nil { return nil, err } userAchievement := &pb.AchievementUser{ Achievement: &achievement, ReceivedAt: timestamppb.New(receivedAt), } userAchievements = append(userAchievements, userAchievement) } if err = rows.Err(); err != nil { return nil, err } return userAchievements, nil }