769e76313f
- Recovery middleware (no more crash-on-panic), request metrics/monitoring store + admin API, in-memory TTL caching for read-heavy option/question/ category lookups, gzip + rate-limiting middleware, sanitized chat message encoding, and pg_dump-based database backups with an admin API. - Fix N+1 queries in the discover feed, candidate detail, and match list endpoints by batching selection/photo/user/last-message lookups into single queries instead of one-per-item loops. - Tune DB connection pooling and add HTTP server timeouts + graceful shutdown so the API degrades gracefully instead of falling over under load. - Fix a null (vs empty array) JSON response for candidates/matches without photos or interests, which crashed the web app's swipe card.
117 lines
3.8 KiB
Go
117 lines
3.8 KiB
Go
package repository
|
|
|
|
import (
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"dating-app/internal/models"
|
|
)
|
|
|
|
type UserRepository struct {
|
|
db *gorm.DB
|
|
}
|
|
|
|
func NewUserRepository(db *gorm.DB) *UserRepository {
|
|
return &UserRepository{db: db}
|
|
}
|
|
|
|
func (r *UserRepository) FindByPhone(phone string) (*models.User, error) {
|
|
var user models.User
|
|
err := r.db.Where("phone = ?", phone).First(&user).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &user, nil
|
|
}
|
|
|
|
func (r *UserRepository) FindByID(id uint) (*models.User, error) {
|
|
var user models.User
|
|
err := r.db.First(&user, id).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &user, nil
|
|
}
|
|
|
|
// FindByIDs چند کاربر رو با هم در یک کوئری برمیگردونه (بهجای یک FindByID
|
|
// جدا بهازای هر id)؛ برای لیستهایی مثل مچها که باید اطلاعات طرف مقابل
|
|
// چندین مچ رو یکجا نشون بدن.
|
|
func (r *UserRepository) FindByIDs(ids []uint) ([]models.User, error) {
|
|
var users []models.User
|
|
if len(ids) == 0 {
|
|
return users, nil
|
|
}
|
|
err := r.db.Where("id IN ?", ids).Find(&users).Error
|
|
return users, err
|
|
}
|
|
|
|
// GetOrCreateByPhone کاربر را با شمارهی دادهشده پیدا میکند یا در صورت نبود میسازد.
|
|
func (r *UserRepository) GetOrCreateByPhone(phone string) (*models.User, bool, error) {
|
|
var user models.User
|
|
err := r.db.Where("phone = ?", phone).First(&user).Error
|
|
if err == nil {
|
|
return &user, false, nil
|
|
}
|
|
if err != gorm.ErrRecordNotFound {
|
|
return nil, false, err
|
|
}
|
|
|
|
user = models.User{
|
|
Phone: phone,
|
|
State: models.UserStateInactive,
|
|
IsActive: true,
|
|
CurrentStep: models.StepBasicInfo,
|
|
}
|
|
if err := r.db.Create(&user).Error; err != nil {
|
|
return nil, false, err
|
|
}
|
|
return &user, true, nil
|
|
}
|
|
|
|
// ListPendingApproval کاربرانی که آنبوردینگ رو تکمیل کردن و منتظر بررسی ادمیناند
|
|
// رو به ترتیب قدیمیترین اول برمیگردونه.
|
|
func (r *UserRepository) ListPendingApproval() ([]models.User, error) {
|
|
var users []models.User
|
|
err := r.db.
|
|
Where("is_onboarded = ? AND approval_status = ?", true, models.ApprovalPending).
|
|
Order("updated_at asc").
|
|
Find(&users).Error
|
|
return users, err
|
|
}
|
|
|
|
// ListDiscoverable کاندیداهای بخش دوستیابی رو برمیگردونه: تاییدشده، فعال،
|
|
// جنسیت مخالف کاربر (بهعنوان سادهترین فرض پیشفرض؛ فیلد ترجیح جنسیتی/گرایش
|
|
// هنوز توی سیستم وجود نداره) و کسایی که قبلاً روشون سواپ نزده.
|
|
// چون فاصله باید توی Go محاسبه بشه (haversine)، یک batch بزرگتر از حد
|
|
// نهایی برمیگردونه تا بعد از فیلتر فاصله همچنان گزینهی کافی بمونه.
|
|
func (r *UserRepository) ListDiscoverable(currentUserID uint, oppositeGender models.Gender, minAge, maxAge int, batchSize int) ([]models.User, error) {
|
|
var users []models.User
|
|
now := time.Now()
|
|
minBirth := now.AddDate(-maxAge-1, 0, 1)
|
|
maxBirth := now.AddDate(-minAge, 0, 0)
|
|
|
|
err := r.db.
|
|
Where("id != ?", currentUserID).
|
|
Where("is_onboarded = ? AND approval_status = ? AND is_active = ?", true, models.ApprovalApproved, true).
|
|
Where("gender = ?", oppositeGender).
|
|
Where("birth_date BETWEEN ? AND ?", minBirth, maxBirth).
|
|
Where("NOT EXISTS (SELECT 1 FROM swipes s WHERE s.swiper_id = ? AND s.swiped_id = users.id)", currentUserID).
|
|
Order("id asc").
|
|
Limit(batchSize).
|
|
Find(&users).Error
|
|
return users, err
|
|
}
|
|
|
|
func (r *UserRepository) Save(user *models.User) error {
|
|
return r.db.Save(user).Error
|
|
}
|
|
|
|
func (r *UserRepository) UpdateFields(user *models.User, fields map[string]interface{}) error {
|
|
return r.db.Model(user).Updates(fields).Error
|
|
}
|
|
|
|
func (r *UserRepository) DB() *gorm.DB {
|
|
return r.db
|
|
}
|