Files
gochat/internal/repository/account_user_repo.go
T

162 lines
6.0 KiB
Go

package repository
import (
"context"
"gorm.io/gorm"
"github.com/gochat/gochat/internal/model"
)
// AccountUserRepo implements GORM repository for AccountUser.
// Reference: Chatwoot AccountUser (join table between Account and User)
// Used for role/availability checks in assignment logic.
type AccountUserRepo struct {
db *gorm.DB
}
// NewAccountUserRepo creates a new AccountUser repository.
func NewAccountUserRepo(db *gorm.DB) *AccountUserRepo {
return &AccountUserRepo{db: db}
}
// FindByAccountAndUser retrieves an AccountUser by accountID and userID.
// Returns nil if not found.
func (r *AccountUserRepo) FindByAccountAndUser(ctx context.Context, accountID, userID uint) (*model.AccountUser, error) {
var au model.AccountUser
err := r.db.WithContext(ctx).
Where("account_id = ? AND user_id = ?", accountID, userID).
First(&au).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &au, nil
}
// FindOnlineAgentsByAccount retrieves all AccountUsers with availability = "online"
// for a given account. Used for auto-assignment eligibility.
func (r *AccountUserRepo) FindOnlineAgentsByAccount(ctx context.Context, accountID uint) ([]model.AccountUser, error) {
var agents []model.AccountUser
err := r.db.WithContext(ctx).
Where("account_id = ? AND availability = ?", accountID, "online").
Find(&agents).Error
return agents, err
}
// FindByAccount retrieves all AccountUsers for a given account with pagination.
func (r *AccountUserRepo) FindByAccount(ctx context.Context, accountID uint, offset, limit int) ([]model.AccountUser, int64, error) {
var agents []model.AccountUser
var count int64
countDB := r.db.WithContext(ctx).Model(&model.AccountUser{}).Where("account_id = ?", accountID)
if err := countDB.Count(&count).Error; err != nil {
return nil, 0, err
}
err := r.db.WithContext(ctx).
Where("account_id = ?", accountID).
Offset(offset).Limit(limit).
Find(&agents).Error
return agents, count, err
}
// UpdateAvailability updates the availability status of an AccountUser.
func (r *AccountUserRepo) UpdateAvailability(ctx context.Context, accountID, userID uint, availability string) error {
return r.db.WithContext(ctx).
Model(&model.AccountUser{}).
Where("account_id = ? AND user_id = ?", accountID, userID).
Update("availability", availability).Error
}
// IsAdministrator checks if a user has administrator role in a given account.
func (r *AccountUserRepo) IsAdministrator(ctx context.Context, accountID, userID uint) (bool, error) {
var count int64
err := r.db.WithContext(ctx).
Model(&model.AccountUser{}).
Where("account_id = ? AND user_id = ? AND role = ?", accountID, userID, "administrator").
Count(&count).Error
return count > 0, err
}
// IsAgentOrAdmin checks if a user is either an agent or administrator in a given account.
func (r *AccountUserRepo) IsAgentOrAdmin(ctx context.Context, accountID, userID uint) (bool, error) {
var count int64
err := r.db.WithContext(ctx).
Model(&model.AccountUser{}).
Where("account_id = ? AND user_id = ? AND role IN ?", accountID, userID, []string{"agent", "administrator"}).
Count(&count).Error
return count > 0, err
}
// FindTeamMembersByAccount retrieves all team members belonging to teams in a given account.
// Used for team overflow logic: when all team members are offline, fall back to account-level agents.
func (r *AccountUserRepo) FindTeamMembersByAccount(ctx context.Context, accountID uint, teamID uint) ([]model.AccountUser, error) {
var members []model.AccountUser
// Join team_members to find agents belonging to a specific team in this account
err := r.db.WithContext(ctx).
Joins("JOIN team_members ON team_members.user_id = account_users.user_id").
Where("team_members.team_id = ? AND account_users.account_id = ?", teamID, accountID).
Find(&members).Error
return members, err
}
// UpdateAutoOffline updates the auto_offline setting for an AccountUser.
func (r *AccountUserRepo) UpdateAutoOffline(ctx context.Context, accountID, userID uint, autoOffline bool) error {
return r.db.WithContext(ctx).
Model(&model.AccountUser{}).
Where("account_id = ? AND user_id = ?", accountID, userID).
Update("auto_offline", autoOffline).Error
}
// UpdateActiveAt updates the active_at timestamp for an AccountUser.
func (r *AccountUserRepo) UpdateActiveAt(ctx context.Context, accountID, userID uint) error {
return r.db.WithContext(ctx).
Model(&model.AccountUser{}).
Where("account_id = ? AND user_id = ?", accountID, userID).
Update("active_at", gorm.Expr("NOW()")).Error
}
// FindByAccountAndUserOrFail retrieves an AccountUser by accountID and userID.
// Returns error if not found.
func (r *AccountUserRepo) FindByAccountAndUserOrFail(ctx context.Context, accountID, userID uint) (*model.AccountUser, error) {
var au model.AccountUser
err := r.db.WithContext(ctx).
Preload("Account").
Where("account_id = ? AND user_id = ?", accountID, userID).
First(&au).Error
if err != nil {
return nil, err
}
return &au, nil
}
// FindByUserWithAccounts retrieves all account memberships for a user with accounts loaded.
func (r *AccountUserRepo) FindByUserWithAccounts(ctx context.Context, userID uint) ([]model.AccountUser, error) {
var accountUsers []model.AccountUser
err := r.db.WithContext(ctx).
Preload("Account").
Where("user_id = ?", userID).
Order("active_at DESC").
Order("id ASC").
Find(&accountUsers).Error
return accountUsers, err
}
// Create inserts a new AccountUser record.
func (r *AccountUserRepo) Create(ctx context.Context, au *model.AccountUser) error {
return r.db.WithContext(ctx).Create(au).Error
}
// Delete removes an AccountUser by ID.
func (r *AccountUserRepo) Delete(ctx context.Context, id uint) error {
return r.db.WithContext(ctx).Delete(&model.AccountUser{}, id).Error
}
// UpdateRole changes the role of an AccountUser.
func (r *AccountUserRepo) UpdateRole(ctx context.Context, id uint, role string) error {
return r.db.WithContext(ctx).Model(&model.AccountUser{}).Where("id = ?", id).Update("role", role).Error
}