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

251 lines
8.6 KiB
Go

package service
import (
"context"
"fmt"
"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"
)
// AssignmentPolicyV2Service implements business logic for AssignmentPolicy V2 operations.
// Reference: Chatwoot app/controllers/api/v1/assignment_policies_controller.rb
type AssignmentPolicyV2Service struct {
policyRepo *repository.AssignmentPolicyV2Repo
apInboxRepo *repository.AssignmentPolicyInboxRepo
}
// NewAssignmentPolicyV2Service creates a new AssignmentPolicyV2 service.
func NewAssignmentPolicyV2Service(
policyRepo *repository.AssignmentPolicyV2Repo,
apInboxRepo *repository.AssignmentPolicyInboxRepo,
) *AssignmentPolicyV2Service {
return &AssignmentPolicyV2Service{policyRepo: policyRepo, apInboxRepo: apInboxRepo}
}
// CreatePolicyV2Request is the DTO for creating a V2 assignment policy.
type CreatePolicyV2Request struct {
Name string `json:"name" validate:"required"`
Description string `json:"description,omitempty"`
Type model.AssignmentPolicyV2Type `json:"type" validate:"required,oneof=round_robin fair best_skill_match"`
}
// UpdatePolicyV2Request is the DTO for updating a V2 assignment policy.
type UpdatePolicyV2Request struct {
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Type model.AssignmentPolicyV2Type `json:"type,omitempty" validate:"omitempty,oneof=round_robin fair best_skill_match"`
}
// AddInboxRequestV2 is the DTO for adding an inbox to a V2 policy.
type AddInboxRequestV2 struct {
InboxID uint `json:"inbox_id" validate:"required"`
}
// Create creates a new assignment policy V2.
func (s *AssignmentPolicyV2Service) Create(ctx context.Context, accountID uint, req *CreatePolicyV2Request) (*model.AssignmentPolicyV2, error) {
if err := pkgvalidator.ValidateStruct(req); err != nil {
return nil, fmt.Errorf("validation error: %w", err)
}
policy := &model.AssignmentPolicyV2{
AccountID: accountID,
Name: req.Name,
Description: req.Description,
Type: req.Type,
}
if err := s.policyRepo.Create(ctx, policy); err != nil {
return nil, fmt.Errorf("create assignment policy v2: %w", err)
}
applogger.L().Infof("Assignment policy V2 %d created for account %d (type=%s)", policy.ID, accountID, policy.Type)
return policy, nil
}
// Get retrieves an assignment policy V2 by ID.
func (s *AssignmentPolicyV2Service) Get(ctx context.Context, accountID, policyID uint) (*model.AssignmentPolicyV2, error) {
policy, err := s.policyRepo.FindByID(ctx, policyID)
if err != nil {
return nil, fmt.Errorf("assignment policy v2 not found: %w", err)
}
if policy.AccountID != accountID {
return nil, fmt.Errorf("assignment policy v2 %d does not belong to account %d", policyID, accountID)
}
return policy, nil
}
// List retrieves all assignment policies V2 for an account.
func (s *AssignmentPolicyV2Service) List(ctx context.Context, accountID uint) ([]model.AssignmentPolicyV2, error) {
return s.policyRepo.FindByAccount(ctx, accountID)
}
// Update updates an assignment policy V2.
func (s *AssignmentPolicyV2Service) Update(ctx context.Context, accountID, policyID uint, req *UpdatePolicyV2Request) (*model.AssignmentPolicyV2, error) {
policy, err := s.Get(ctx, accountID, policyID)
if err != nil {
return nil, err
}
if req.Name != "" {
policy.Name = req.Name
}
if req.Description != "" {
policy.Description = req.Description
}
if req.Type != "" {
policy.Type = req.Type
}
if err := s.policyRepo.Update(ctx, policy); err != nil {
return nil, fmt.Errorf("update assignment policy v2: %w", err)
}
applogger.L().Infof("Assignment policy V2 %d updated for account %d", policyID, accountID)
return policy, nil
}
// Delete deletes an assignment policy V2 (and its inbox associations).
func (s *AssignmentPolicyV2Service) Delete(ctx context.Context, accountID, policyID uint) error {
policy, err := s.Get(ctx, accountID, policyID)
if err != nil {
return err
}
// Remove all inbox associations first
if err := s.apInboxRepo.DeleteByAssignmentPolicy(ctx, policy.ID); err != nil {
return fmt.Errorf("delete inbox associations: %w", err)
}
if err := s.policyRepo.Delete(ctx, policy.ID); err != nil {
return fmt.Errorf("delete assignment policy v2: %w", err)
}
applogger.L().Infof("Assignment policy V2 %d deleted for account %d", policyID, accountID)
return nil
}
// ListInboxes retrieves all inboxes associated with a policy.
func (s *AssignmentPolicyV2Service) ListInboxes(ctx context.Context, accountID, policyID uint) ([]model.AssignmentPolicyInbox, error) {
policy, err := s.Get(ctx, accountID, policyID)
if err != nil {
return nil, err
}
return s.apInboxRepo.FindByAssignmentPolicy(ctx, policy.ID)
}
// AddInbox associates an inbox with a policy.
func (s *AssignmentPolicyV2Service) AddInbox(ctx context.Context, accountID, policyID uint, req *AddInboxRequestV2) (*model.AssignmentPolicyInbox, error) {
if err := pkgvalidator.ValidateStruct(req); err != nil {
return nil, fmt.Errorf("validation error: %w", err)
}
policy, err := s.Get(ctx, accountID, policyID)
if err != nil {
return nil, err
}
// Check if inbox already has a policy association
existing, findErr := s.apInboxRepo.FindByInbox(ctx, req.InboxID)
if findErr == nil && existing != nil {
// Inbox already has a policy — update it to the new policy
existing.AssignmentPolicyID = policy.ID
if err := s.apInboxRepo.Create(ctx, existing); err != nil {
return nil, fmt.Errorf("inbox already has a policy association: %w", err)
}
return existing, nil
}
apInbox := &model.AssignmentPolicyInbox{
AssignmentPolicyID: policy.ID,
InboxID: req.InboxID,
AccountID: accountID,
}
if err := s.apInboxRepo.Create(ctx, apInbox); err != nil {
return nil, fmt.Errorf("add inbox to policy: %w", err)
}
applogger.L().Infof("Inbox %d added to assignment policy V2 %d", req.InboxID, policyID)
return apInbox, nil
}
// RemoveInbox removes an inbox association from a policy.
func (s *AssignmentPolicyV2Service) RemoveInbox(ctx context.Context, accountID, policyID, inboxID uint) error {
policy, err := s.Get(ctx, accountID, policyID)
if err != nil {
return err
}
apInboxes, err := s.apInboxRepo.FindByAssignmentPolicy(ctx, policy.ID)
if err != nil {
return fmt.Errorf("find inbox associations: %w", err)
}
for _, apInbox := range apInboxes {
if apInbox.InboxID == inboxID {
if err := s.apInboxRepo.Delete(ctx, apInbox.ID); err != nil {
return fmt.Errorf("remove inbox from policy: %w", err)
}
applogger.L().Infof("Inbox %d removed from assignment policy V2 %d", inboxID, policyID)
return nil
}
}
return fmt.Errorf("inbox %d not associated with policy %d", inboxID, policyID)
}
// GetInboxPolicy retrieves the assignment policy for a specific inbox.
func (s *AssignmentPolicyV2Service) GetInboxPolicy(ctx context.Context, accountID, inboxID uint) (*model.AssignmentPolicyV2, error) {
apInbox, err := s.apInboxRepo.FindByInbox(ctx, inboxID)
if err != nil {
return nil, fmt.Errorf("inbox %d has no assignment policy v2: %w", inboxID, err)
}
if apInbox.AccountID != accountID {
return nil, fmt.Errorf("inbox policy does not belong to account %d", accountID)
}
policy, err := s.policyRepo.FindByID(ctx, apInbox.AssignmentPolicyID)
if err != nil {
return nil, fmt.Errorf("assignment policy v2 not found: %w", err)
}
return policy, nil
}
// SetInboxPolicy sets the assignment policy for a specific inbox.
func (s *AssignmentPolicyV2Service) SetInboxPolicy(ctx context.Context, accountID, inboxID, policyID uint) (*model.AssignmentPolicyInbox, error) {
policy, err := s.Get(ctx, accountID, policyID)
if err != nil {
return nil, err
}
// Check if inbox already has a policy — remove it first
s.apInboxRepo.DeleteByInbox(ctx, inboxID) // ignore error (may not exist)
apInbox := &model.AssignmentPolicyInbox{
AssignmentPolicyID: policy.ID,
InboxID: inboxID,
AccountID: accountID,
}
if err := s.apInboxRepo.Create(ctx, apInbox); err != nil {
return nil, fmt.Errorf("set inbox policy: %w", err)
}
applogger.L().Infof("Inbox %d set to assignment policy V2 %d", inboxID, policyID)
return apInbox, nil
}
// DeleteInboxPolicy removes the assignment policy association from an inbox.
func (s *AssignmentPolicyV2Service) DeleteInboxPolicy(ctx context.Context, accountID, inboxID uint) error {
if err := s.apInboxRepo.DeleteByInbox(ctx, inboxID); err != nil {
return fmt.Errorf("delete inbox policy: %w", err)
}
applogger.L().Infof("Assignment policy V2 removed from inbox %d", inboxID)
return nil
}