feat: P1 profile/portrait API and freeze local-dev environment
Add host-first environment contracts (Local vs CI vs Prod), deps-only compose, and the Profile → Portrait → deep-access mock payment slice with device identity and auto-migrate on API startup. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
|
||||
"github.com/yuxingu/digital-psychology/apps/api/internal/model"
|
||||
)
|
||||
|
||||
// ProfileRepo persists profiles.
|
||||
type ProfileRepo struct {
|
||||
Pool *pgxpool.Pool
|
||||
}
|
||||
|
||||
// Create inserts a profile.
|
||||
func (r *ProfileRepo) Create(ctx context.Context, userID uuid.UUID, relation, name string, birth time.Time, relationType *string) (*model.Profile, error) {
|
||||
p := &model.Profile{}
|
||||
err := r.Pool.QueryRow(ctx, `
|
||||
INSERT INTO profiles(user_id, relation, display_name, birth_date, relation_type)
|
||||
VALUES ($1,$2,$3,$4,$5)
|
||||
RETURNING id, user_id, relation, display_name, birth_date, created_at`,
|
||||
userID, relation, name, birth, relationType,
|
||||
).Scan(&p.ID, &p.UserID, &p.Relation, &p.DisplayName, &p.BirthDate, &p.CreatedAt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.RelationType = relationType
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// ListByUser returns non-deleted profiles.
|
||||
func (r *ProfileRepo) ListByUser(ctx context.Context, userID uuid.UUID) ([]model.Profile, error) {
|
||||
rows, err := r.Pool.Query(ctx, `
|
||||
SELECT id, user_id, relation, display_name, birth_date, relation_type, created_at
|
||||
FROM profiles WHERE user_id=$1 AND deleted_at IS NULL
|
||||
ORDER BY created_at DESC`, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var out []model.Profile
|
||||
for rows.Next() {
|
||||
var p model.Profile
|
||||
if err := rows.Scan(&p.ID, &p.UserID, &p.Relation, &p.DisplayName, &p.BirthDate, &p.RelationType, &p.CreatedAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, p)
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
// GetForUser loads a profile owned by user.
|
||||
func (r *ProfileRepo) GetForUser(ctx context.Context, userID, profileID uuid.UUID) (*model.Profile, error) {
|
||||
p := &model.Profile{}
|
||||
err := r.Pool.QueryRow(ctx, `
|
||||
SELECT id, user_id, relation, display_name, birth_date, relation_type, created_at
|
||||
FROM profiles WHERE id=$1 AND user_id=$2 AND deleted_at IS NULL`,
|
||||
profileID, userID,
|
||||
).Scan(&p.ID, &p.UserID, &p.Relation, &p.DisplayName, &p.BirthDate, &p.RelationType, &p.CreatedAt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
Reference in New Issue
Block a user