gohorsejobs/backend/internal/infrastructure/persistence/postgres/location_repository.go
NANDO9322 1f9b54d719 fix: resolve problemas de cadastro, seletor de localização e swagger
- Corrige violação de restrição de role no Registro de Candidato (usa 'candidate' em minúsculo)
- Corrige erro de chave duplicada para slug da empresa adicionando timestamp ao workspace do candidato
- Corrige crash no LocationPicker tratando respostas nulas no frontend e retornando arrays vazios no backend
- Corrige documentação do Swagger para o endpoint de Login e adiciona definição de segurança BearerAuth
2026-01-05 13:30:02 -03:00

144 lines
4 KiB
Go

package postgres
import (
"context"
"database/sql"
"github.com/rede5/gohorsejobs/backend/internal/core/domain/entity"
)
type LocationRepository struct {
db *sql.DB
}
func NewLocationRepository(db *sql.DB) *LocationRepository {
return &LocationRepository{db: db}
}
func (r *LocationRepository) ListCountries(ctx context.Context) ([]*entity.Country, error) {
query := `SELECT id, name, iso2, iso3, phonecode, currency, emoji, emoji_u, created_at, updated_at FROM countries ORDER BY name ASC`
rows, err := r.db.QueryContext(ctx, query)
if err != nil {
return nil, err
}
defer rows.Close()
countries := []*entity.Country{}
for rows.Next() {
c := &entity.Country{}
// Scan columns. Be careful with NULL handling if fields are nullable, but schema says NOT NULL mostly.
// Check schema again. phonecode, etc can be null.
// For simplicity, we scan into sql.NullString or just string (and risk error if NULL).
// Migration 021 says:
// name VARCHAR(100) NOT NULL
// iso2 CHAR(2)
// phonecode VARCHAR(255)
// ...
// So nullable fields need handling.
var iso2, iso3, phonecode, currency, emoji, emojiU sql.NullString
if err := rows.Scan(&c.ID, &c.Name, &iso2, &iso3, &phonecode, &currency, &emoji, &emojiU, &c.CreatedAt, &c.UpdatedAt); err != nil {
return nil, err
}
c.ISO2 = iso2.String
c.ISO3 = iso3.String
c.PhoneCode = phonecode.String
c.Currency = currency.String
c.Emoji = emoji.String
c.EmojiU = emojiU.String
countries = append(countries, c)
}
return countries, nil
}
func (r *LocationRepository) ListStates(ctx context.Context, countryID int64) ([]*entity.State, error) {
query := `SELECT id, name, country_id, country_code, iso2, type, latitude, longitude FROM states WHERE country_id = $1 ORDER BY name ASC`
rows, err := r.db.QueryContext(ctx, query, countryID)
if err != nil {
return nil, err
}
defer rows.Close()
states := []*entity.State{}
for rows.Next() {
s := &entity.State{}
var iso2, typeStr sql.NullString
var lat, lng sql.NullFloat64
if err := rows.Scan(&s.ID, &s.Name, &s.CountryID, &s.CountryCode, &iso2, &typeStr, &lat, &lng); err != nil {
return nil, err
}
s.ISO2 = iso2.String
s.Type = typeStr.String
s.Latitude = lat.Float64
s.Longitude = lng.Float64
states = append(states, s)
}
return states, nil
}
func (r *LocationRepository) ListCities(ctx context.Context, stateID int64) ([]*entity.City, error) {
query := `SELECT id, name, state_id, country_id, latitude, longitude FROM cities WHERE state_id = $1 ORDER BY name ASC`
rows, err := r.db.QueryContext(ctx, query, stateID)
if err != nil {
return nil, err
}
defer rows.Close()
cities := []*entity.City{}
for rows.Next() {
c := &entity.City{}
// schema: latitude NOT NULL, longitude NOT NULL.
if err := rows.Scan(&c.ID, &c.Name, &c.StateID, &c.CountryID, &c.Latitude, &c.Longitude); err != nil {
return nil, err
}
cities = append(cities, c)
}
return cities, nil
}
func (r *LocationRepository) Search(ctx context.Context, query string, countryID int64) ([]*entity.LocationSearchResult, error) {
// Search Cities and States
// Simple LIKE query
q := "%" + query + "%"
sqlQuery := `
SELECT id, name, 'state' as type, country_id, NULL as state_id, '' as region_name
FROM states
WHERE country_id = $1 AND name ILIKE $2
UNION ALL
SELECT c.id, c.name, 'city' as type, c.country_id, c.state_id, s.name as region_name
FROM cities c
JOIN states s ON c.state_id = s.id
WHERE c.country_id = $1 AND c.name ILIKE $2
LIMIT 20
`
rows, err := r.db.QueryContext(ctx, sqlQuery, countryID, q)
if err != nil {
return nil, err
}
defer rows.Close()
results := []*entity.LocationSearchResult{}
for rows.Next() {
res := &entity.LocationSearchResult{}
var stateID sql.NullInt64
var regionName sql.NullString
if err := rows.Scan(&res.ID, &res.Name, &res.Type, &res.CountryID, &stateID, &regionName); err != nil {
return nil, err
}
if stateID.Valid {
id := stateID.Int64
res.StateID = &id
}
res.RegionName = regionName.String
results = append(results, res)
}
return results, nil
}