159 lines
5.4 KiB
Go
159 lines
5.4 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"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"
|
|
)
|
|
|
|
// InboxMemberService implements business logic for InboxMember operations.
|
|
// Reference: Chatwoot app/models/inbox_member.rb
|
|
type InboxMemberService struct {
|
|
repo *repository.InboxMemberRepo
|
|
}
|
|
|
|
// NewInboxMemberService creates a new InboxMember service.
|
|
func NewInboxMemberService(repo *repository.InboxMemberRepo) *InboxMemberService {
|
|
return &InboxMemberService{repo: repo}
|
|
}
|
|
|
|
// GetByID retrieves a single inbox_member.
|
|
func (s *InboxMemberService) GetByID(ctx context.Context, id uint) (*model.InboxMember, error) {
|
|
return s.repo.FindByID(ctx, id)
|
|
}
|
|
|
|
// ListByInbox retrieves all members (agents) assigned to an inbox.
|
|
func (s *InboxMemberService) ListByInbox(ctx context.Context, inboxID uint) ([]model.InboxMember, error) {
|
|
return s.repo.FindByInbox(ctx, inboxID)
|
|
}
|
|
|
|
// ListByUser retrieves all inboxes a user is assigned to.
|
|
func (s *InboxMemberService) ListByUser(ctx context.Context, userID uint) ([]model.InboxMember, error) {
|
|
return s.repo.FindByUser(ctx, userID)
|
|
}
|
|
|
|
// AddMemberRequest is the DTO for adding a member to an inbox.
|
|
type AddMemberRequest struct {
|
|
InboxID uint `json:"inbox_id" validate:"required"`
|
|
UserID uint `json:"user_id" validate:"required"`
|
|
}
|
|
|
|
// AddMember assigns a user (agent) to an inbox.
|
|
func (s *InboxMemberService) AddMember(ctx context.Context, req AddMemberRequest) (*model.InboxMember, error) {
|
|
if err := pkgvalidator.ValidateStruct(req); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Check for duplicate
|
|
existing, err := s.repo.FindByInboxAndUser(ctx, req.InboxID, req.UserID)
|
|
if err == nil && existing != nil {
|
|
return nil, errors.New("user is already a member of this inbox")
|
|
}
|
|
|
|
im := &model.InboxMember{
|
|
InboxID: req.InboxID,
|
|
UserID: req.UserID,
|
|
}
|
|
if err := s.repo.Create(ctx, im); err != nil {
|
|
applogger.L().Errorf("InboxMemberService.AddMember failed: %v", err)
|
|
return nil, err
|
|
}
|
|
return im, nil
|
|
}
|
|
|
|
// RemoveMember removes a user from an inbox.
|
|
func (s *InboxMemberService) RemoveMember(ctx context.Context, inboxID, userID uint) error {
|
|
return s.repo.DeleteByInboxAndUser(ctx, inboxID, userID)
|
|
}
|
|
|
|
// RemoveAllMembers removes all members from an inbox.
|
|
func (s *InboxMemberService) RemoveAllMembers(ctx context.Context, inboxID uint) error {
|
|
return s.repo.DeleteByInbox(ctx, inboxID)
|
|
}
|
|
|
|
// UpdateMemberRequest is the DTO for updating a member's role/availability.
|
|
type UpdateMemberRequest struct {
|
|
Role string `json:"role" validate:"omitempty,oneof=agent supervisor"`
|
|
AvailabilityStatus string `json:"availability_status" validate:"omitempty,oneof=online offline busy"`
|
|
}
|
|
|
|
// UpdateMember updates a member's role or availability status.
|
|
func (s *InboxMemberService) UpdateMember(ctx context.Context, inboxID, userID uint, req UpdateMemberRequest) (*model.InboxMember, error) {
|
|
if err := pkgvalidator.ValidateStruct(req); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
updates := map[string]interface{}{}
|
|
if req.Role != "" {
|
|
updates["role"] = req.Role
|
|
}
|
|
if req.AvailabilityStatus != "" {
|
|
updates["availability_status"] = req.AvailabilityStatus
|
|
}
|
|
|
|
if len(updates) == 0 {
|
|
// Nothing to update, just return the existing record
|
|
return s.repo.FindByInboxAndUser(ctx, inboxID, userID)
|
|
}
|
|
|
|
if err := s.repo.UpdateByInboxAndUser(ctx, inboxID, userID, updates); err != nil {
|
|
applogger.L().Errorf("InboxMemberService.UpdateMember failed: %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
return s.repo.FindByInboxAndUser(ctx, inboxID, userID)
|
|
}
|
|
|
|
// UpdateMultipleRequest is the DTO for batch-updating inbox members.
|
|
// Reference: Chatwoot inbox_members_controller#update (batch replace all members)
|
|
type UpdateMultipleRequest struct {
|
|
InboxID uint `json:"inbox_id" validate:"required"`
|
|
UserIDs []uint `json:"user_ids" validate:"required,min=1"`
|
|
}
|
|
|
|
// UpdateMultiple replaces all members of an inbox with the given user list.
|
|
// Reference: Chatwoot inbox_members#update — replaces entire member list in one call.
|
|
// This is the "seat assignment" batch update: remove all existing, then add new ones.
|
|
func (s *InboxMemberService) UpdateMultiple(ctx context.Context, req UpdateMultipleRequest) ([]model.InboxMember, error) {
|
|
if err := pkgvalidator.ValidateStruct(req); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Remove all existing members for this inbox
|
|
if err := s.RemoveAllMembers(ctx, req.InboxID); err != nil {
|
|
applogger.L().Errorf("InboxMemberService.UpdateMultiple: failed to remove existing members: %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
// Add each new member
|
|
var members []model.InboxMember
|
|
for _, userID := range req.UserIDs {
|
|
im := &model.InboxMember{
|
|
InboxID: req.InboxID,
|
|
UserID: userID,
|
|
Role: "agent",
|
|
AvailabilityStatus: "online",
|
|
}
|
|
if err := s.repo.Create(ctx, im); err != nil {
|
|
applogger.L().Errorf("InboxMemberService.UpdateMultiple: failed to add member user_id=%d: %v", userID, err)
|
|
continue
|
|
}
|
|
members = append(members, *im)
|
|
}
|
|
|
|
return members, nil
|
|
}
|
|
|
|
// IsMemberOfInbox checks whether a user is a member (assigned agent) of an inbox.
|
|
// Used by ConversationService.AssignAgent to validate seat assignment routing.
|
|
func (s *InboxMemberService) IsMemberOfInbox(ctx context.Context, inboxID, userID uint) bool {
|
|
member, err := s.repo.FindByInboxAndUser(ctx, inboxID, userID)
|
|
if err != nil || member == nil {
|
|
return false
|
|
}
|
|
return true
|
|
} |