package routing import ( "context" "database/sql" "encoding/json" "errors" "fmt" "time" dbx "github.com/wangjia/pangolin/server/internal/db" ) // Store wraps a *sql.DB and exposes the routing_profiles CRUD the routing // package needs. type Store struct { db *sql.DB dialect dbx.Dialect } // NewStore creates a Store backed by the given connection pool (MySQL or SQLite). func NewStore(db *sql.DB) *Store { return &Store{db: db, dialect: dbx.DialectForDB(db)} } // Get returns the user's routing profile, or (nil, nil) if none is set yet // (caller should fall back to Default()). func (s *Store) Get(ctx context.Context, userID int64) (*Profile, error) { var raw string err := s.db.QueryRowContext(ctx, `SELECT profile_json FROM routing_profiles WHERE user_id = ?`, userID).Scan(&raw) if errors.Is(err, sql.ErrNoRows) { return nil, nil } if err != nil { return nil, fmt.Errorf("store.Get: %w", err) } var p Profile if err := json.Unmarshal([]byte(raw), &p); err != nil { return nil, fmt.Errorf("store.Get: unmarshal: %w", err) } return &p, nil } // Upsert creates or overwrites the user's routing profile. func (s *Store) Upsert(ctx context.Context, userID int64, p *Profile) error { raw, err := json.Marshal(p) if err != nil { return fmt.Errorf("store.Upsert: marshal: %w", err) } now := time.Now().UTC() q := `INSERT INTO routing_profiles (user_id, profile_json, updated_at) VALUES (?,?,?) ` + s.dialect.Upsert([]string{"user_id"}, "profile_json = EXCLUDED.profile_json", "updated_at = EXCLUDED.updated_at") if _, err := s.db.ExecContext(ctx, q, userID, string(raw), now); err != nil { return fmt.Errorf("store.Upsert: %w", err) } return nil }