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
+9
View File
@@ -137,6 +137,7 @@ func (s *DiscoveryService) GetFeed(user *models.User, maxDistanceKM *float64, mi
ID: wd.user.ID,
FirstName: wd.user.FirstName,
Age: utils.CalcAge(wd.user.BirthDate),
Bio: derefStr(wd.user.Bio),
// نبود عکس/علاقه‌مندی نباید توی JSON به‌صورت null دربیاد؛ کلاینت‌ها
// همیشه یک آرایه (حتی خالی) انتظار دارن، نه null.
Photos: orEmptyStrings(photosByUser[wd.user.ID]),
@@ -154,6 +155,13 @@ func orEmptyStrings(s []string) []string {
return s
}
func derefStr(s *string) string {
if s == nil {
return ""
}
return *s
}
// batchInterests علاقه‌مندی‌های تمام کاندیداهای فید رو در یک کوئری واحد
// می‌گیره (به‌جای یک کوئری جدا به‌ازای هر کاندیدا)؛ روی فید دیسکاوری که تا ۶۰
// کاربر با هم پردازش می‌شن، این تفاوت بین ۱ کوئری و تا ۶۰ کوئری رو ایجاد می‌کنه.
@@ -252,6 +260,7 @@ func (s *DiscoveryService) GetCandidateDetail(viewer *models.User, targetID uint
ID: target.ID,
FirstName: target.FirstName,
Age: utils.CalcAge(target.BirthDate),
Bio: derefStr(target.Bio),
Photos: urls,
DistanceKM: distance,
HeightCM: target.HeightCM,