Files
gochat/internal/repository/account_repo.go
T

215 lines
6.6 KiB
Go

package repository
import (
"context"
"time"
"gorm.io/gorm"
"github.com/gochat/gochat/internal/model"
)
// AccountRepo implements GORM repository for Account.
// Reference: Chatwoot app/models/account.rb + AccountUser join
type AccountRepo struct {
db *gorm.DB
}
// NewAccountRepo creates a new Account repository.
func NewAccountRepo(db *gorm.DB) *AccountRepo {
return &AccountRepo{db: db}
}
// FindByID retrieves an account by primary key.
func (r *AccountRepo) FindByID(ctx context.Context, id uint) (*model.Account, error) {
var account model.Account
err := r.db.WithContext(ctx).First(&account, id).Error
if err != nil {
return nil, err
}
return &account, nil
}
// FindByUser retrieves all accounts accessible by a user via AccountUser join.
func (r *AccountRepo) FindByUser(ctx context.Context, userID uint, offset, limit int) ([]model.Account, int64, error) {
var accounts []model.Account
var total int64
// Count total accounts for this user
countDB := r.db.WithContext(ctx).Model(&model.Account{}).
Joins("JOIN account_users ON account_users.account_id = accounts.id").
Where("account_users.user_id = ?", userID)
if err := countDB.Count(&total).Error; err != nil {
return nil, 0, err
}
// Fetch paginated accounts
err := r.db.WithContext(ctx).
Joins("JOIN account_users ON account_users.account_id = accounts.id").
Where("account_users.user_id = ?", userID).
Offset(offset).Limit(limit).
Find(&accounts).Error
if err != nil {
return nil, 0, err
}
return accounts, total, nil
}
// Create inserts a new account.
func (r *AccountRepo) Create(ctx context.Context, account *model.Account) error {
return r.db.WithContext(ctx).Create(account).Error
}
// Update modifies an existing account.
func (r *AccountRepo) Update(ctx context.Context, account *model.Account) error {
return r.db.WithContext(ctx).Save(account).Error
}
// Delete soft-deletes an account.
func (r *AccountRepo) Delete(ctx context.Context, id uint) error {
return r.db.WithContext(ctx).Delete(&model.Account{}, id).Error
}
// FindUsersByAccount retrieves all users belonging to an account.
func (r *AccountRepo) FindUsersByAccount(ctx context.Context, accountID uint, offset, limit int) ([]model.User, int64, error) {
var users []model.User
var total int64
countDB := r.db.WithContext(ctx).Model(&model.User{}).
Joins("JOIN account_users ON account_users.user_id = users.id").
Where("account_users.account_id = ?", accountID)
if err := countDB.Count(&total).Error; err != nil {
return nil, 0, err
}
err := r.db.WithContext(ctx).
Joins("JOIN account_users ON account_users.user_id = users.id").
Where("account_users.account_id = ?", accountID).
Offset(offset).Limit(limit).
Find(&users).Error
return users, total, err
}
// AddUserToAccount creates an AccountUser association.
func (r *AccountRepo) AddUserToAccount(ctx context.Context, accountID, userID uint, role string) error {
au := model.AccountUser{
AccountID: accountID,
UserID: userID,
Role: role,
}
return r.db.WithContext(ctx).Create(&au).Error
}
// UpsertAccountUser creates or updates an AccountUser membership.
// Reference: Chatwoot Platform::Api::V1::AccountUsersController#create uses
// account.account_users.find_or_initialize_by(user_id: ...).update!(params).
func (r *AccountRepo) UpsertAccountUser(ctx context.Context, accountID, userID uint, role *string) (*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 && err != gorm.ErrRecordNotFound {
return nil, err
}
if err == gorm.ErrRecordNotFound {
au = model.AccountUser{
AccountID: accountID,
UserID: userID,
Role: "agent",
Availability: "offline",
AutoOffline: true,
}
}
if role != nil && *role != "" {
au.Role = *role
}
if err == gorm.ErrRecordNotFound {
if err := r.db.WithContext(ctx).Create(&au).Error; err != nil {
return nil, err
}
return &au, nil
}
if role != nil {
if err := r.db.WithContext(ctx).Model(&au).Update("role", au.Role).Error; err != nil {
return nil, err
}
}
return &au, nil
}
// RemoveUserFromAccount deletes an AccountUser association.
func (r *AccountRepo) RemoveUserFromAccount(ctx context.Context, accountID, userID uint) error {
return r.db.WithContext(ctx).Exec(
"DELETE FROM account_users WHERE account_id = ? AND user_id = ?", accountID, userID,
).Error
}
// Count returns total number of accounts.
func (r *AccountRepo) Count(ctx context.Context) (int64, error) {
var total int64
err := r.db.WithContext(ctx).Model(&model.Account{}).Count(&total).Error
return total, err
}
// FindAll retrieves all accounts with pagination.
func (r *AccountRepo) FindAll(ctx context.Context, offset, limit int) ([]model.Account, int64, error) {
var accounts []model.Account
var total int64
if err := r.db.WithContext(ctx).Model(&model.Account{}).Count(&total).Error; err != nil {
return nil, 0, err
}
err := r.db.WithContext(ctx).Offset(offset).Limit(limit).Find(&accounts).Error
return accounts, total, err
}
// FindAgentsByAccount retrieves AccountUser records for an account with pagination.
// Reference: Chatwoot app/controllers/api/v1/accounts/agents_controller.rb#index
func (r *AccountRepo) FindAgentsByAccount(ctx context.Context, accountID uint, offset, limit int) ([]model.AccountUser, int64, error) {
var agents []model.AccountUser
var total int64
countDB := r.db.WithContext(ctx).Model(&model.AccountUser{}).
Where("account_id = ?", accountID)
if err := countDB.Count(&total).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, total, err
}
// UpdateActiveAt updates the active_at timestamp for a user in an account.
// Reference: Chatwoot accounts_controller.rb#update_active_at
func (r *AccountRepo) UpdateActiveAt(ctx context.Context, accountID, userID uint, activeAt time.Time) error {
result := r.db.WithContext(ctx).Model(&model.AccountUser{}).
Where("account_id = ? AND user_id = ?", accountID, userID).
Update("active_at", activeAt)
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
return gorm.ErrRecordNotFound
}
return nil
}
// FindAccountUserByUserAndAccount retrieves the AccountUser record for a specific user-account pair.
func (r *AccountRepo) FindAccountUserByUserAndAccount(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 {
return nil, err
}
return &au, nil
}