Add photo upload as a real onboarding step

Photos are now step 2 of onboarding (right after basic info) instead
of only being manageable from the profile page later. Uploading still
goes through the existing /profile/photos endpoint; the new
POST /onboarding/photos just confirms at least one photo exists and
advances the step counter. Renumbered the OnboardingStep enum
accordingly (9 steps total now).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-16 09:21:50 +03:30
parent fda1b130ff
commit 15583009e0
6 changed files with 54 additions and 11 deletions
+13
View File
@@ -66,6 +66,19 @@ func (h *OnboardingHandler) BasicInfo(c *gin.Context) {
utils.Success(c, http.StatusOK, "اطلاعات پایه ثبت شد", h.onboardingService.GetStatus(user))
}
// Photos POST /onboarding/photos — تایید مرحله‌ی عکس‌ها (خودِ آپلود از /profile/photos انجام می‌شه)
func (h *OnboardingHandler) Photos(c *gin.Context) {
user, ok := currentUser(c)
if !ok {
return
}
if err := h.onboardingService.ConfirmPhotos(user); err != nil {
h.handleStepError(c, err)
return
}
utils.Success(c, http.StatusOK, "عکس‌ها ثبت شد", h.onboardingService.GetStatus(user))
}
// BodyInfo POST /onboarding/body-info
func (h *OnboardingHandler) BodyInfo(c *gin.Context) {
user, ok := currentUser(c)
+2
View File
@@ -26,6 +26,7 @@ type OnboardingStep int
const (
StepBasicInfo OnboardingStep = iota + 1
StepPhotos
StepBodyInfo
StepProfileQuestions
StepEducation
@@ -58,6 +59,7 @@ var OnboardingStepOrder = []struct {
Key string
}{
{StepBasicInfo, "basic_info"},
{StepPhotos, "photos"},
{StepBodyInfo, "body_info"},
{StepProfileQuestions, "profile_questions"},
{StepEducation, "education"},
+1
View File
@@ -59,6 +59,7 @@ func Setup(
{
onboarding.GET("/status", h.Onboarding.Status)
onboarding.POST("/basic-info", h.Onboarding.BasicInfo)
onboarding.POST("/photos", h.Onboarding.Photos)
onboarding.POST("/body-info", h.Onboarding.BodyInfo)
onboarding.GET("/profile-questions", h.Onboarding.AvailableQuestions)
onboarding.POST("/profile-questions", h.Onboarding.SetProfileQuestions)
+23 -1
View File
@@ -17,23 +17,27 @@ var (
ErrSelectionCount = errors.New("تعداد گزینه‌های انتخابی مجاز نیست")
ErrQuestionsInvalid = errors.New("سوالات انتخابی معتبر نیستند")
ErrDuplicateQuestion = errors.New("هر سوال فقط یک‌بار می‌تواند انتخاب شود")
ErrNoPhotos = errors.New("برای ادامه حداقل باید یک عکس اضافه کنی")
)
type OnboardingService struct {
userRepo *repository.UserRepository
optionRepo *repository.OptionRepository
questionRepo *repository.QuestionRepository
photoRepo *repository.PhotoRepository
}
func NewOnboardingService(
userRepo *repository.UserRepository,
optionRepo *repository.OptionRepository,
questionRepo *repository.QuestionRepository,
photoRepo *repository.PhotoRepository,
) *OnboardingService {
return &OnboardingService{
userRepo: userRepo,
optionRepo: optionRepo,
questionRepo: questionRepo,
photoRepo: photoRepo,
}
}
@@ -109,7 +113,25 @@ func (s *OnboardingService) SetBasicInfo(user *models.User, req dto.BasicInfoReq
return s.advanceIfCurrent(user, models.StepBasicInfo)
}
// ---------------- مرحله ۲: قد و وزن ----------------
// ---------------- مرحله ۲: عکس‌ها (حداقل ۱ عکس) ----------------
// ConfirmPhotos عکس واقعی رو از طریق endpoint عمومی /profile/photos آپلود می‌کنیم؛
// این متد فقط تایید می‌کنه که حداقل یک عکس هست و مرحله رو جلو می‌بره.
func (s *OnboardingService) ConfirmPhotos(user *models.User) error {
if err := s.canModifyStep(user, models.StepPhotos); err != nil {
return err
}
count, err := s.photoRepo.CountByUser(user.ID)
if err != nil {
return err
}
if count < 1 {
return ErrNoPhotos
}
return s.advanceIfCurrent(user, models.StepPhotos)
}
// ---------------- مرحله ۳: قد و وزن ----------------
func (s *OnboardingService) SetBodyInfo(user *models.User, req dto.BodyInfoRequest) error {
if err := s.canModifyStep(user, models.StepBodyInfo); err != nil {