package handler import ( "carrotskin/internal/service" "carrotskin/internal/types" "carrotskin/pkg/config" "carrotskin/pkg/database" "carrotskin/pkg/logger" "carrotskin/pkg/redis" "carrotskin/pkg/storage" "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) { userID, ok := GetUserIDFromContext(c) if !ok { return } user, err := service.GetUserByID(userID) if err != nil || user == nil { logger.MustGetLogger().Error("获取用户信息失败", zap.Int64("user_id", userID), zap.Error(err), ) RespondNotFound(c, "用户不存在") return } RespondSuccess(c, UserToUserInfo(user)) } // 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, ok := GetUserIDFromContext(c) if !ok { return } var req types.UpdateUserRequest if err := c.ShouldBindJSON(&req); err != nil { RespondBadRequest(c, "请求参数错误", err) return } user, err := service.GetUserByID(userID) if err != nil || user == nil { RespondNotFound(c, "用户不存在") return } // 处理密码修改 if req.NewPassword != "" { if req.OldPassword == "" { RespondBadRequest(c, "修改密码需要提供原密码", nil) return } if err := service.ChangeUserPassword(userID, req.OldPassword, req.NewPassword); err != nil { loggerInstance.Error("修改密码失败", zap.Int64("user_id", userID), zap.Error(err)) RespondBadRequest(c, err.Error(), nil) return } loggerInstance.Info("用户修改密码成功", zap.Int64("user_id", userID)) } // 更新头像 if req.Avatar != "" { // 验证头像 URL 是否来自允许的域名 if err := service.ValidateAvatarURL(req.Avatar); err != nil { RespondBadRequest(c, err.Error(), nil) return } user.Avatar = req.Avatar if err := service.UpdateUserInfo(user); err != nil { loggerInstance.Error("更新用户信息失败", zap.Int64("user_id", user.ID), zap.Error(err)) RespondServerError(c, "更新失败", err) return } } // 重新获取更新后的用户信息 updatedUser, err := service.GetUserByID(userID) if err != nil || updatedUser == nil { RespondNotFound(c, "用户不存在") return } RespondSuccess(c, UserToUserInfo(updatedUser)) } // 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) { userID, ok := GetUserIDFromContext(c) if !ok { return } var req types.GenerateAvatarUploadURLRequest if err := c.ShouldBindJSON(&req); err != nil { RespondBadRequest(c, "请求参数错误", err) return } storageClient := storage.MustGetClient() cfg := *config.MustGetRustFSConfig() result, err := service.GenerateAvatarUploadURL(c.Request.Context(), storageClient, cfg, userID, req.FileName) if err != nil { logger.MustGetLogger().Error("生成头像上传URL失败", zap.Int64("user_id", userID), zap.String("file_name", req.FileName), zap.Error(err), ) RespondBadRequest(c, err.Error(), nil) return } RespondSuccess(c, &types.GenerateAvatarUploadURLResponse{ PostURL: result.PostURL, FormData: result.FormData, AvatarURL: result.FileURL, ExpiresIn: 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) { userID, ok := GetUserIDFromContext(c) if !ok { return } avatarURL := c.Query("avatar_url") if avatarURL == "" { RespondBadRequest(c, "头像URL不能为空", nil) return } if err := service.ValidateAvatarURL(avatarURL); err != nil { RespondBadRequest(c, err.Error(), nil) return } if err := service.UpdateUserAvatar(userID, avatarURL); err != nil { logger.MustGetLogger().Error("更新头像失败", zap.Int64("user_id", userID), zap.String("avatar_url", avatarURL), zap.Error(err), ) RespondServerError(c, "更新头像失败", err) return } user, err := service.GetUserByID(userID) if err != nil || user == nil { RespondNotFound(c, "用户不存在") return } RespondSuccess(c, UserToUserInfo(user)) } // 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, ok := GetUserIDFromContext(c) if !ok { return } var req types.ChangeEmailRequest if err := c.ShouldBindJSON(&req); err != nil { RespondBadRequest(c, "请求参数错误", 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)) RespondBadRequest(c, err.Error(), nil) return } if err := service.ChangeUserEmail(userID, req.NewEmail); err != nil { loggerInstance.Error("更换邮箱失败", zap.Int64("user_id", userID), zap.String("new_email", req.NewEmail), zap.Error(err), ) RespondBadRequest(c, err.Error(), nil) return } user, err := service.GetUserByID(userID) if err != nil || user == nil { RespondNotFound(c, "用户不存在") return } RespondSuccess(c, UserToUserInfo(user)) } // ResetYggdrasilPassword 重置Yggdrasil密码 // @Summary 重置Yggdrasil密码 // @Description 重置当前用户的Yggdrasil密码并返回新密码 // @Tags user // @Accept json // @Produce json // @Security BearerAuth // @Success 200 {object} model.Response "重置成功" // @Failure 401 {object} model.ErrorResponse "未授权" // @Failure 500 {object} model.ErrorResponse "服务器错误" // @Router /api/v1/user/yggdrasil-password/reset [post] func ResetYggdrasilPassword(c *gin.Context) { loggerInstance := logger.MustGetLogger() userID, ok := GetUserIDFromContext(c) if !ok { return } db := database.MustGetDB() newPassword, err := service.ResetYggdrasilPassword(db, userID) if err != nil { loggerInstance.Error("重置Yggdrasil密码失败", zap.Error(err), zap.Int64("userId", userID)) RespondServerError(c, "重置Yggdrasil密码失败", nil) return } loggerInstance.Info("Yggdrasil密码重置成功", zap.Int64("userId", userID)) RespondSuccess(c, gin.H{"password": newPassword}) }