package handler import ( "cmp" "context" "crypto/sha256" "fmt" "strconv" "github.com/gin-gonic/gin" "with_you/internal/dto" "with_you/internal/middleware" "with_you/internal/model" "with_you/internal/pkg/response" "with_you/internal/service" ) const ( maxPageSize = 100 defaultPageSize = 20 ) func normalizePagination(page, pageSize int) (int, int) { if page < 1 { page = 1 } if pageSize < 1 || pageSize > maxPageSize { pageSize = defaultPageSize } return page, pageSize } // UserHandler 用户处理器 type UserHandler struct { userService service.UserService activityService service.UserActivityService profileAuditService service.UserProfileAuditService jwtService service.JWTService logService *service.LogService } func NewUserHandler(userService service.UserService, activityService service.UserActivityService, logService *service.LogService) *UserHandler { return &UserHandler{ userService: userService, activityService: activityService, logService: logService, } } func (h *UserHandler) SetProfileAuditService(profileAuditService service.UserProfileAuditService) { h.profileAuditService = profileAuditService } // SetJWTService 设置JWT服务 func (h *UserHandler) SetJWTService(jwtService service.JWTService) { h.jwtService = jwtService } // SetActivityService 设置活跃统计服务 func (h *UserHandler) SetActivityService(activityService service.UserActivityService) { h.activityService = activityService } // generateTokenID 生成Token ID func generateTokenID(token string) string { h := sha256.New() h.Write([]byte(token)) return fmt.Sprintf("%x", h.Sum(nil))[:16] } // Register 用户注册 func (h *UserHandler) Register(c *gin.Context) { type RegisterRequest struct { Username string `json:"username" binding:"required"` Email string `json:"email" binding:"required,email"` Password string `json:"password" binding:"required,min=6"` Nickname string `json:"nickname" binding:"required"` Phone string `json:"phone"` VerificationCode string `json:"verification_code" binding:"required"` } var req RegisterRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } user, err := h.userService.Register(c.Request.Context(), req.Username, req.Email, req.Password, req.Nickname, req.Phone, req.VerificationCode) if err != nil { response.HandleError(c, err, "failed to register") return } // 生成Token accessToken, _ := h.jwtService.GenerateAccessToken(user.ID, user.Username) refreshToken, _ := h.jwtService.GenerateRefreshToken(user.ID, user.Username) ip := c.ClientIP() userAgent := c.GetHeader("User-Agent") // 记录用户活跃 if h.activityService != nil { go func() { ctx := context.Background() loginType := model.LoginTypeLogin h.activityService.RecordUserActive(ctx, user.ID, loginType, ip, userAgent) }() } // 记录注册后首次登录 if h.logService != nil { go func() { h.logService.LoginLog.RecordLogin(&model.LoginLog{ UserID: user.ID, UserName: user.Username, NickName: user.Nickname, LoginType: string(model.LoginTypePassword), Event: string(model.LoginEventLogin), IP: ip, UserAgent: userAgent, Result: string(model.LoginResultSuccess), TokenID: generateTokenID(refreshToken), }) }() } response.Success(c, gin.H{ "user": dto.ConvertUserToSelfResponse(user, 0), "token": accessToken, "refresh_token": refreshToken, }) } // Login 用户登录 func (h *UserHandler) Login(c *gin.Context) { type LoginRequest struct { Username string `json:"username"` Account string `json:"account"` Password string `json:"password" binding:"required"` } var req LoginRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } account := cmp.Or(req.Account, req.Username) if account == "" { response.BadRequest(c, "username or account is required") return } user, err := h.userService.Login(c.Request.Context(), account, req.Password) if err != nil { middleware.RecordLoginFailure(c.ClientIP()) response.HandleError(c, err, "failed to login") return } middleware.ResetLoginFailures(c.ClientIP()) // 生成Token accessToken, _ := h.jwtService.GenerateAccessToken(user.ID, user.Username) refreshToken, _ := h.jwtService.GenerateRefreshToken(user.ID, user.Username) ip := c.ClientIP() userAgent := c.GetHeader("User-Agent") // 记录用户活跃 if h.activityService != nil { go func() { ctx := context.Background() loginType := model.LoginTypeLogin h.activityService.RecordUserActive(ctx, user.ID, loginType, ip, userAgent) }() } // 更新登录日志(补充IP和UserAgent) if h.logService != nil { ip := c.ClientIP() userAgent := c.GetHeader("User-Agent") go func() { h.logService.LoginLog.RecordLogin(&model.LoginLog{ UserID: user.ID, UserName: user.Username, NickName: user.Nickname, LoginType: string(model.LoginTypePassword), Event: string(model.LoginEventLogin), IP: ip, UserAgent: userAgent, Result: string(model.LoginResultSuccess), }) }() } response.Success(c, gin.H{ "user": dto.ConvertUserToSelfResponse(user, int(user.PostsCount)), "token": accessToken, "refresh_token": refreshToken, }) } // SendRegisterCode 发送注册验证码 func (h *UserHandler) SendRegisterCode(c *gin.Context) { type SendCodeRequest struct { Email string `json:"email" binding:"required,email"` } var req SendCodeRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } clientIP := c.ClientIP() if err := h.userService.SendRegisterCode(c.Request.Context(), req.Email, clientIP); err != nil { response.HandleError(c, err, "failed to send register verification code") return } response.Success(c, gin.H{"success": true}) } func (h *UserHandler) SendPasswordResetCode(c *gin.Context) { type SendCodeRequest struct { Email string `json:"email" binding:"required,email"` } var req SendCodeRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } clientIP := c.ClientIP() if err := h.userService.SendPasswordResetCode(c.Request.Context(), req.Email, clientIP); err != nil { response.HandleError(c, err, "failed to send reset verification code") return } response.Success(c, gin.H{"success": true}) } // ResetPassword 找回密码并重置 func (h *UserHandler) ResetPassword(c *gin.Context) { type ResetPasswordRequest struct { Email string `json:"email" binding:"required,email"` VerificationCode string `json:"verification_code" binding:"required"` NewPassword string `json:"new_password" binding:"required,min=6"` } var req ResetPasswordRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } if err := h.userService.ResetPasswordByEmail(c.Request.Context(), req.Email, req.VerificationCode, req.NewPassword); err != nil { response.HandleError(c, err, "failed to reset password") return } response.Success(c, gin.H{"success": true}) } // GetCurrentUser 获取当前用户 func (h *UserHandler) GetCurrentUser(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { response.Unauthorized(c, "") return } user, err := h.userService.GetUserByID(c.Request.Context(), userID) if err != nil { response.NotFound(c, "user not found") return } postsCount, err := h.userService.GetUserPostCount(c.Request.Context(), userID) if err != nil { postsCount = int64(user.PostsCount) } response.Success(c, dto.ConvertUserToSelfResponse(user, int(postsCount))) } // GetUserByID 获取指定用户 func (h *UserHandler) GetUserByID(c *gin.Context) { id := c.Param("id") currentUserID := c.GetString("user_id") // 获取用户信息,包含双向关注状态 user, isFollowing, isFollowingMe, err := h.userService.GetUserByIDWithMutualFollowStatus(c.Request.Context(), id, currentUserID) if err != nil { response.NotFound(c, "user not found") return } // 实时计算帖子数量 postsCount, err := h.userService.GetUserPostCount(c.Request.Context(), id) if err != nil { // 如果获取失败,使用数据库中的值 postsCount = int64(user.PostsCount) } // 转换为响应格式,包含双向关注状态和实时计算的帖子数量 userResponse := dto.ConvertUserToResponse(user, dto.WithFollowing(isFollowing, isFollowingMe), dto.WithPostsCount(int(postsCount))) response.Success(c, userResponse) } // UpdateUser 更新用户 func (h *UserHandler) UpdateUser(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { response.Unauthorized(c, "") return } type UpdateRequest struct { Nickname string `json:"nickname"` Bio string `json:"bio"` Website string `json:"website"` Location string `json:"location"` Avatar string `json:"avatar"` CoverURL string `json:"cover_url"` Phone *string `json:"phone"` Email *string `json:"email"` } var req UpdateRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } user, err := h.userService.GetUserByID(c.Request.Context(), userID) if err != nil { response.NotFound(c, "user not found") return } if req.Nickname != "" { user.Nickname = req.Nickname } if req.Website != "" { user.Website = req.Website } if req.Location != "" { user.Location = req.Location } if req.Phone != nil { user.Phone = req.Phone } if req.Email != nil { if user.Email == nil || *user.Email != *req.Email { user.EmailVerified = false } user.Email = req.Email } avatarNeedsAudit := req.Avatar != "" && req.Avatar != user.Avatar && h.profileAuditService != nil coverNeedsAudit := req.CoverURL != "" && req.CoverURL != user.CoverURL && h.profileAuditService != nil bioNeedsAudit := req.Bio != "" && req.Bio != user.Bio && h.profileAuditService != nil if avatarNeedsAudit { if _, err := h.profileAuditService.SubmitAvatarAudit(c.Request.Context(), userID, req.Avatar); err != nil { avatarNeedsAudit = false user.Avatar = req.Avatar } } else if req.Avatar != "" { user.Avatar = req.Avatar } if coverNeedsAudit { if _, err := h.profileAuditService.SubmitCoverAudit(c.Request.Context(), userID, req.CoverURL); err != nil { coverNeedsAudit = false user.CoverURL = req.CoverURL } } else if req.CoverURL != "" { user.CoverURL = req.CoverURL } if bioNeedsAudit { if _, err := h.profileAuditService.SubmitBioAudit(c.Request.Context(), userID, req.Bio); err != nil { bioNeedsAudit = false user.Bio = req.Bio } } else if req.Bio != "" { user.Bio = req.Bio } err = h.userService.UpdateUser(c.Request.Context(), user) if err != nil { response.InternalServerError(c, "failed to update user") return } postsCount, err := h.userService.GetUserPostCount(c.Request.Context(), userID) if err != nil { postsCount = int64(user.PostsCount) } resp := gin.H{"user": dto.ConvertUserToDetailResponseWithPostsCount(user, int(postsCount))} if avatarNeedsAudit || coverNeedsAudit || bioNeedsAudit { resp["audit_pending"] = true } response.Success(c, resp) } // SendEmailVerifyCode 发送当前用户邮箱验证码 func (h *UserHandler) SendEmailVerifyCode(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { response.Unauthorized(c, "") return } type SendCodeRequest struct { Email string `json:"email" binding:"required,email"` } var req SendCodeRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } if err := h.userService.SendCurrentUserEmailVerifyCode(c.Request.Context(), userID, req.Email, c.ClientIP()); err != nil { response.HandleError(c, err, "failed to send email verify code") return } response.Success(c, gin.H{"success": true}) } // VerifyEmail 验证当前用户邮箱 func (h *UserHandler) VerifyEmail(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { response.Unauthorized(c, "") return } type VerifyEmailRequest struct { Email string `json:"email" binding:"required,email"` VerificationCode string `json:"verification_code" binding:"required"` } var req VerifyEmailRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } if err := h.userService.VerifyCurrentUserEmail(c.Request.Context(), userID, req.Email, req.VerificationCode); err != nil { response.HandleError(c, err, "failed to verify email") return } response.Success(c, gin.H{"success": true}) } // RefreshToken 刷新Token func (h *UserHandler) RefreshToken(c *gin.Context) { type RefreshRequest struct { RefreshToken string `json:"refresh_token" binding:"required"` } var req RefreshRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } // 解析 refresh token claims, err := h.jwtService.ParseToken(req.RefreshToken) if err != nil { response.Unauthorized(c, "invalid refresh token") return } // 获取用户信息 user, err := h.userService.GetUserByID(c.Request.Context(), claims.UserID) if err != nil { response.InternalServerError(c, "user not found") return } // 生成新 token accessToken, _ := h.jwtService.GenerateAccessToken(user.ID, user.Username) refreshToken, _ := h.jwtService.GenerateRefreshToken(user.ID, user.Username) ip := c.ClientIP() userAgent := c.GetHeader("User-Agent") // 记录用户活跃 if h.activityService != nil { go func() { ctx := context.Background() loginType := model.LoginTypeRefresh h.activityService.RecordUserActive(ctx, user.ID, loginType, ip, userAgent) }() } // 记录Token刷新日志 if h.logService != nil { go func() { h.logService.LoginLog.RecordLogin(&model.LoginLog{ UserID: user.ID, UserName: user.Username, NickName: user.Nickname, LoginType: string(model.LoginTypePassword), Event: string(model.LoginEventRefresh), IP: ip, UserAgent: userAgent, Result: string(model.LoginResultSuccess), TokenID: generateTokenID(req.RefreshToken), }) }() } response.Success(c, gin.H{ "token": accessToken, "refresh_token": refreshToken, }) } // FollowUser 关注用户 func (h *UserHandler) FollowUser(c *gin.Context) { userID := c.Param("id") currentUserID := c.GetString("user_id") if userID == currentUserID { response.BadRequest(c, "cannot follow yourself") return } err := h.userService.FollowUser(c.Request.Context(), currentUserID, userID) if err != nil { response.InternalServerError(c, "failed to follow user") return } response.Success(c, gin.H{"success": true}) } // UnfollowUser 取消关注用户 func (h *UserHandler) UnfollowUser(c *gin.Context) { userID := c.Param("id") currentUserID := c.GetString("user_id") err := h.userService.UnfollowUser(c.Request.Context(), currentUserID, userID) if err != nil { response.InternalServerError(c, "failed to unfollow user") return } response.Success(c, gin.H{"success": true}) } // BlockUser 拉黑用户 func (h *UserHandler) BlockUser(c *gin.Context) { targetUserID := c.Param("id") currentUserID := c.GetString("user_id") if targetUserID == currentUserID { response.BadRequest(c, "cannot block yourself") return } err := h.userService.BlockUser(c.Request.Context(), currentUserID, targetUserID) if err != nil { response.HandleError(c, err, "failed to block user") return } response.Success(c, gin.H{"success": true}) } // UnblockUser 取消拉黑 func (h *UserHandler) UnblockUser(c *gin.Context) { targetUserID := c.Param("id") currentUserID := c.GetString("user_id") if targetUserID == currentUserID { response.BadRequest(c, "cannot unblock yourself") return } err := h.userService.UnblockUser(c.Request.Context(), currentUserID, targetUserID) if err != nil { response.HandleError(c, err, "failed to unblock user") return } response.Success(c, gin.H{"success": true}) } // GetBlockedUsers 获取黑名单列表 func (h *UserHandler) GetBlockedUsers(c *gin.Context) { currentUserID := c.GetString("user_id") if currentUserID == "" { response.Unauthorized(c, "") return } page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) page, pageSize = normalizePagination(page, pageSize) users, total, err := h.userService.GetBlockedUsers(c.Request.Context(), currentUserID, page, pageSize) if err != nil { response.InternalServerError(c, "failed to get blocked users") return } userIDs := make([]string, len(users)) for i, u := range users { userIDs[i] = u.ID } postsCountMap, _ := h.userService.GetUserPostCountBatch(c.Request.Context(), userIDs) userResponses := dto.ConvertUsersToResponseWithMutualFollowAndPostsCount(users, nil, postsCountMap) response.Paginated(c, userResponses, total, page, pageSize) } // GetBlockStatus 获取拉黑状态 func (h *UserHandler) GetBlockStatus(c *gin.Context) { targetUserID := c.Param("id") currentUserID := c.GetString("user_id") if currentUserID == "" { response.Unauthorized(c, "") return } if targetUserID == "" { response.BadRequest(c, "target user id is required") return } isBlocked, err := h.userService.IsBlocked(c.Request.Context(), currentUserID, targetUserID) if err != nil { response.InternalServerError(c, "failed to get block status") return } response.Success(c, gin.H{"is_blocked": isBlocked}) } // GetFollowingList 获取关注列表 func (h *UserHandler) GetFollowingList(c *gin.Context) { userID := c.Param("id") currentUserID := c.GetString("user_id") canView, err := h.userService.CanViewFollowing(c.Request.Context(), currentUserID, userID) if err != nil { response.InternalServerError(c, "failed to check privacy settings") return } if !canView { response.Forbidden(c, "该用户的关注列表不对外公开") return } page := c.DefaultQuery("page", "1") pageSize := c.DefaultQuery("page_size", "20") keyword := c.Query("keyword") users, err := h.userService.GetFollowingList(c.Request.Context(), userID, page, pageSize, keyword) if err != nil { response.InternalServerError(c, "failed to get following list") return } var userResponses []*dto.UserResponse if len(users) > 0 { userIDs := make([]string, len(users)) for i, u := range users { userIDs[i] = u.ID } postsCountMap, _ := h.userService.GetUserPostCountBatch(c.Request.Context(), userIDs) if currentUserID != "" && userID == currentUserID { statusMap, _ := h.userService.GetMutualFollowStatus(c.Request.Context(), currentUserID, userIDs) userResponses = dto.ConvertUsersToResponseWithMutualFollowAndPostsCount(users, statusMap, postsCountMap) } else { userResponses = dto.ConvertUsersToResponseWithMutualFollowAndPostsCount(users, nil, postsCountMap) } } else { userResponses = dto.ConvertUsersToResponse(users) } response.Success(c, gin.H{ "list": userResponses, }) } // GetFollowersList 获取粉丝列表 func (h *UserHandler) GetFollowersList(c *gin.Context) { userID := c.Param("id") currentUserID := c.GetString("user_id") canView, err := h.userService.CanViewFollowers(c.Request.Context(), currentUserID, userID) if err != nil { response.InternalServerError(c, "failed to check privacy settings") return } if !canView { response.Forbidden(c, "该用户的粉丝列表不对外公开") return } page := c.DefaultQuery("page", "1") pageSize := c.DefaultQuery("page_size", "20") keyword := c.Query("keyword") users, err := h.userService.GetFollowersList(c.Request.Context(), userID, page, pageSize, keyword) if err != nil { response.InternalServerError(c, "failed to get followers list") return } var userResponses []*dto.UserResponse if len(users) > 0 { userIDs := make([]string, len(users)) for i, u := range users { userIDs[i] = u.ID } postsCountMap, _ := h.userService.GetUserPostCountBatch(c.Request.Context(), userIDs) if currentUserID != "" && userID == currentUserID { statusMap, _ := h.userService.GetMutualFollowStatus(c.Request.Context(), currentUserID, userIDs) userResponses = dto.ConvertUsersToResponseWithMutualFollowAndPostsCount(users, statusMap, postsCountMap) } else { userResponses = dto.ConvertUsersToResponseWithMutualFollowAndPostsCount(users, nil, postsCountMap) } } else { userResponses = dto.ConvertUsersToResponse(users) } response.Success(c, gin.H{ "list": userResponses, }) } // CheckUsername 检查用户名是否可用 func (h *UserHandler) CheckUsername(c *gin.Context) { username := c.Query("username") if username == "" { response.BadRequest(c, "username is required") return } available, err := h.userService.CheckUsernameAvailable(c.Request.Context(), username) if err != nil { response.InternalServerError(c, "failed to check username") return } response.Success(c, gin.H{"available": available}) } // ChangePassword 修改密码 func (h *UserHandler) ChangePassword(c *gin.Context) { currentUserID := c.GetString("user_id") type ChangePasswordRequest struct { OldPassword string `json:"old_password" binding:"required"` NewPassword string `json:"new_password" binding:"required,min=6"` VerificationCode string `json:"verification_code" binding:"required"` } var req ChangePasswordRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } err := h.userService.ChangePassword(c.Request.Context(), currentUserID, req.OldPassword, req.NewPassword, req.VerificationCode) if err != nil { response.HandleError(c, err, "failed to change password") return } response.Success(c, gin.H{"success": true}) } // SendChangePasswordCode 发送修改密码验证码 func (h *UserHandler) SendChangePasswordCode(c *gin.Context) { currentUserID := c.GetString("user_id") if currentUserID == "" { response.Unauthorized(c, "") return } err := h.userService.SendChangePasswordCode(c.Request.Context(), currentUserID, c.ClientIP()) if err != nil { response.HandleError(c, err, "failed to send change password code") return } response.Success(c, gin.H{"success": true}) } // Logout 用户登出 func (h *UserHandler) Logout(c *gin.Context) { currentUserID := c.GetString("user_id") if currentUserID == "" { response.Unauthorized(c, "not logged in") return } ip := c.ClientIP() userAgent := c.GetHeader("User-Agent") // 获取用户信息 user, err := h.userService.GetUserByID(c.Request.Context(), currentUserID) if err != nil { response.InternalServerError(c, "user not found") return } // 记录登出日志 if h.logService != nil { h.logService.LoginLog.RecordLogin(&model.LoginLog{ UserID: user.ID, UserName: user.Username, NickName: user.Nickname, Event: string(model.LoginEventLogout), IP: ip, UserAgent: userAgent, Result: string(model.LoginResultSuccess), }) } response.Success(c, gin.H{"success": true}) } // Search 搜索用户 func (h *UserHandler) Search(c *gin.Context) { keyword := c.Query("keyword") page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) page, pageSize = normalizePagination(page, pageSize) users, total, err := h.userService.Search(c.Request.Context(), keyword, page, pageSize) if err != nil { response.InternalServerError(c, "failed to search users") return } // 获取实时计算的帖子数量 var userResponses []*dto.UserResponse if len(users) > 0 { userIDs := make([]string, len(users)) for i, u := range users { userIDs[i] = u.ID } postsCountMap, _ := h.userService.GetUserPostCountBatch(c.Request.Context(), userIDs) userResponses = dto.ConvertUsersToResponseWithMutualFollowAndPostsCount(users, nil, postsCountMap) } else { userResponses = dto.ConvertUsersToResponse(users) } response.Paginated(c, userResponses, total, page, pageSize) } // GetPrivacySettings 获取隐私设置 func (h *UserHandler) GetPrivacySettings(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { response.Unauthorized(c, "") return } settings, err := h.userService.GetPrivacySettings(c.Request.Context(), userID) if err != nil { response.InternalServerError(c, "failed to get privacy settings") return } response.Success(c, &dto.PrivacySettingsResponse{ FollowersVisibility: string(settings.FollowersVisibility), FollowingVisibility: string(settings.FollowingVisibility), PostsVisibility: string(settings.PostsVisibility), FavoritesVisibility: string(settings.FavoritesVisibility), }) } // UpdatePrivacySettings 更新隐私设置 func (h *UserHandler) UpdatePrivacySettings(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { response.Unauthorized(c, "") return } var req dto.PrivacySettingsRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } settings := &model.PrivacySettings{} if req.FollowersVisibility != nil { settings.FollowersVisibility = model.VisibilityLevel(*req.FollowersVisibility) } else { settings.FollowersVisibility = model.VisibilityEveryone } if req.FollowingVisibility != nil { settings.FollowingVisibility = model.VisibilityLevel(*req.FollowingVisibility) } else { settings.FollowingVisibility = model.VisibilityEveryone } if req.PostsVisibility != nil { settings.PostsVisibility = model.VisibilityLevel(*req.PostsVisibility) } else { settings.PostsVisibility = model.VisibilityEveryone } if req.FavoritesVisibility != nil { settings.FavoritesVisibility = model.VisibilityLevel(*req.FavoritesVisibility) } else { settings.FavoritesVisibility = model.VisibilityEveryone } if err := h.userService.UpdatePrivacySettings(c.Request.Context(), userID, settings); err != nil { response.InternalServerError(c, "failed to update privacy settings") return } response.Success(c, &dto.PrivacySettingsResponse{ FollowersVisibility: string(settings.FollowersVisibility), FollowingVisibility: string(settings.FollowingVisibility), PostsVisibility: string(settings.PostsVisibility), FavoritesVisibility: string(settings.FavoritesVisibility), }) } // RequestAccountDeletion 申请注销账号 func (h *UserHandler) RequestAccountDeletion(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { response.Unauthorized(c, "") return } var req dto.RequestDeletionRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } if err := h.userService.RequestAccountDeletion(c.Request.Context(), userID, req.Password); err != nil { response.HandleError(c, err, "failed to request account deletion") return } isPending, requestedAt, daysLeft, err := h.userService.GetDeletionStatus(c.Request.Context(), userID) if err != nil { response.InternalServerError(c, "failed to get deletion status") return } resp := &dto.DeletionStatusResponse{ IsPendingDeletion: isPending, CoolDownDays: daysLeft, } if requestedAt != nil { resp.DeletionRequestedAt = dto.FormatTime(*requestedAt) } response.Success(c, resp) } // CancelAccountDeletion 取消注销 func (h *UserHandler) CancelAccountDeletion(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { response.Unauthorized(c, "") return } if err := h.userService.CancelAccountDeletion(c.Request.Context(), userID); err != nil { response.InternalServerError(c, "failed to cancel account deletion") return } response.Success(c, gin.H{"success": true}) } // GetDeletionStatus 获取注销状态 func (h *UserHandler) GetDeletionStatus(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { response.Unauthorized(c, "") return } isPending, requestedAt, daysLeft, err := h.userService.GetDeletionStatus(c.Request.Context(), userID) if err != nil { response.InternalServerError(c, "failed to get deletion status") return } resp := &dto.DeletionStatusResponse{ IsPendingDeletion: isPending, CoolDownDays: daysLeft, } if requestedAt != nil { resp.DeletionRequestedAt = dto.FormatTime(*requestedAt) } response.Success(c, resp) }