package handler import ( "carrotskin/internal/model" "carrotskin/internal/service" "carrotskin/internal/types" "carrotskin/pkg/config" "carrotskin/pkg/logger" "carrotskin/pkg/redis" "carrotskin/pkg/storage" "net/http" "github.com/gin-gonic/gin" "go.uber.org/zap" ) // GetUserProfile 获取用户信息 // @Summary 获取用户信息 // @Description 获取当前登录用户的详细信息 // @Tags user // @Accept json // @Produce json // @Security BearerAuth // @Success 200 {object} model.Response "获取成功" // @Failure 401 {object} model.ErrorResponse "未授权" // @Router /api/v1/user/profile [get] func GetUserProfile(c *gin.Context) { loggerInstance := logger.MustGetLogger() // 从上下文获取用户ID (由JWT中间件设置) userID, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, model.NewErrorResponse( model.CodeUnauthorized, model.MsgUnauthorized, nil, )) return } // 获取用户信息 user, err := service.GetUserByID(userID.(int64)) if err != nil || user == nil { loggerInstance.Error("获取用户信息失败", zap.Int64("user_id", userID.(int64)), zap.Error(err), ) c.JSON(http.StatusNotFound, model.NewErrorResponse( model.CodeNotFound, "用户不存在", err, )) return } // 返回用户信息 c.JSON(http.StatusOK, model.NewSuccessResponse(&types.UserInfo{ ID: user.ID, Username: user.Username, Email: user.Email, Avatar: user.Avatar, Points: user.Points, Role: user.Role, Status: user.Status, LastLoginAt: user.LastLoginAt, CreatedAt: user.CreatedAt, UpdatedAt: user.UpdatedAt, })) } // UpdateUserProfile 更新用户信息 // @Summary 更新用户信息 // @Description 更新当前登录用户的头像和密码(修改邮箱请使用 /change-email 接口) // @Tags user // @Accept json // @Produce json // @Security BearerAuth // @Param request body types.UpdateUserRequest true "更新信息(修改密码时需同时提供old_password和new_password)" // @Success 200 {object} model.Response{data=types.UserInfo} "更新成功" // @Failure 400 {object} model.ErrorResponse "请求参数错误" // @Failure 401 {object} model.ErrorResponse "未授权" // @Failure 404 {object} model.ErrorResponse "用户不存在" // @Failure 500 {object} model.ErrorResponse "服务器错误" // @Router /api/v1/user/profile [put] func UpdateUserProfile(c *gin.Context) { loggerInstance := logger.MustGetLogger() userID, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, model.NewErrorResponse( model.CodeUnauthorized, model.MsgUnauthorized, nil, )) return } var req types.UpdateUserRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, model.NewErrorResponse( model.CodeBadRequest, "请求参数错误", err, )) return } // 获取用户 user, err := service.GetUserByID(userID.(int64)) if err != nil || user == nil { c.JSON(http.StatusNotFound, model.NewErrorResponse( model.CodeNotFound, "用户不存在", err, )) return } // 处理密码修改 if req.NewPassword != "" { // 如果提供了新密码,必须同时提供旧密码 if req.OldPassword == "" { c.JSON(http.StatusBadRequest, model.NewErrorResponse( model.CodeBadRequest, "修改密码需要提供原密码", nil, )) return } // 调用修改密码服务 if err := service.ChangeUserPassword(userID.(int64), req.OldPassword, req.NewPassword); err != nil { loggerInstance.Error("修改密码失败", zap.Int64("user_id", userID.(int64)), zap.Error(err), ) c.JSON(http.StatusBadRequest, model.NewErrorResponse( model.CodeBadRequest, err.Error(), nil, )) return } loggerInstance.Info("用户修改密码成功", zap.Int64("user_id", userID.(int64)), ) } // 更新头像 if req.Avatar != "" { user.Avatar = req.Avatar } // 保存更新(仅当有头像修改时) if req.Avatar != "" { if err := service.UpdateUserInfo(user); err != nil { loggerInstance.Error("更新用户信息失败", zap.Int64("user_id", user.ID), zap.Error(err), ) c.JSON(http.StatusInternalServerError, model.NewErrorResponse( model.CodeServerError, "更新失败", err, )) return } } // 重新获取更新后的用户信息 updatedUser, err := service.GetUserByID(userID.(int64)) if err != nil || updatedUser == nil { c.JSON(http.StatusNotFound, model.NewErrorResponse( model.CodeNotFound, "用户不存在", err, )) return } // 返回更新后的用户信息 c.JSON(http.StatusOK, model.NewSuccessResponse(&types.UserInfo{ ID: updatedUser.ID, Username: updatedUser.Username, Email: updatedUser.Email, Avatar: updatedUser.Avatar, Points: updatedUser.Points, Role: updatedUser.Role, Status: updatedUser.Status, LastLoginAt: updatedUser.LastLoginAt, CreatedAt: updatedUser.CreatedAt, UpdatedAt: updatedUser.UpdatedAt, })) } // GenerateAvatarUploadURL 生成头像上传URL // @Summary 生成头像上传URL // @Description 生成预签名URL用于上传用户头像 // @Tags user // @Accept json // @Produce json // @Security BearerAuth // @Param request body types.GenerateAvatarUploadURLRequest true "文件名" // @Success 200 {object} model.Response "生成成功" // @Failure 400 {object} model.ErrorResponse "请求参数错误" // @Router /api/v1/user/avatar/upload-url [post] func GenerateAvatarUploadURL(c *gin.Context) { loggerInstance := logger.MustGetLogger() userID, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, model.NewErrorResponse( model.CodeUnauthorized, model.MsgUnauthorized, nil, )) return } var req types.GenerateAvatarUploadURLRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, model.NewErrorResponse( model.CodeBadRequest, "请求参数错误", err, )) return } // 调用UploadService生成预签名URL storageClient := storage.MustGetClient() cfg := *config.MustGetRustFSConfig() result, err := service.GenerateAvatarUploadURL(c.Request.Context(), storageClient, cfg, userID.(int64), req.FileName) if err != nil { loggerInstance.Error("生成头像上传URL失败", zap.Int64("user_id", userID.(int64)), zap.String("file_name", req.FileName), zap.Error(err), ) c.JSON(http.StatusBadRequest, model.NewErrorResponse( model.CodeBadRequest, err.Error(), nil, )) return } // 返回响应 c.JSON(http.StatusOK, model.NewSuccessResponse(&types.GenerateAvatarUploadURLResponse{ PostURL: result.PostURL, FormData: result.FormData, AvatarURL: result.FileURL, ExpiresIn: 900, // 15分钟 = 900秒 })) } // UpdateAvatar 更新头像URL // @Summary 更新头像URL // @Description 上传完成后更新用户的头像URL到数据库 // @Tags user // @Accept json // @Produce json // @Security BearerAuth // @Param avatar_url query string true "头像URL" // @Success 200 {object} model.Response "更新成功" // @Failure 400 {object} model.ErrorResponse "请求参数错误" // @Router /api/v1/user/avatar [put] func UpdateAvatar(c *gin.Context) { loggerInstance := logger.MustGetLogger() userID, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, model.NewErrorResponse( model.CodeUnauthorized, model.MsgUnauthorized, nil, )) return } avatarURL := c.Query("avatar_url") if avatarURL == "" { c.JSON(http.StatusBadRequest, model.NewErrorResponse( model.CodeBadRequest, "头像URL不能为空", nil, )) return } // 更新头像 if err := service.UpdateUserAvatar(userID.(int64), avatarURL); err != nil { loggerInstance.Error("更新头像失败", zap.Int64("user_id", userID.(int64)), zap.String("avatar_url", avatarURL), zap.Error(err), ) c.JSON(http.StatusInternalServerError, model.NewErrorResponse( model.CodeServerError, "更新头像失败", err, )) return } // 获取更新后的用户信息 user, err := service.GetUserByID(userID.(int64)) if err != nil || user == nil { c.JSON(http.StatusNotFound, model.NewErrorResponse( model.CodeNotFound, "用户不存在", err, )) return } // 返回更新后的用户信息 c.JSON(http.StatusOK, model.NewSuccessResponse(&types.UserInfo{ ID: user.ID, Username: user.Username, Email: user.Email, Avatar: user.Avatar, Points: user.Points, Role: user.Role, Status: user.Status, LastLoginAt: user.LastLoginAt, CreatedAt: user.CreatedAt, })) } // ChangeEmail 更换邮箱 // @Summary 更换邮箱 // @Description 通过验证码更换用户邮箱 // @Tags user // @Accept json // @Produce json // @Security BearerAuth // @Param request body types.ChangeEmailRequest true "更换邮箱请求" // @Success 200 {object} model.Response{data=types.UserInfo} "更换成功" // @Failure 400 {object} model.ErrorResponse "请求参数错误" // @Failure 401 {object} model.ErrorResponse "未授权" // @Router /api/v1/user/change-email [post] func ChangeEmail(c *gin.Context) { loggerInstance := logger.MustGetLogger() userID, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, model.NewErrorResponse( model.CodeUnauthorized, model.MsgUnauthorized, nil, )) return } var req types.ChangeEmailRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, model.NewErrorResponse( model.CodeBadRequest, "请求参数错误", err, )) return } // 验证验证码 redisClient := redis.MustGetClient() if err := service.VerifyCode(c.Request.Context(), redisClient, req.NewEmail, req.VerificationCode, service.VerificationTypeChangeEmail); err != nil { loggerInstance.Warn("验证码验证失败", zap.String("new_email", req.NewEmail), zap.Error(err), ) c.JSON(http.StatusBadRequest, model.NewErrorResponse( model.CodeBadRequest, err.Error(), nil, )) return } // 更换邮箱 if err := service.ChangeUserEmail(userID.(int64), req.NewEmail); err != nil { loggerInstance.Error("更换邮箱失败", zap.Int64("user_id", userID.(int64)), zap.String("new_email", req.NewEmail), zap.Error(err), ) c.JSON(http.StatusBadRequest, model.NewErrorResponse( model.CodeBadRequest, err.Error(), nil, )) return } // 获取更新后的用户信息 user, err := service.GetUserByID(userID.(int64)) if err != nil || user == nil { c.JSON(http.StatusNotFound, model.NewErrorResponse( model.CodeNotFound, "用户不存在", err, )) return } c.JSON(http.StatusOK, model.NewSuccessResponse(&types.UserInfo{ ID: user.ID, Username: user.Username, Email: user.Email, Avatar: user.Avatar, Points: user.Points, Role: user.Role, Status: user.Status, LastLoginAt: user.LastLoginAt, CreatedAt: user.CreatedAt, UpdatedAt: user.UpdatedAt, })) }