Files
gochat/internal/service/captain_preference_service.go
T
2026-06-04 15:44:48 +08:00

174 lines
6.0 KiB
Go

package service
import (
"context"
"fmt"
"github.com/gochat/gochat/internal/model"
"github.com/gochat/gochat/internal/repository"
pkgvalidator "github.com/gochat/gochat/pkg/validator"
applogger "github.com/gochat/gochat/pkg/logger"
)
// CaptainPreferenceService implements business logic for CaptainPreference operations.
// Reference: Chatwoot enterprise/app/controllers/api/v1/captain/preferences_controller.rb
type CaptainPreferenceService struct {
repo *repository.CaptainPreferenceRepo
}
func NewCaptainPreferenceService(repo *repository.CaptainPreferenceRepo) *CaptainPreferenceService {
return &CaptainPreferenceService{repo: repo}
}
// --- Request DTOs ---
// CreatePreferenceRequest is the DTO for creating a preference.
// Reference: Chatwoot enterprise/app/models/captain/preference.rb
type CreatePreferenceRequest struct {
Tone string `json:"tone" validate:"omitempty,oneof=professional casual friendly formal"`
Language string `json:"language" validate:"omitempty,min=1,max=10"`
ResponseGuidelines string `json:"response_guidelines" validate:"omitempty,max=2000"`
AutoLabelEnabled bool `json:"auto_label_enabled"`
AutoFollowUpEnabled bool `json:"auto_follow_up_enabled"`
AutoReplyEnabled bool `json:"auto_reply_enabled"`
MaxResponseLength int `json:"max_response_length" validate:"omitempty,min=50,max=5000"`
CustomPromptSuffix string `json:"custom_prompt_suffix" validate:"omitempty,max=1000"`
}
// UpdatePreferenceRequest is the DTO for updating a preference.
// Reference: Chatwoot enterprise/app/models/captain/preference.rb
type UpdatePreferenceRequest struct {
Tone string `json:"tone,omitempty" validate:"omitempty,oneof=professional casual friendly formal"`
Language string `json:"language,omitempty" validate:"omitempty,min=1,max=10"`
ResponseGuidelines string `json:"response_guidelines,omitempty" validate:"omitempty,max=2000"`
AutoLabelEnabled *bool `json:"auto_label_enabled,omitempty"`
AutoFollowUpEnabled *bool `json:"auto_follow_up_enabled,omitempty"`
AutoReplyEnabled *bool `json:"auto_reply_enabled,omitempty"`
MaxResponseLength *int `json:"max_response_length,omitempty" validate:"omitempty,min=50,max=5000"`
CustomPromptSuffix string `json:"custom_prompt_suffix,omitempty" validate:"omitempty,max=1000"`
}
// --- Business logic ---
// validTones are the allowed tone values for captain preferences.
// Reference: Chatwoot enterprise/app/models/captain/preference.rb TONES
var validTones = []string{"professional", "casual", "friendly", "formal"}
// isValidTone checks if a tone value is valid.
func isValidTone(tone string) bool {
for _, t := range validTones {
if t == tone {
return true
}
}
return false
}
// Create creates a new preference for an account.
func (s *CaptainPreferenceService) Create(ctx context.Context, accountID uint, req *CreatePreferenceRequest) (*model.CaptainPreference, error) {
// Validate request
if err := pkgvalidator.ValidateStruct(req); err != nil {
return nil, fmt.Errorf("validation error: %w", err)
}
// Check if preference already exists for this account
existing, err := s.repo.GetByAccountID(ctx, accountID)
if err == nil && existing != nil {
return nil, fmt.Errorf("preference already exists for account %d", accountID)
}
pref := &model.CaptainPreference{
AccountID: accountID,
Tone: req.Tone,
Language: req.Language,
ResponseGuidelines: req.ResponseGuidelines,
AutoLabelEnabled: req.AutoLabelEnabled,
AutoFollowUpEnabled: req.AutoFollowUpEnabled,
AutoReplyEnabled: req.AutoReplyEnabled,
MaxResponseLength: req.MaxResponseLength,
CustomPromptSuffix: req.CustomPromptSuffix,
}
// Apply defaults
if pref.Tone == "" {
pref.Tone = "professional"
}
if pref.Language == "" {
pref.Language = "en"
}
if pref.MaxResponseLength == 0 {
pref.MaxResponseLength = 500
}
if err := s.repo.Create(ctx, pref); err != nil {
applogger.L().Errorf("Create captain preference: %v", err)
return nil, fmt.Errorf("create preference: %w", err)
}
return pref, nil
}
// Get retrieves the preference for an account.
func (s *CaptainPreferenceService) Get(ctx context.Context, accountID uint) (*model.CaptainPreference, error) {
pref, err := s.repo.GetByAccountID(ctx, accountID)
if err != nil {
return nil, fmt.Errorf("preference not found for account %d: %w", accountID, err)
}
return pref, nil
}
// Update updates the preference for an account.
func (s *CaptainPreferenceService) Update(ctx context.Context, accountID uint, req *UpdatePreferenceRequest) (*model.CaptainPreference, error) {
// Validate request
if err := pkgvalidator.ValidateStruct(req); err != nil {
return nil, fmt.Errorf("validation error: %w", err)
}
pref, err := s.repo.GetByAccountID(ctx, accountID)
if err != nil {
return nil, fmt.Errorf("preference not found for account %d: %w", accountID, err)
}
// Apply partial updates
if req.Tone != "" {
pref.Tone = req.Tone
}
if req.Language != "" {
pref.Language = req.Language
}
if req.ResponseGuidelines != "" {
pref.ResponseGuidelines = req.ResponseGuidelines
}
if req.AutoLabelEnabled != nil {
pref.AutoLabelEnabled = *req.AutoLabelEnabled
}
if req.AutoFollowUpEnabled != nil {
pref.AutoFollowUpEnabled = *req.AutoFollowUpEnabled
}
if req.AutoReplyEnabled != nil {
pref.AutoReplyEnabled = *req.AutoReplyEnabled
}
if req.MaxResponseLength != nil {
pref.MaxResponseLength = *req.MaxResponseLength
}
if req.CustomPromptSuffix != "" {
pref.CustomPromptSuffix = req.CustomPromptSuffix
}
if err := s.repo.Update(ctx, pref); err != nil {
applogger.L().Errorf("Update captain preference: %v", err)
return nil, fmt.Errorf("update preference: %w", err)
}
return pref, nil
}
// Delete removes the preference for an account.
func (s *CaptainPreferenceService) Delete(ctx context.Context, accountID uint) error {
if err := s.repo.Delete(ctx, accountID); err != nil {
applogger.L().Errorf("Delete captain preference: %v", err)
return fmt.Errorf("delete preference: %w", err)
}
return nil
}