// codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/service/me/verifyprofile.go package me import ( "context" "errors" "go.uber.org/zap" "github.com/gocql/gocql" "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config" "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config/constants" domain "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/user" uc_user "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/usecase/user" "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror" ) type VerifyProfileRequestDTO struct { // Common fields Country string `json:"country,omitempty"` Region string `json:"region,omitempty"` City string `json:"city,omitempty"` PostalCode string `json:"postal_code,omitempty"` AddressLine1 string `json:"address_line1,omitempty"` AddressLine2 string `json:"address_line2,omitempty"` HasShippingAddress bool `json:"has_shipping_address,omitempty"` ShippingName string `json:"shipping_name,omitempty"` ShippingPhone string `json:"shipping_phone,omitempty"` ShippingCountry string `json:"shipping_country,omitempty"` ShippingRegion string `json:"shipping_region,omitempty"` ShippingCity string `json:"shipping_city,omitempty"` ShippingPostalCode string `json:"shipping_postal_code,omitempty"` ShippingAddressLine1 string `json:"shipping_address_line1,omitempty"` ShippingAddressLine2 string `json:"shipping_address_line2,omitempty"` HowDidYouHearAboutUs int8 `json:"how_did_you_hear_about_us,omitempty"` HowDidYouHearAboutUsOther string `json:"how_did_you_hear_about_us_other,omitempty"` WebsiteURL string `json:"website_url,omitempty"` Description string `bson:"description" json:"description"` // Customer specific fields HowLongCollectingComicBooksForGrading int8 `json:"how_long_collecting_comic_books_for_grading,omitempty"` HasPreviouslySubmittedComicBookForGrading int8 `json:"has_previously_submitted_comic_book_for_grading,omitempty"` HasOwnedGradedComicBooks int8 `json:"has_owned_graded_comic_books,omitempty"` HasRegularComicBookShop int8 `json:"has_regular_comic_book_shop,omitempty"` HasPreviouslyPurchasedFromAuctionSite int8 `json:"has_previously_purchased_from_auction_site,omitempty"` HasPreviouslyPurchasedFromFacebookMarketplace int8 `json:"has_previously_purchased_from_facebook_marketplace,omitempty"` HasRegularlyAttendedComicConsOrCollectibleShows int8 `json:"has_regularly_attended_comic_cons_or_collectible_shows,omitempty"` // Retailer specific fields ComicBookStoreName string `json:"comic_book_store_name,omitempty"` StoreLogo string `json:"store_logo,omitempty"` HowLongStoreOperating int8 `json:"how_long_store_operating,omitempty"` GradingComicsExperience string `json:"grading_comics_experience,omitempty"` RetailPartnershipReason string `json:"retail_partnership_reason,omitempty"` ComicCoinPartnershipReason string `json:"comic_coin_partnership_reason,omitempty"` EstimatedSubmissionsPerMonth int8 `json:"estimated_submissions_per_month,omitempty"` HasOtherGradingService int8 `json:"has_other_grading_service,omitempty"` OtherGradingServiceName string `json:"other_grading_service_name,omitempty"` RequestWelcomePackage int8 `json:"request_welcome_package,omitempty"` // Explicitly specify user role if needed (overrides the user's current role) UserRole int8 `json:"user_role,omitempty"` } type VerifyProfileResponseDTO struct { Message string `json:"message"` UserRole int8 `json:"user_role"` Status int8 `json:"profile_verification_status"` } type VerifyProfileService interface { Execute(sessCtx context.Context, req *VerifyProfileRequestDTO) (*VerifyProfileResponseDTO, error) } type verifyProfileServiceImpl struct { config *config.Configuration logger *zap.Logger userGetByIDUseCase uc_user.UserGetByIDUseCase userUpdateUseCase uc_user.UserUpdateUseCase } func NewVerifyProfileService( config *config.Configuration, logger *zap.Logger, userGetByIDUseCase uc_user.UserGetByIDUseCase, userUpdateUseCase uc_user.UserUpdateUseCase, ) VerifyProfileService { return &verifyProfileServiceImpl{ config: config, logger: logger, userGetByIDUseCase: userGetByIDUseCase, userUpdateUseCase: userUpdateUseCase, } } func (s *verifyProfileServiceImpl) Execute( sessCtx context.Context, req *VerifyProfileRequestDTO, ) (*VerifyProfileResponseDTO, error) { // // STEP 1: Get required from context. // userID, ok := sessCtx.Value(constants.SessionUserID).(gocql.UUID) if !ok { s.logger.Error("Failed getting local user id", zap.Any("error", "Not found in context: user_id")) return nil, errors.New("user id not found in context") } // // STEP 2: Retrieve user from database // user, err := s.userGetByIDUseCase.Execute(sessCtx, userID) if err != nil { s.logger.Error("Failed retrieving user", zap.Any("error", err)) return nil, err } if user == nil { s.logger.Error("User not found", zap.Any("userID", userID)) return nil, httperror.NewForBadRequestWithSingleField("non_field_error", "User not found") } // Check if we need to override the user role based on the request if req.UserRole != 0 && (req.UserRole == domain.UserRoleIndividual || req.UserRole == domain.UserRoleCompany) { s.logger.Info("Setting user role based on request", zap.Int("original_role", int(user.Role)), zap.Int("new_role", int(req.UserRole))) user.Role = req.UserRole } // // STEP 3: Validate request based on user role // e := make(map[string]string) // Validate common fields regardless of role s.validateCommonFields(req, e) // Role-specific validation if user.Role == domain.UserRoleIndividual { s.validateCustomerFields(req, e) } else if user.Role == domain.UserRoleCompany { s.validateRetailerFields(req, e) } else { s.logger.Warn("Unrecognized user role", zap.Int("role", int(user.Role))) e["user_role"] = "Invalid user role. Must be either customer or retailer." } // Return validation errors if any if len(e) != 0 { s.logger.Warn("Failed validation", zap.Any("errors", e)) return nil, httperror.NewForBadRequest(&e) } // // STEP 4: Update user profile based on role // // Update common fields s.updateCommonFields(user, req) // // STEP 5: Save updated user to database // if err := s.userUpdateUseCase.Execute(sessCtx, user); err != nil { s.logger.Error("Failed to update user", zap.Any("error", err)) return nil, err } // // STEP 6: Generate appropriate response // var responseMessage string if user.Role == domain.UserRoleIndividual { responseMessage = "Your profile has been submitted for verification. You'll be notified once it's been reviewed." } else if user.Role == domain.UserRoleCompany { responseMessage = "Your retailer profile has been submitted for verification. Our team will review your application and contact you soon." } else { responseMessage = "Your profile has been submitted for verification." } return &VerifyProfileResponseDTO{ Message: responseMessage, UserRole: user.Role, }, nil } // validateCommonFields validates fields common to all user types func (s *verifyProfileServiceImpl) validateCommonFields(req *VerifyProfileRequestDTO, e map[string]string) { if req.Country == "" { e["country"] = "Country is required" } if req.City == "" { e["city"] = "City is required" } if req.AddressLine1 == "" { e["address_line1"] = "Address is required" } if req.PostalCode == "" { e["postal_code"] = "Postal code is required" } if req.HowDidYouHearAboutUs == 0 { e["how_did_you_hear_about_us"] = "How did you hear about us is required" } if req.HowDidYouHearAboutUs == 7 && req.HowDidYouHearAboutUsOther == "" { // Assuming 7 is "Other" e["how_did_you_hear_about_us_other"] = "Please specify how you heard about us" } // Validate shipping address if it's enabled if req.HasShippingAddress { if req.ShippingName == "" { e["shipping_name"] = "Shipping name is required" } if req.ShippingPhone == "" { e["shipping_phone"] = "Shipping phone is required" } if req.ShippingCountry == "" { e["shipping_country"] = "Shipping country is required" } if req.ShippingCity == "" { e["shipping_city"] = "Shipping city is required" } if req.ShippingAddressLine1 == "" { e["shipping_address_line1"] = "Shipping address is required" } if req.ShippingPostalCode == "" { e["shipping_postal_code"] = "Shipping postal code is required" } } // More common fields... if req.WebsiteURL == "" { e["website_url"] = "Website URL is required" } if req.Description == "" { e["description"] = "Description is required" } } // validateCustomerFields validates fields specific to customers func (s *verifyProfileServiceImpl) validateCustomerFields(req *VerifyProfileRequestDTO, e map[string]string) { if req.HowLongCollectingComicBooksForGrading == 0 { e["how_long_collecting_comic_books_for_grading"] = "How long you've been collecting comic books for grading is required" } if req.HasPreviouslySubmittedComicBookForGrading == 0 { e["has_previously_submitted_comic_book_for_grading"] = "Previous submission information is required" } if req.HasOwnedGradedComicBooks == 0 { e["has_owned_graded_comic_books"] = "Information about owning graded comic books is required" } if req.HasRegularComicBookShop == 0 { e["has_regular_comic_book_shop"] = "Regular comic book shop information is required" } if req.HasPreviouslyPurchasedFromAuctionSite == 0 { e["has_previously_purchased_from_auction_site"] = "Auction site purchase information is required" } if req.HasPreviouslyPurchasedFromFacebookMarketplace == 0 { e["has_previously_purchased_from_facebook_marketplace"] = "Facebook Marketplace purchase information is required" } if req.HasRegularlyAttendedComicConsOrCollectibleShows == 0 { e["has_regularly_attended_comic_cons_or_collectible_shows"] = "Comic convention attendance information is required" } } // validateRetailerFields validates fields specific to retailers func (s *verifyProfileServiceImpl) validateRetailerFields(req *VerifyProfileRequestDTO, e map[string]string) { if req.ComicBookStoreName == "" { e["comic_book_store_name"] = "Store name is required" } if req.HowLongStoreOperating == 0 { e["how_long_store_operating"] = "Store operation duration is required" } if req.GradingComicsExperience == "" { e["grading_comics_experience"] = "Grading comics experience is required" } if req.RetailPartnershipReason == "" { e["retail_partnership_reason"] = "Retail partnership reason is required" } if req.ComicBookStoreName == "" { e["comic_book_store_name"] = "Comic book store name is required" } if req.EstimatedSubmissionsPerMonth == 0 { e["estimated_submissions_per_month"] = "Estimated submissions per month is required" } if req.HasOtherGradingService == 0 { e["has_other_grading_service"] = "Other grading service information is required" } if req.HasOtherGradingService == 1 && req.OtherGradingServiceName == "" { e["other_grading_service_name"] = "Please specify the grading service" } if req.RequestWelcomePackage == 0 { e["request_welcome_package"] = "Welcome package request information is required" } } // updateCommonFields updates common fields for all user types func (s *verifyProfileServiceImpl) updateCommonFields(user *domain.User, req *VerifyProfileRequestDTO) { user.ProfileData.Country = req.Country user.ProfileData.Region = req.Region user.ProfileData.City = req.City user.ProfileData.PostalCode = req.PostalCode user.ProfileData.AddressLine1 = req.AddressLine1 user.ProfileData.AddressLine2 = req.AddressLine2 user.ProfileData.HasShippingAddress = req.HasShippingAddress user.ProfileData.ShippingName = req.ShippingName user.ProfileData.ShippingPhone = req.ShippingPhone user.ProfileData.ShippingCountry = req.ShippingCountry user.ProfileData.ShippingRegion = req.ShippingRegion user.ProfileData.ShippingCity = req.ShippingCity user.ProfileData.ShippingPostalCode = req.ShippingPostalCode user.ProfileData.ShippingAddressLine1 = req.ShippingAddressLine1 user.ProfileData.ShippingAddressLine2 = req.ShippingAddressLine2 }