187 lines
5.1 KiB
Go
187 lines
5.1 KiB
Go
|
|
package handlers
|
||
|
|
|
||
|
|
import (
|
||
|
|
"database/sql"
|
||
|
|
"encoding/json"
|
||
|
|
"net/http"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
type MarketingHandler struct {
|
||
|
|
DB *sql.DB
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewMarketingHandler(db *sql.DB) *MarketingHandler {
|
||
|
|
return &MarketingHandler{DB: db}
|
||
|
|
}
|
||
|
|
|
||
|
|
type Campaign struct {
|
||
|
|
ID string `json:"id"`
|
||
|
|
Name string `json:"name"`
|
||
|
|
Description string `json:"description"`
|
||
|
|
Type string `json:"type"`
|
||
|
|
Status string `json:"status"`
|
||
|
|
Budget float64 `json:"budget"`
|
||
|
|
Spent float64 `json:"spent"`
|
||
|
|
Currency string `json:"currency"`
|
||
|
|
StartDate *time.Time `json:"start_date"`
|
||
|
|
EndDate *time.Time `json:"end_date"`
|
||
|
|
Metrics map[string]interface{} `json:"metrics"`
|
||
|
|
CreatedAt time.Time `json:"created_at"`
|
||
|
|
}
|
||
|
|
|
||
|
|
type Content struct {
|
||
|
|
ID string `json:"id"`
|
||
|
|
CampaignID *string `json:"campaign_id"`
|
||
|
|
Title string `json:"title"`
|
||
|
|
Type string `json:"type"`
|
||
|
|
Status string `json:"status"`
|
||
|
|
PublishAt *time.Time `json:"publish_at"`
|
||
|
|
PublishedAt *time.Time `json:"published_at"`
|
||
|
|
URL string `json:"url"`
|
||
|
|
Metrics map[string]interface{} `json:"metrics"`
|
||
|
|
CreatedAt time.Time `json:"created_at"`
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *MarketingHandler) ListCampaigns(w http.ResponseWriter, r *http.Request) {
|
||
|
|
status := r.URL.Query().Get("status")
|
||
|
|
if status == "" {
|
||
|
|
status = "active"
|
||
|
|
}
|
||
|
|
|
||
|
|
rows, err := h.DB.Query(`
|
||
|
|
SELECT id, name, description, type, status, budget, spent, currency, start_date, end_date, metrics, created_at
|
||
|
|
FROM boc_campaigns
|
||
|
|
WHERE status = $1
|
||
|
|
ORDER BY created_at DESC
|
||
|
|
`, status)
|
||
|
|
if err != nil {
|
||
|
|
writeError(w, http.StatusInternalServerError, "database error")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
defer rows.Close()
|
||
|
|
|
||
|
|
campaigns := []Campaign{}
|
||
|
|
for rows.Next() {
|
||
|
|
var c Campaign
|
||
|
|
var metrics []byte
|
||
|
|
if err := rows.Scan(&c.ID, &c.Name, &c.Description, &c.Type, &c.Status, &c.Budget,
|
||
|
|
&c.Spent, &c.Currency, &c.StartDate, &c.EndDate, &metrics, &c.CreatedAt); err != nil {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
json.Unmarshal(metrics, &c.Metrics)
|
||
|
|
campaigns = append(campaigns, c)
|
||
|
|
}
|
||
|
|
|
||
|
|
writeJSON(w, http.StatusOK, map[string]interface{}{
|
||
|
|
"campaigns": campaigns,
|
||
|
|
"total": len(campaigns),
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *MarketingHandler) CreateCampaign(w http.ResponseWriter, r *http.Request) {
|
||
|
|
var req Campaign
|
||
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||
|
|
writeError(w, http.StatusBadRequest, "invalid request")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
metrics, _ := json.Marshal(req.Metrics)
|
||
|
|
|
||
|
|
var id string
|
||
|
|
err := h.DB.QueryRow(`
|
||
|
|
INSERT INTO boc_campaigns (name, description, type, status, budget, currency, start_date, end_date, metrics)
|
||
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||
|
|
RETURNING id
|
||
|
|
`, req.Name, req.Description, req.Type, req.Status, req.Budget, req.Currency,
|
||
|
|
req.StartDate, req.EndDate, metrics).Scan(&id)
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
writeError(w, http.StatusInternalServerError, "failed to create campaign")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
writeJSON(w, http.StatusCreated, map[string]interface{}{
|
||
|
|
"id": id,
|
||
|
|
"message": "Campaign created",
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *MarketingHandler) ListContent(w http.ResponseWriter, r *http.Request) {
|
||
|
|
status := r.URL.Query().Get("status")
|
||
|
|
if status == "" {
|
||
|
|
status = "all"
|
||
|
|
}
|
||
|
|
|
||
|
|
var query string
|
||
|
|
var args []interface{}
|
||
|
|
if status == "all" {
|
||
|
|
query = `
|
||
|
|
SELECT id, campaign_id, title, type, status, publish_at, published_at, url, metrics, created_at
|
||
|
|
FROM boc_content
|
||
|
|
ORDER BY created_at DESC
|
||
|
|
LIMIT 100
|
||
|
|
`
|
||
|
|
} else {
|
||
|
|
query = `
|
||
|
|
SELECT id, campaign_id, title, type, status, publish_at, published_at, url, metrics, created_at
|
||
|
|
FROM boc_content
|
||
|
|
WHERE status = $1
|
||
|
|
ORDER BY created_at DESC
|
||
|
|
LIMIT 100
|
||
|
|
`
|
||
|
|
args = append(args, status)
|
||
|
|
}
|
||
|
|
|
||
|
|
rows, err := h.DB.Query(query, args...)
|
||
|
|
if err != nil {
|
||
|
|
writeError(w, http.StatusInternalServerError, "database error")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
defer rows.Close()
|
||
|
|
|
||
|
|
contents := []Content{}
|
||
|
|
for rows.Next() {
|
||
|
|
var c Content
|
||
|
|
var metrics []byte
|
||
|
|
if err := rows.Scan(&c.ID, &c.CampaignID, &c.Title, &c.Type, &c.Status, &c.PublishAt,
|
||
|
|
&c.PublishedAt, &c.URL, &metrics, &c.CreatedAt); err != nil {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
json.Unmarshal(metrics, &c.Metrics)
|
||
|
|
contents = append(contents, c)
|
||
|
|
}
|
||
|
|
|
||
|
|
writeJSON(w, http.StatusOK, map[string]interface{}{
|
||
|
|
"content": contents,
|
||
|
|
"total": len(contents),
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *MarketingHandler) CreateContent(w http.ResponseWriter, r *http.Request) {
|
||
|
|
var req Content
|
||
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||
|
|
writeError(w, http.StatusBadRequest, "invalid request")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
metrics, _ := json.Marshal(req.Metrics)
|
||
|
|
|
||
|
|
var id string
|
||
|
|
err := h.DB.QueryRow(`
|
||
|
|
INSERT INTO boc_content (campaign_id, title, type, status, publish_at, url, metrics)
|
||
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||
|
|
RETURNING id
|
||
|
|
`, req.CampaignID, req.Title, req.Type, req.Status, req.PublishAt, req.URL, metrics).Scan(&id)
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
writeError(w, http.StatusInternalServerError, "failed to create content")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
writeJSON(w, http.StatusCreated, map[string]interface{}{
|
||
|
|
"id": id,
|
||
|
|
"message": "Content created",
|
||
|
|
})
|
||
|
|
}
|