feat(campaigns): align chatwoot payloads
This commit is contained in:
@@ -2,7 +2,9 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gochat/gochat/internal/campaign"
|
||||
"github.com/gochat/gochat/internal/repository"
|
||||
@@ -13,7 +15,7 @@ import (
|
||||
// CampaignService implements business logic for Campaign operations.
|
||||
// Reference: Chatwoot app/controllers/api/v1/campaigns_controller.rb
|
||||
type CampaignService struct {
|
||||
campaignSvc *campaign.CampaignService
|
||||
campaignSvc *campaign.CampaignService
|
||||
campaignRepo *repository.CampaignRepo
|
||||
}
|
||||
|
||||
@@ -24,32 +26,32 @@ func NewCampaignService(campaignSvc *campaign.CampaignService, campaignRepo *rep
|
||||
|
||||
// CreateCampaignRequest is the DTO for creating a campaign.
|
||||
type CreateCampaignRequest struct {
|
||||
InboxID uint `json:"inbox_id" validate:"required"`
|
||||
SenderID *uint `json:"sender_id,omitempty"`
|
||||
Title string `json:"title" validate:"required,min=2"`
|
||||
Message string `json:"message" validate:"required"`
|
||||
Description string `json:"description,omitempty"`
|
||||
CampaignType string `json:"campaign_type" validate:"required,oneof=ongoing one_off"`
|
||||
Audience string `json:"audience,omitempty"`
|
||||
TriggerRules string `json:"trigger_rules,omitempty"`
|
||||
TemplateParams string `json:"template_params,omitempty"`
|
||||
ScheduledAt *string `json:"scheduled_at,omitempty"` // ISO8601 timestamp string
|
||||
Enabled *bool `json:"enabled,omitempty"`
|
||||
TriggerOnlyDuringBusinessHours *bool `json:"trigger_only_during_business_hours,omitempty"`
|
||||
InboxID uint `json:"inbox_id" validate:"required"`
|
||||
SenderID *uint `json:"sender_id,omitempty"`
|
||||
Title string `json:"title" validate:"required,min=2"`
|
||||
Message string `json:"message" validate:"required"`
|
||||
Description string `json:"description,omitempty"`
|
||||
CampaignType string `json:"campaign_type" validate:"required,oneof=ongoing one_off"`
|
||||
Audience json.RawMessage `json:"audience,omitempty"`
|
||||
TriggerRules json.RawMessage `json:"trigger_rules,omitempty"`
|
||||
TemplateParams json.RawMessage `json:"template_params,omitempty"`
|
||||
ScheduledAt *string `json:"scheduled_at,omitempty"` // ISO8601 timestamp string
|
||||
Enabled *bool `json:"enabled,omitempty"`
|
||||
TriggerOnlyDuringBusinessHours *bool `json:"trigger_only_during_business_hours,omitempty"`
|
||||
}
|
||||
|
||||
// UpdateCampaignRequest is the DTO for updating a campaign.
|
||||
type UpdateCampaignRequest struct {
|
||||
Title string `json:"title,omitempty" validate:"omitempty,min=2"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
CampaignType string `json:"campaign_type,omitempty" validate:"omitempty,oneof=ongoing one_off"`
|
||||
Audience string `json:"audience,omitempty"`
|
||||
TriggerRules string `json:"trigger_rules,omitempty"`
|
||||
TemplateParams string `json:"template_params,omitempty"`
|
||||
ScheduledAt *string `json:"scheduled_at,omitempty"`
|
||||
Enabled *bool `json:"enabled,omitempty"`
|
||||
TriggerOnlyDuringBusinessHours *bool `json:"trigger_only_during_business_hours,omitempty"`
|
||||
Title string `json:"title,omitempty" validate:"omitempty,min=2"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
CampaignType string `json:"campaign_type,omitempty" validate:"omitempty,oneof=ongoing one_off"`
|
||||
Audience json.RawMessage `json:"audience,omitempty"`
|
||||
TriggerRules json.RawMessage `json:"trigger_rules,omitempty"`
|
||||
TemplateParams json.RawMessage `json:"template_params,omitempty"`
|
||||
ScheduledAt *string `json:"scheduled_at,omitempty"`
|
||||
Enabled *bool `json:"enabled,omitempty"`
|
||||
TriggerOnlyDuringBusinessHours *bool `json:"trigger_only_during_business_hours,omitempty"`
|
||||
}
|
||||
|
||||
// List retrieves all campaigns for an account with pagination.
|
||||
@@ -91,9 +93,9 @@ func (s *CampaignService) Create(ctx context.Context, accountID uint, req Create
|
||||
Description: req.Description,
|
||||
CampaignStatus: campaign.CampaignStatusActive,
|
||||
CampaignType: campaign.CampaignType(req.CampaignType),
|
||||
Audience: req.Audience,
|
||||
TriggerRules: req.TriggerRules,
|
||||
TemplateParams: req.TemplateParams,
|
||||
Audience: rawJSONParamString(req.Audience),
|
||||
TriggerRules: rawJSONParamString(req.TriggerRules),
|
||||
TemplateParams: rawJSONParamString(req.TemplateParams),
|
||||
Enabled: enabled,
|
||||
TriggerOnlyDuringBusinessHours: triggerDuringBH,
|
||||
}
|
||||
@@ -132,14 +134,14 @@ func (s *CampaignService) Update(ctx context.Context, id, accountID uint, req Up
|
||||
if req.CampaignType != "" {
|
||||
c.CampaignType = campaign.CampaignType(req.CampaignType)
|
||||
}
|
||||
if req.Audience != "" {
|
||||
c.Audience = req.Audience
|
||||
if len(req.Audience) > 0 {
|
||||
c.Audience = rawJSONParamString(req.Audience)
|
||||
}
|
||||
if req.TriggerRules != "" {
|
||||
c.TriggerRules = req.TriggerRules
|
||||
if len(req.TriggerRules) > 0 {
|
||||
c.TriggerRules = rawJSONParamString(req.TriggerRules)
|
||||
}
|
||||
if req.TemplateParams != "" {
|
||||
c.TemplateParams = req.TemplateParams
|
||||
if len(req.TemplateParams) > 0 {
|
||||
c.TemplateParams = rawJSONParamString(req.TemplateParams)
|
||||
}
|
||||
if req.Enabled != nil {
|
||||
c.Enabled = *req.Enabled
|
||||
@@ -149,14 +151,14 @@ func (s *CampaignService) Update(ctx context.Context, id, accountID uint, req Up
|
||||
}
|
||||
|
||||
if err := s.campaignSvc.Update(ctx, c.ID, map[string]interface{}{
|
||||
"title": c.Title,
|
||||
"message": c.Message,
|
||||
"description": c.Description,
|
||||
"campaign_type": c.CampaignType,
|
||||
"audience": c.Audience,
|
||||
"trigger_rules": c.TriggerRules,
|
||||
"template_params": c.TemplateParams,
|
||||
"enabled": c.Enabled,
|
||||
"title": c.Title,
|
||||
"message": c.Message,
|
||||
"description": c.Description,
|
||||
"campaign_type": c.CampaignType,
|
||||
"audience": c.Audience,
|
||||
"trigger_rules": c.TriggerRules,
|
||||
"template_params": c.TemplateParams,
|
||||
"enabled": c.Enabled,
|
||||
"trigger_only_during_business_hours": c.TriggerOnlyDuringBusinessHours,
|
||||
}); err != nil {
|
||||
applogger.L().Errorf("failed to update campaign: %v", err)
|
||||
@@ -167,7 +169,11 @@ func (s *CampaignService) Update(ctx context.Context, id, accountID uint, req Up
|
||||
|
||||
// Delete soft-deletes a campaign scoped to an account.
|
||||
func (s *CampaignService) Delete(ctx context.Context, id, accountID uint) error {
|
||||
if err := s.campaignSvc.Delete(ctx, id); err != nil {
|
||||
c, err := s.campaignRepo.FindByDisplayIDAndAccountOrID(ctx, id, accountID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("campaign not found: %w", err)
|
||||
}
|
||||
if err := s.campaignSvc.Delete(ctx, c.ID); err != nil {
|
||||
return fmt.Errorf("failed to delete campaign: %w", err)
|
||||
}
|
||||
return nil
|
||||
@@ -185,6 +191,18 @@ func (s *CampaignService) Start(ctx context.Context, id, accountID uint) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func rawJSONParamString(raw json.RawMessage) string {
|
||||
trimmed := strings.TrimSpace(string(raw))
|
||||
if trimmed == "" || trimmed == "null" {
|
||||
return ""
|
||||
}
|
||||
var value string
|
||||
if err := json.Unmarshal(raw, &value); err == nil {
|
||||
return value
|
||||
}
|
||||
return trimmed
|
||||
}
|
||||
|
||||
// Stop marks a campaign as completed.
|
||||
func (s *CampaignService) Stop(ctx context.Context, id, accountID uint) error {
|
||||
c, err := s.campaignRepo.FindByIDAndAccount(ctx, id, accountID)
|
||||
|
||||
Reference in New Issue
Block a user