700 lines
19 KiB
Go
700 lines
19 KiB
Go
|
|
package handler
|
||
|
|
|
||
|
|
import (
|
||
|
|
"strconv"
|
||
|
|
|
||
|
|
"github.com/gin-gonic/gin"
|
||
|
|
|
||
|
|
"carrot_bbs/internal/dto"
|
||
|
|
"carrot_bbs/internal/pkg/response"
|
||
|
|
"carrot_bbs/internal/service"
|
||
|
|
)
|
||
|
|
|
||
|
|
// UserHandler 用户处理器
|
||
|
|
type UserHandler struct {
|
||
|
|
userService *service.UserService
|
||
|
|
jwtService *service.JWTService
|
||
|
|
}
|
||
|
|
|
||
|
|
// NewUserHandler 创建用户处理器
|
||
|
|
func NewUserHandler(userService *service.UserService) *UserHandler {
|
||
|
|
return &UserHandler{
|
||
|
|
userService: userService,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 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 {
|
||
|
|
if se, ok := err.(*service.ServiceError); ok {
|
||
|
|
response.Error(c, se.Code, se.Message)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
response.InternalServerError(c, "failed to register")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
// 生成Token
|
||
|
|
accessToken, _ := h.jwtService.GenerateAccessToken(user.ID, user.Username)
|
||
|
|
refreshToken, _ := h.jwtService.GenerateRefreshToken(user.ID, user.Username)
|
||
|
|
|
||
|
|
response.Success(c, gin.H{
|
||
|
|
"user": dto.ConvertUserToResponse(user),
|
||
|
|
"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 := req.Account
|
||
|
|
if account == "" {
|
||
|
|
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 {
|
||
|
|
if se, ok := err.(*service.ServiceError); ok {
|
||
|
|
response.Error(c, se.Code, se.Message)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
response.InternalServerError(c, "failed to login")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
// 生成Token
|
||
|
|
accessToken, _ := h.jwtService.GenerateAccessToken(user.ID, user.Username)
|
||
|
|
refreshToken, _ := h.jwtService.GenerateRefreshToken(user.ID, user.Username)
|
||
|
|
|
||
|
|
response.Success(c, gin.H{
|
||
|
|
"user": dto.ConvertUserToResponse(user),
|
||
|
|
"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
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := h.userService.SendRegisterCode(c.Request.Context(), req.Email); err != nil {
|
||
|
|
if se, ok := err.(*service.ServiceError); ok {
|
||
|
|
response.Error(c, se.Code, se.Message)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
response.InternalServerError(c, "failed to send register verification code")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
response.Success(c, gin.H{"success": true})
|
||
|
|
}
|
||
|
|
|
||
|
|
// SendPasswordResetCode 发送找回密码验证码
|
||
|
|
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
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := h.userService.SendPasswordResetCode(c.Request.Context(), req.Email); err != nil {
|
||
|
|
if se, ok := err.(*service.ServiceError); ok {
|
||
|
|
response.Error(c, se.Code, se.Message)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
response.InternalServerError(c, "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 {
|
||
|
|
if se, ok := err.(*service.ServiceError); ok {
|
||
|
|
response.Error(c, se.Code, se.Message)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
response.InternalServerError(c, "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.ConvertUserToDetailResponseWithPostsCount(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.ConvertUserToResponseWithMutualFollowAndPostsCount(user, isFollowing, isFollowingMe, 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"`
|
||
|
|
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.Bio != "" {
|
||
|
|
user.Bio = req.Bio
|
||
|
|
}
|
||
|
|
if req.Website != "" {
|
||
|
|
user.Website = req.Website
|
||
|
|
}
|
||
|
|
if req.Location != "" {
|
||
|
|
user.Location = req.Location
|
||
|
|
}
|
||
|
|
if req.Avatar != "" {
|
||
|
|
user.Avatar = req.Avatar
|
||
|
|
}
|
||
|
|
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
|
||
|
|
}
|
||
|
|
|
||
|
|
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)
|
||
|
|
}
|
||
|
|
|
||
|
|
response.Success(c, dto.ConvertUserToDetailResponseWithPostsCount(user, int(postsCount)))
|
||
|
|
}
|
||
|
|
|
||
|
|
// 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); err != nil {
|
||
|
|
if se, ok := err.(*service.ServiceError); ok {
|
||
|
|
response.Error(c, se.Code, se.Message)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
response.InternalServerError(c, "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 {
|
||
|
|
if se, ok := err.(*service.ServiceError); ok {
|
||
|
|
response.Error(c, se.Code, se.Message)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
response.InternalServerError(c, "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
|
||
|
|
}
|
||
|
|
|
||
|
|
// 生成新 token
|
||
|
|
accessToken, _ := h.jwtService.GenerateAccessToken(claims.UserID, claims.Username)
|
||
|
|
refreshToken, _ := h.jwtService.GenerateRefreshToken(claims.UserID, claims.Username)
|
||
|
|
|
||
|
|
response.Success(c, gin.H{
|
||
|
|
"token": accessToken,
|
||
|
|
"refresh_token": refreshToken,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
// SetJWTService 设置JWT服务
|
||
|
|
func (h *UserHandler) SetJWTService(jwtSvc *service.JWTService) {
|
||
|
|
h.jwtService = jwtSvc
|
||
|
|
}
|
||
|
|
|
||
|
|
// 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 {
|
||
|
|
if se, ok := err.(*service.ServiceError); ok {
|
||
|
|
response.Error(c, se.Code, se.Message)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
response.InternalServerError(c, "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 {
|
||
|
|
if se, ok := err.(*service.ServiceError); ok {
|
||
|
|
response.Error(c, se.Code, se.Message)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
response.InternalServerError(c, "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"))
|
||
|
|
if page <= 0 {
|
||
|
|
page = 1
|
||
|
|
}
|
||
|
|
if pageSize <= 0 {
|
||
|
|
pageSize = 20
|
||
|
|
}
|
||
|
|
|
||
|
|
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")
|
||
|
|
page := c.DefaultQuery("page", "1")
|
||
|
|
pageSize := c.DefaultQuery("page_size", "20")
|
||
|
|
|
||
|
|
users, err := h.userService.GetFollowingList(c.Request.Context(), userID, page, pageSize)
|
||
|
|
if err != nil {
|
||
|
|
response.InternalServerError(c, "failed to get following list")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
// 如果已登录,获取双向关注状态和实时计算的帖子数量
|
||
|
|
var userResponses []*dto.UserResponse
|
||
|
|
if currentUserID != "" && len(users) > 0 {
|
||
|
|
userIDs := make([]string, len(users))
|
||
|
|
for i, u := range users {
|
||
|
|
userIDs[i] = u.ID
|
||
|
|
}
|
||
|
|
statusMap, _ := h.userService.GetMutualFollowStatus(c.Request.Context(), currentUserID, userIDs)
|
||
|
|
postsCountMap, _ := h.userService.GetUserPostCountBatch(c.Request.Context(), userIDs)
|
||
|
|
userResponses = dto.ConvertUsersToResponseWithMutualFollowAndPostsCount(users, statusMap, postsCountMap)
|
||
|
|
} else 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.Success(c, gin.H{
|
||
|
|
"list": userResponses,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetFollowersList 获取粉丝列表
|
||
|
|
func (h *UserHandler) GetFollowersList(c *gin.Context) {
|
||
|
|
userID := c.Param("id")
|
||
|
|
currentUserID := c.GetString("user_id")
|
||
|
|
page := c.DefaultQuery("page", "1")
|
||
|
|
pageSize := c.DefaultQuery("page_size", "20")
|
||
|
|
|
||
|
|
users, err := h.userService.GetFollowersList(c.Request.Context(), userID, page, pageSize)
|
||
|
|
if err != nil {
|
||
|
|
response.InternalServerError(c, "failed to get followers list")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
// 如果已登录,获取双向关注状态和实时计算的帖子数量
|
||
|
|
var userResponses []*dto.UserResponse
|
||
|
|
if currentUserID != "" && len(users) > 0 {
|
||
|
|
userIDs := make([]string, len(users))
|
||
|
|
for i, u := range users {
|
||
|
|
userIDs[i] = u.ID
|
||
|
|
}
|
||
|
|
statusMap, _ := h.userService.GetMutualFollowStatus(c.Request.Context(), currentUserID, userIDs)
|
||
|
|
postsCountMap, _ := h.userService.GetUserPostCountBatch(c.Request.Context(), userIDs)
|
||
|
|
userResponses = dto.ConvertUsersToResponseWithMutualFollowAndPostsCount(users, statusMap, postsCountMap)
|
||
|
|
} else 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.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 {
|
||
|
|
if se, ok := err.(*service.ServiceError); ok {
|
||
|
|
response.Error(c, se.Code, se.Message)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
response.InternalServerError(c, "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)
|
||
|
|
if err != nil {
|
||
|
|
if se, ok := err.(*service.ServiceError); ok {
|
||
|
|
response.Error(c, se.Code, se.Message)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
response.InternalServerError(c, "failed to send change password code")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
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"))
|
||
|
|
|
||
|
|
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)
|
||
|
|
}
|