- 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
144 lines
4 KiB
Go
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, ¤cy, &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, ®ionName); 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
|
|
}
|