[BE-API-017] be-api: Implement user follow/unfollow endpoints
- Added FollowUser and UnfollowUser handlers in ProfileHandler - Added socialService field and SetSocialService method - Initialized SocialService in setupUserRoutes - Added routes: POST /users/:id/follow and DELETE /users/:id/follow - Handlers use existing SocialService methods - Includes validation to prevent users from following themselves - Handlers use standard API response format Phase: PHASE-2 Priority: P2 Progress: 26/267 (9.7%)
This commit is contained in:
parent
a20266299c
commit
9c49bce93e
3 changed files with 121 additions and 1 deletions
|
|
@ -1864,7 +1864,18 @@
|
|||
"description": "POST /api/v1/users/:id/follow, DELETE /api/v1/users/:id/follow",
|
||||
"owner": "backend",
|
||||
"estimated_hours": 3,
|
||||
"status": "todo",
|
||||
"status": "completed",
|
||||
"completion": {
|
||||
"completed_at": "2025-12-23T09:57:30Z",
|
||||
"actual_hours": 1.0,
|
||||
"commits": [],
|
||||
"files_changed": [
|
||||
"veza-backend-api/internal/handlers/profile_handler.go",
|
||||
"veza-backend-api/internal/api/router.go"
|
||||
],
|
||||
"notes": "Added FollowUser and UnfollowUser handlers in ProfileHandler. Added socialService field and SetSocialService method to ProfileHandler. Initialized SocialService in setupUserRoutes and injected it into ProfileHandler. Added routes: POST /users/:id/follow and DELETE /users/:id/follow (protected). Handlers use existing SocialService.FollowUser and SocialService.UnfollowUser methods. Includes validation to prevent users from following themselves. Handlers use standard API response format (RespondSuccess, RespondWithAppError).",
|
||||
"issues_encountered": []
|
||||
},
|
||||
"files_involved": [],
|
||||
"implementation_steps": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -365,6 +365,9 @@ func (r *APIRouter) setupUserRoutes(router *gin.RouterGroup) {
|
|||
if r.config != nil && r.config.PermissionService != nil {
|
||||
profileHandler.SetPermissionService(r.config.PermissionService)
|
||||
}
|
||||
// BE-API-017: Initialize SocialService for follow/unfollow functionality
|
||||
socialService := services.NewSocialService(r.db, r.logger)
|
||||
profileHandler.SetSocialService(socialService)
|
||||
|
||||
users := router.Group("/users")
|
||||
{
|
||||
|
|
@ -387,6 +390,10 @@ func (r *APIRouter) setupUserRoutes(router *gin.RouterGroup) {
|
|||
|
||||
protected.GET("/:id/completion", profileHandler.GetProfileCompletion)
|
||||
|
||||
// BE-API-017: User follow/unfollow endpoints
|
||||
protected.POST("/:id/follow", profileHandler.FollowUser) // Follow user endpoint
|
||||
protected.DELETE("/:id/follow", profileHandler.UnfollowUser) // Unfollow user endpoint
|
||||
|
||||
// BE-API-007: User role assignment routes
|
||||
roleService := services.NewRoleService(r.db.GormDB)
|
||||
roleHandler := handlers.NewRoleHandler(roleService, r.logger)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ type ProfileHandler struct {
|
|||
userService *services.UserService
|
||||
commonHandler *CommonHandler
|
||||
permissionService *services.PermissionService // MOD-P1-003: Added for admin check
|
||||
socialService *services.SocialService // BE-API-017: Added for follow/unfollow functionality
|
||||
logger *zap.Logger // BE-API-017: Added for logging
|
||||
}
|
||||
|
||||
// NewProfileHandler creates a new ProfileHandler instance
|
||||
|
|
@ -26,9 +28,16 @@ func NewProfileHandler(userService *services.UserService, logger *zap.Logger) *P
|
|||
return &ProfileHandler{
|
||||
userService: userService,
|
||||
commonHandler: NewCommonHandler(logger),
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// SetSocialService sets the social service for follow/unfollow functionality
|
||||
// BE-API-017: Implement user follow/unfollow endpoints
|
||||
func (h *ProfileHandler) SetSocialService(socialService *services.SocialService) {
|
||||
h.socialService = socialService
|
||||
}
|
||||
|
||||
// SetPermissionService définit le service de permissions (pour injection de dépendance)
|
||||
// MOD-P1-003: Added for admin check in ownership verification
|
||||
func (h *ProfileHandler) SetPermissionService(permissionService *services.PermissionService) {
|
||||
|
|
@ -197,6 +206,99 @@ func (h *ProfileHandler) SearchUsers(c *gin.Context) {
|
|||
})
|
||||
}
|
||||
|
||||
// FollowUser gère le suivi d'un utilisateur
|
||||
// POST /api/v1/users/:id/follow
|
||||
// BE-API-017: Implement user follow/unfollow endpoints
|
||||
func (h *ProfileHandler) FollowUser(c *gin.Context) {
|
||||
// Récupérer l'ID de l'utilisateur à suivre depuis l'URL
|
||||
userIDStr := c.Param("id")
|
||||
userID, err := uuid.Parse(userIDStr)
|
||||
if err != nil {
|
||||
RespondWithAppError(c, apperrors.NewValidationError("invalid user id"))
|
||||
return
|
||||
}
|
||||
|
||||
// Récupérer l'ID de l'utilisateur authentifié
|
||||
followerID, ok := GetUserIDUUID(c)
|
||||
if !ok {
|
||||
return // Erreur déjà envoyée par GetUserIDUUID
|
||||
}
|
||||
|
||||
// Vérifier qu'on ne peut pas se suivre soi-même
|
||||
if followerID == userID {
|
||||
RespondWithAppError(c, apperrors.NewValidationError("cannot follow yourself"))
|
||||
return
|
||||
}
|
||||
|
||||
// Vérifier que l'utilisateur existe (on peut utiliser GetProfile qui vérifie l'existence)
|
||||
// Pour simplifier, on laisse le service social gérer l'erreur si l'utilisateur n'existe pas
|
||||
|
||||
// Vérifier que le service social est initialisé
|
||||
if h.socialService == nil {
|
||||
RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Social service not initialized", nil))
|
||||
return
|
||||
}
|
||||
|
||||
// Suivre l'utilisateur
|
||||
err = h.socialService.FollowUser(followerID, userID)
|
||||
if err != nil {
|
||||
h.logger.Error("failed to follow user",
|
||||
zap.Error(err),
|
||||
zap.String("follower_id", followerID.String()),
|
||||
zap.String("followed_id", userID.String()))
|
||||
RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to follow user", err))
|
||||
return
|
||||
}
|
||||
|
||||
h.logger.Info("user followed",
|
||||
zap.String("follower_id", followerID.String()),
|
||||
zap.String("followed_id", userID.String()))
|
||||
|
||||
RespondSuccess(c, http.StatusOK, gin.H{"message": "User followed successfully"})
|
||||
}
|
||||
|
||||
// UnfollowUser gère l'arrêt du suivi d'un utilisateur
|
||||
// DELETE /api/v1/users/:id/follow
|
||||
// BE-API-017: Implement user follow/unfollow endpoints
|
||||
func (h *ProfileHandler) UnfollowUser(c *gin.Context) {
|
||||
// Récupérer l'ID de l'utilisateur à ne plus suivre depuis l'URL
|
||||
userIDStr := c.Param("id")
|
||||
userID, err := uuid.Parse(userIDStr)
|
||||
if err != nil {
|
||||
RespondWithAppError(c, apperrors.NewValidationError("invalid user id"))
|
||||
return
|
||||
}
|
||||
|
||||
// Récupérer l'ID de l'utilisateur authentifié
|
||||
followerID, ok := GetUserIDUUID(c)
|
||||
if !ok {
|
||||
return // Erreur déjà envoyée par GetUserIDUUID
|
||||
}
|
||||
|
||||
// Vérifier que le service social est initialisé
|
||||
if h.socialService == nil {
|
||||
RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Social service not initialized", nil))
|
||||
return
|
||||
}
|
||||
|
||||
// Ne plus suivre l'utilisateur
|
||||
err = h.socialService.UnfollowUser(followerID, userID)
|
||||
if err != nil {
|
||||
h.logger.Error("failed to unfollow user",
|
||||
zap.Error(err),
|
||||
zap.String("follower_id", followerID.String()),
|
||||
zap.String("followed_id", userID.String()))
|
||||
RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to unfollow user", err))
|
||||
return
|
||||
}
|
||||
|
||||
h.logger.Info("user unfollowed",
|
||||
zap.String("follower_id", followerID.String()),
|
||||
zap.String("followed_id", userID.String()))
|
||||
|
||||
RespondSuccess(c, http.StatusOK, gin.H{"message": "User unfollowed successfully"})
|
||||
}
|
||||
|
||||
// UpdateProfileRequest represents the request body for updating a user profile
|
||||
type UpdateProfileRequest struct {
|
||||
FirstName string `json:"first_name" binding:"omitempty,max=100" validate:"omitempty,max=100"`
|
||||
|
|
|
|||
Loading…
Reference in a new issue