Add bio field and daily streak check-in for gamification

- User.Bio: a short (300 char) freeform profile bio, editable via
  PATCH /profile/bio, shown on discover cards and candidate detail (richer
  profiles beyond the fixed onboarding fields).
- User.StreakCount/LastActiveDate: POST /profile/checkin increments the
  streak on consecutive days, resets after a gap, no-ops if already
  checked in today. Backs a daily-return hook on the frontend.
This commit is contained in:
2026-09-20 12:26:06 +03:30
parent 1dc96e86fc
commit c8e6f80c51
9 changed files with 110 additions and 0 deletions
+44
View File
@@ -2,7 +2,10 @@ package service
import (
"errors"
"strings"
"time"
"dating-app/internal/dto"
"dating-app/internal/models"
"dating-app/internal/repository"
)
@@ -97,3 +100,44 @@ func (s *ProfileService) UpdateLocation(user *models.User, lat, lng float64) err
"longitude": lng,
})
}
// UpdateBio بیوگرافی کوتاه پروفایل رو ذخیره می‌کنه (طول توسط binding در DTO چک شده)
func (s *ProfileService) UpdateBio(user *models.User, bio string) error {
bio = strings.TrimSpace(bio)
if err := s.userRepo.UpdateFields(user, map[string]interface{}{"bio": bio}); err != nil {
return err
}
user.Bio = &bio
return nil
}
// CheckIn ورود روزانه‌ی کاربر رو ثبت می‌کنه و استریک (چند روز پشت‌سرهم) رو
// به‌روز می‌کنه: اگه دیروز هم اومده بوده، استریک +۱ می‌شه؛ اگه امروز قبلاً
// چک‌این کرده، چیزی تغییر نمی‌کنه؛ وگرنه (وقفه افتاده) استریک از ۱ شروع می‌شه.
func (s *ProfileService) CheckIn(user *models.User) (*dto.CheckInResult, error) {
now := time.Now()
today := now.Format("2006-01-02")
if user.LastActiveDate != nil && user.LastActiveDate.Format("2006-01-02") == today {
return &dto.CheckInResult{StreakCount: user.StreakCount, IsNewToday: false}, nil
}
newStreak := 1
if user.LastActiveDate != nil {
yesterday := now.AddDate(0, 0, -1).Format("2006-01-02")
if user.LastActiveDate.Format("2006-01-02") == yesterday {
newStreak = user.StreakCount + 1
}
}
if err := s.userRepo.UpdateFields(user, map[string]interface{}{
"streak_count": newStreak,
"last_active_date": now,
}); err != nil {
return nil, err
}
user.StreakCount = newStreak
user.LastActiveDate = &now
return &dto.CheckInResult{StreakCount: newStreak, IsNewToday: true}, nil
}