This commit is contained in:
2026-01-25 21:59:00 +08:00
parent 7fd537bef3
commit 4cad3f0250
118 changed files with 30473 additions and 0 deletions

View File

@@ -0,0 +1,165 @@
// Package repository provides data access layer for the application
package repository
import (
"errors"
"accounting-app/internal/models"
"gorm.io/gorm"
)
// User repository errors
var (
ErrUserNotFound = errors.New("user not found")
ErrUserEmailExists = errors.New("email already exists")
ErrOAuthAccountExists = errors.New("oauth account already linked")
)
// UserRepository handles database operations for users
// Feature: api-interface-optimization
// Validates: Requirements 12, 13
type UserRepository struct {
db *gorm.DB
}
// NewUserRepository creates a new UserRepository instance
func NewUserRepository(db *gorm.DB) *UserRepository {
return &UserRepository{db: db}
}
// Create creates a new user in the database
func (r *UserRepository) Create(user *models.User) error {
// Check if email already exists
var count int64
if err := r.db.Model(&models.User{}).Where("email = ?", user.Email).Count(&count).Error; err != nil {
return err
}
if count > 0 {
return ErrUserEmailExists
}
return r.db.Create(user).Error
}
// GetByID retrieves a user by ID
func (r *UserRepository) GetByID(id uint) (*models.User, error) {
var user models.User
if err := r.db.First(&user, id).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrUserNotFound
}
return nil, err
}
return &user, nil
}
// GetByEmail retrieves a user by email
func (r *UserRepository) GetByEmail(email string) (*models.User, error) {
var user models.User
if err := r.db.Where("email = ?", email).First(&user).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrUserNotFound
}
return nil, err
}
return &user, nil
}
// Update updates a user in the database
func (r *UserRepository) Update(user *models.User) error {
return r.db.Save(user).Error
}
// Delete soft deletes a user
func (r *UserRepository) Delete(id uint) error {
result := r.db.Delete(&models.User{}, id)
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
return ErrUserNotFound
}
return nil
}
// GetByOAuthProvider retrieves a user by OAuth provider and provider ID
func (r *UserRepository) GetByOAuthProvider(provider, providerID string) (*models.User, error) {
var oauth models.OAuthAccount
if err := r.db.Where("provider = ? AND provider_id = ?", provider, providerID).First(&oauth).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrUserNotFound
}
return nil, err
}
var user models.User
if err := r.db.First(&user, oauth.UserID).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrUserNotFound
}
return nil, err
}
return &user, nil
}
// CreateOAuthAccount creates a new OAuth account linked to a user
func (r *UserRepository) CreateOAuthAccount(oauth *models.OAuthAccount) error {
// Check if OAuth account already exists
var count int64
if err := r.db.Model(&models.OAuthAccount{}).
Where("provider = ? AND provider_id = ?", oauth.Provider, oauth.ProviderID).
Count(&count).Error; err != nil {
return err
}
if count > 0 {
return ErrOAuthAccountExists
}
return r.db.Create(oauth).Error
}
// GetOAuthAccounts retrieves all OAuth accounts for a user
func (r *UserRepository) GetOAuthAccounts(userID uint) ([]models.OAuthAccount, error) {
var accounts []models.OAuthAccount
if err := r.db.Where("user_id = ?", userID).Find(&accounts).Error; err != nil {
return nil, err
}
return accounts, nil
}
// UpdateOAuthToken updates the access token for an OAuth account
func (r *UserRepository) UpdateOAuthToken(provider, providerID, accessToken string) error {
result := r.db.Model(&models.OAuthAccount{}).
Where("provider = ? AND provider_id = ?", provider, providerID).
Update("access_token", accessToken)
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
return ErrUserNotFound
}
return nil
}
// DeleteOAuthAccount removes an OAuth account link
func (r *UserRepository) DeleteOAuthAccount(userID uint, provider string) error {
result := r.db.Where("user_id = ? AND provider = ?", userID, provider).Delete(&models.OAuthAccount{})
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
return ErrUserNotFound
}
return nil
}
// EmailExists checks if an email is already registered
func (r *UserRepository) EmailExists(email string) (bool, error) {
var count int64
if err := r.db.Model(&models.User{}).Where("email = ?", email).Count(&count).Error; err != nil {
return false, err
}
return count > 0, nil
}