package services import ( "database/sql" "fmt" "strings" "time" "github.com/rede5/gohorsejobs/backend/internal/dto" "github.com/rede5/gohorsejobs/backend/internal/models" ) type JobService struct { DB *sql.DB } func NewJobService(db *sql.DB) *JobService { return &JobService{DB: db} } func (s *JobService) CreateJob(req dto.CreateJobRequest) (*models.Job, error) { query := ` INSERT INTO jobs ( company_id, title, description, salary_min, salary_max, salary_type, employment_type, working_hours, location, region_id, city_id, requirements, benefits, visa_support, language_level, status, created_at, updated_at ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18) RETURNING id, created_at, updated_at ` job := &models.Job{ CompanyID: req.CompanyID, Title: req.Title, Description: req.Description, SalaryMin: req.SalaryMin, SalaryMax: req.SalaryMax, SalaryType: req.SalaryType, EmploymentType: req.EmploymentType, WorkingHours: req.WorkingHours, Location: req.Location, RegionID: req.RegionID, CityID: req.CityID, Requirements: req.Requirements, Benefits: req.Benefits, VisaSupport: req.VisaSupport, LanguageLevel: req.LanguageLevel, Status: req.Status, CreatedAt: time.Now(), UpdatedAt: time.Now(), } err := s.DB.QueryRow( query, job.CompanyID, job.Title, job.Description, job.SalaryMin, job.SalaryMax, job.SalaryType, job.EmploymentType, job.WorkingHours, job.Location, job.RegionID, job.CityID, job.Requirements, job.Benefits, job.VisaSupport, job.LanguageLevel, job.Status, job.CreatedAt, job.UpdatedAt, ).Scan(&job.ID, &job.CreatedAt, &job.UpdatedAt) if err != nil { return nil, err } return job, nil } func (s *JobService) GetJobs(filter dto.JobFilterQuery) ([]models.JobWithCompany, int, error) { baseQuery := ` SELECT j.id, j.company_id, j.title, j.description, j.salary_min, j.salary_max, j.salary_type, j.employment_type, j.location, j.status, j.is_featured, j.created_at, j.updated_at, c.name as company_name, c.logo_url as company_logo_url, r.name as region_name, ci.name as city_name FROM jobs j LEFT JOIN companies c ON j.company_id = c.id LEFT JOIN regions r ON j.region_id = r.id LEFT JOIN cities ci ON j.city_id = ci.id WHERE 1=1` countQuery := `SELECT COUNT(*) FROM jobs j WHERE 1=1` var args []interface{} argId := 1 if filter.CompanyID != nil { baseQuery += fmt.Sprintf(" AND j.company_id = $%d", argId) countQuery += fmt.Sprintf(" AND j.company_id = $%d", argId) args = append(args, *filter.CompanyID) argId++ } if filter.IsFeatured != nil { baseQuery += fmt.Sprintf(" AND j.is_featured = $%d", argId) countQuery += fmt.Sprintf(" AND j.is_featured = $%d", argId) args = append(args, *filter.IsFeatured) argId++ } // Add more filters as needed... // Pagination limit := filter.Limit if limit == 0 { limit = 10 } offset := (filter.Page - 1) * limit if offset < 0 { offset = 0 } paginationQuery := baseQuery + fmt.Sprintf(" LIMIT $%d OFFSET $%d", argId, argId+1) paginationArgs := append(args, limit, offset) rows, err := s.DB.Query(paginationQuery, paginationArgs...) if err != nil { return nil, 0, err } defer rows.Close() var jobs []models.JobWithCompany for rows.Next() { var j models.JobWithCompany if err := rows.Scan( &j.ID, &j.CompanyID, &j.Title, &j.Description, &j.SalaryMin, &j.SalaryMax, &j.SalaryType, &j.EmploymentType, &j.Location, &j.Status, &j.IsFeatured, &j.CreatedAt, &j.UpdatedAt, &j.CompanyName, &j.CompanyLogoURL, &j.RegionName, &j.CityName, ); err != nil { return nil, 0, err } jobs = append(jobs, j) } var total int err = s.DB.QueryRow(countQuery, args...).Scan(&total) if err != nil { return nil, 0, err } return jobs, total, nil } func (s *JobService) GetJobByID(id int) (*models.Job, error) { var j models.Job query := ` SELECT id, company_id, title, description, salary_min, salary_max, salary_type, employment_type, working_hours, location, region_id, city_id, requirements, benefits, visa_support, language_level, status, created_at, updated_at FROM jobs WHERE id = $1 ` err := s.DB.QueryRow(query, id).Scan( &j.ID, &j.CompanyID, &j.Title, &j.Description, &j.SalaryMin, &j.SalaryMax, &j.SalaryType, &j.EmploymentType, &j.WorkingHours, &j.Location, &j.RegionID, &j.CityID, &j.Requirements, &j.Benefits, &j.VisaSupport, &j.LanguageLevel, &j.Status, &j.CreatedAt, &j.UpdatedAt, ) if err != nil { return nil, err } return &j, nil } func (s *JobService) UpdateJob(id int, req dto.UpdateJobRequest) (*models.Job, error) { var setClauses []string var args []interface{} argId := 1 if req.Title != nil { setClauses = append(setClauses, fmt.Sprintf("title = $%d", argId)) args = append(args, *req.Title) argId++ } if req.Description != nil { setClauses = append(setClauses, fmt.Sprintf("description = $%d", argId)) args = append(args, *req.Description) argId++ } // Add other fields... if req.Status != nil { setClauses = append(setClauses, fmt.Sprintf("status = $%d", argId)) args = append(args, *req.Status) argId++ } if len(setClauses) == 0 { return s.GetJobByID(id) } setClauses = append(setClauses, "updated_at = NOW()") query := fmt.Sprintf("UPDATE jobs SET %s WHERE id = $%d RETURNING id, updated_at", strings.Join(setClauses, ", "), argId) args = append(args, id) var j models.Job err := s.DB.QueryRow(query, args...).Scan(&j.ID, &j.UpdatedAt) if err != nil { return nil, err } return s.GetJobByID(id) } func (s *JobService) DeleteJob(id int) error { _, err := s.DB.Exec("DELETE FROM jobs WHERE id = $1", id) return err }