gohorsejobs/backend/internal/services/metrics_service.go
Tiago Yamamoto b23393bf35 feat: implement stripe subscriptions, google analytics, and user crud
- Backend:
  - Add Stripe subscription fields to companies (migration 019)
  - Implement Stripe Checkout and Webhook handlers
  - Add Metrics API (view count, recording)
  - Update Company and Job models
- Frontend:
  - Add Google Analytics component
  - Implement User CRUD in Backoffice (Dashboard)
  - Add 'Featured' badge to JobCard
- Docs: Update Roadmap and artifacts
2025-12-27 12:06:54 -03:00

100 lines
2.4 KiB
Go

package services
import (
"database/sql"
"github.com/rede5/gohorsejobs/backend/internal/models"
)
// MetricsService handles job analytics
type MetricsService struct {
DB *sql.DB
}
// NewMetricsService creates a new metrics service
func NewMetricsService(db *sql.DB) *MetricsService {
return &MetricsService{DB: db}
}
// RecordView records a job view and increments the counter
func (s *MetricsService) RecordView(jobID int, userID *int, ip *string, userAgent *string) error {
tx, err := s.DB.Begin()
if err != nil {
return err
}
defer tx.Rollback()
// Insert view record
_, err = tx.Exec(`
INSERT INTO job_views (job_id, user_id, ip_address, user_agent)
VALUES ($1, $2, $3, $4)
`, jobID, userID, ip, userAgent)
if err != nil {
return err
}
// Increment cached view count
_, err = tx.Exec(`
UPDATE jobs SET view_count = view_count + 1 WHERE id = $1
`, jobID)
if err != nil {
return err
}
return tx.Commit()
}
// GetJobMetrics returns analytics data for a job
func (s *MetricsService) GetJobMetrics(jobID int) (*models.JobMetrics, error) {
metrics := &models.JobMetrics{JobID: jobID}
// Get view count from jobs table
err := s.DB.QueryRow(`
SELECT COALESCE(view_count, 0) FROM jobs WHERE id = $1
`, jobID).Scan(&metrics.ViewCount)
if err != nil {
return nil, err
}
// Get unique viewers
err = s.DB.QueryRow(`
SELECT COUNT(DISTINCT COALESCE(user_id::text, ip_address))
FROM job_views WHERE job_id = $1
`, jobID).Scan(&metrics.UniqueViewers)
if err != nil {
metrics.UniqueViewers = 0 // Don't fail if table doesn't exist yet
}
// Get application count
err = s.DB.QueryRow(`
SELECT COUNT(*) FROM applications WHERE job_id = $1
`, jobID).Scan(&metrics.ApplicationCount)
if err != nil {
metrics.ApplicationCount = 0
}
// Calculate conversion rate
if metrics.ViewCount > 0 {
metrics.ConversionRate = float64(metrics.ApplicationCount) / float64(metrics.ViewCount) * 100
}
// Get views last 7 days
err = s.DB.QueryRow(`
SELECT COUNT(*) FROM job_views
WHERE job_id = $1 AND viewed_at > NOW() - INTERVAL '7 days'
`, jobID).Scan(&metrics.ViewsLast7Days)
if err != nil {
metrics.ViewsLast7Days = 0
}
// Get views last 30 days
err = s.DB.QueryRow(`
SELECT COUNT(*) FROM job_views
WHERE job_id = $1 AND viewed_at > NOW() - INTERVAL '30 days'
`, jobID).Scan(&metrics.ViewsLast30Days)
if err != nil {
metrics.ViewsLast30Days = 0
}
return metrics, nil
}