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

117 lines
4.1 KiB
Go

package service
import (
"context"
"crypto/rand"
"encoding/hex"
"errors"
"github.com/gochat/gochat/internal/model"
"github.com/gochat/gochat/internal/repository"
applogger "github.com/gochat/gochat/pkg/logger"
pkgvalidator "github.com/gochat/gochat/pkg/validator"
)
// ContactInboxService implements business logic for ContactInbox operations.
// Reference: Chatwoot app/models/contact_inbox.rb
type ContactInboxService struct {
repo *repository.ContactInboxRepo
}
// NewContactInboxService creates a new ContactInbox service.
func NewContactInboxService(repo *repository.ContactInboxRepo) *ContactInboxService {
return &ContactInboxService{repo: repo}
}
// GetByID retrieves a single contact_inbox.
func (s *ContactInboxService) GetByID(ctx context.Context, id uint) (*model.ContactInbox, error) {
return s.repo.FindByID(ctx, id)
}
// GetByContactAndInbox retrieves a contact_inbox by contact and inbox.
func (s *ContactInboxService) GetByContactAndInbox(ctx context.Context, contactID, inboxID uint) (*model.ContactInbox, error) {
return s.repo.FindByContactAndInbox(ctx, contactID, inboxID)
}
// ListByContact retrieves all contact_inboxes for a contact.
func (s *ContactInboxService) ListByContact(ctx context.Context, contactID uint) ([]model.ContactInbox, error) {
return s.repo.FindByContact(ctx, contactID)
}
// ListByInbox retrieves all contact_inboxes for an inbox (paginated).
func (s *ContactInboxService) ListByInbox(ctx context.Context, inboxID uint, offset, limit int) ([]model.ContactInbox, int64, error) {
return s.repo.FindByInbox(ctx, inboxID, offset, limit)
}
// GetBySourceID retrieves a contact_inbox by source_id and inbox.
func (s *ContactInboxService) GetBySourceID(ctx context.Context, inboxID uint, sourceID string) (*model.ContactInbox, error) {
return s.repo.FindBySourceID(ctx, inboxID, sourceID)
}
// CreateContactInboxRequest is the DTO for creating a contact_inbox.
type CreateContactInboxRequest struct {
ContactID uint `json:"contact_id" validate:"required"`
InboxID uint `json:"inbox_id" validate:"required"`
SourceID string `json:"source_id" validate:"required"`
}
// Create adds a new contact_inbox join record.
func (s *ContactInboxService) Create(ctx context.Context, req CreateContactInboxRequest) (*model.ContactInbox, error) {
if err := pkgvalidator.ValidateStruct(req); err != nil {
return nil, err
}
// Check for duplicate
existing, err := s.repo.FindByContactAndInbox(ctx, req.ContactID, req.InboxID)
if err == nil && existing != nil {
return nil, errors.New("contact_inbox already exists for this contact and inbox")
}
hmacToken, err := generateToken(24)
if err != nil {
return nil, err
}
pubsubToken, err := generateToken(24)
if err != nil {
return nil, err
}
ci := &model.ContactInbox{
ContactID: req.ContactID,
InboxID: req.InboxID,
SourceID: req.SourceID,
HMACToken: hmacToken,
PubsubToken: pubsubToken,
}
if err := s.repo.Create(ctx, ci); err != nil {
applogger.L().Errorf("ContactInboxService.Create failed: %v", err)
return nil, err
}
return ci, nil
}
// Delete removes a contact_inbox by ID.
func (s *ContactInboxService) Delete(ctx context.Context, id uint) error {
return s.repo.Delete(ctx, id)
}
// DeleteByContactAndInbox removes the join record.
func (s *ContactInboxService) DeleteByContactAndInbox(ctx context.Context, contactID, inboxID uint) error {
return s.repo.DeleteByContactAndInbox(ctx, contactID, inboxID)
}
// FilterContactInboxes retrieves contact_inboxes by account with optional filters.
// GET /api/v1/accounts/:id/contact_inboxes/filter
// Reference: Chatwoot contact_inboxes_controller#filter
func (s *ContactInboxService) FilterContactInboxes(ctx context.Context, accountID uint, inboxID, contactID *uint, sourceID string, offset, limit int) ([]model.ContactInbox, int64, error) {
return s.repo.Filter(ctx, accountID, inboxID, contactID, sourceID, offset, limit)
}
// generateToken creates a random hex token of the specified byte length.
func generateToken(byteLen int) (string, error) {
b := make([]byte, byteLen)
if _, err := rand.Read(b); err != nil {
return "", err
}
return hex.EncodeToString(b), nil
}