feat: 添加种子数据初始化功能,重构多个处理程序以简化错误响应和用户验证
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"carrotskin/internal/model"
|
||||
"carrotskin/internal/service"
|
||||
"carrotskin/internal/types"
|
||||
"carrotskin/pkg/config"
|
||||
@@ -9,7 +8,6 @@ import (
|
||||
"carrotskin/pkg/logger"
|
||||
"carrotskin/pkg/redis"
|
||||
"carrotskin/pkg/storage"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
@@ -26,46 +24,22 @@ import (
|
||||
// @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,
|
||||
))
|
||||
userID, ok := GetUserIDFromContext(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
user, err := service.GetUserByID(userID.(int64))
|
||||
user, err := service.GetUserByID(userID)
|
||||
if err != nil || user == nil {
|
||||
loggerInstance.Error("获取用户信息失败",
|
||||
zap.Int64("user_id", userID.(int64)),
|
||||
logger.MustGetLogger().Error("获取用户信息失败",
|
||||
zap.Int64("user_id", userID),
|
||||
zap.Error(err),
|
||||
)
|
||||
c.JSON(http.StatusNotFound, model.NewErrorResponse(
|
||||
model.CodeNotFound,
|
||||
"用户不存在",
|
||||
err,
|
||||
))
|
||||
RespondNotFound(c, "用户不存在")
|
||||
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,
|
||||
}))
|
||||
RespondSuccess(c, UserToUserInfo(user))
|
||||
}
|
||||
|
||||
// UpdateUserProfile 更新用户信息
|
||||
@@ -84,113 +58,62 @@ func GetUserProfile(c *gin.Context) {
|
||||
// @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,
|
||||
))
|
||||
userID, ok := GetUserIDFromContext(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var req types.UpdateUserRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.NewErrorResponse(
|
||||
model.CodeBadRequest,
|
||||
"请求参数错误",
|
||||
err,
|
||||
))
|
||||
RespondBadRequest(c, "请求参数错误", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取用户
|
||||
user, err := service.GetUserByID(userID.(int64))
|
||||
user, err := service.GetUserByID(userID)
|
||||
if err != nil || user == nil {
|
||||
c.JSON(http.StatusNotFound, model.NewErrorResponse(
|
||||
model.CodeNotFound,
|
||||
"用户不存在",
|
||||
err,
|
||||
))
|
||||
RespondNotFound(c, "用户不存在")
|
||||
return
|
||||
}
|
||||
|
||||
// 处理密码修改
|
||||
if req.NewPassword != "" {
|
||||
// 如果提供了新密码,必须同时提供旧密码
|
||||
if req.OldPassword == "" {
|
||||
c.JSON(http.StatusBadRequest, model.NewErrorResponse(
|
||||
model.CodeBadRequest,
|
||||
"修改密码需要提供原密码",
|
||||
nil,
|
||||
))
|
||||
RespondBadRequest(c, "修改密码需要提供原密码", 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,
|
||||
))
|
||||
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.(int64)),
|
||||
)
|
||||
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 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,
|
||||
))
|
||||
loggerInstance.Error("更新用户信息失败", zap.Int64("user_id", user.ID), zap.Error(err))
|
||||
RespondServerError(c, "更新失败", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 重新获取更新后的用户信息
|
||||
updatedUser, err := service.GetUserByID(userID.(int64))
|
||||
updatedUser, err := service.GetUserByID(userID)
|
||||
if err != nil || updatedUser == nil {
|
||||
c.JSON(http.StatusNotFound, model.NewErrorResponse(
|
||||
model.CodeNotFound,
|
||||
"用户不存在",
|
||||
err,
|
||||
))
|
||||
RespondNotFound(c, "用户不存在")
|
||||
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,
|
||||
}))
|
||||
RespondSuccess(c, UserToUserInfo(updatedUser))
|
||||
}
|
||||
|
||||
// GenerateAvatarUploadURL 生成头像上传URL
|
||||
@@ -205,52 +128,36 @@ func UpdateUserProfile(c *gin.Context) {
|
||||
// @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,
|
||||
))
|
||||
userID, ok := GetUserIDFromContext(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var req types.GenerateAvatarUploadURLRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.NewErrorResponse(
|
||||
model.CodeBadRequest,
|
||||
"请求参数错误",
|
||||
err,
|
||||
))
|
||||
RespondBadRequest(c, "请求参数错误", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 调用UploadService生成预签名URL
|
||||
storageClient := storage.MustGetClient()
|
||||
cfg := *config.MustGetRustFSConfig()
|
||||
result, err := service.GenerateAvatarUploadURL(c.Request.Context(), storageClient, cfg, userID.(int64), req.FileName)
|
||||
result, err := service.GenerateAvatarUploadURL(c.Request.Context(), storageClient, cfg, userID, req.FileName)
|
||||
if err != nil {
|
||||
loggerInstance.Error("生成头像上传URL失败",
|
||||
zap.Int64("user_id", userID.(int64)),
|
||||
logger.MustGetLogger().Error("生成头像上传URL失败",
|
||||
zap.Int64("user_id", userID),
|
||||
zap.String("file_name", req.FileName),
|
||||
zap.Error(err),
|
||||
)
|
||||
c.JSON(http.StatusBadRequest, model.NewErrorResponse(
|
||||
model.CodeBadRequest,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
RespondBadRequest(c, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 返回响应
|
||||
c.JSON(http.StatusOK, model.NewSuccessResponse(&types.GenerateAvatarUploadURLResponse{
|
||||
RespondSuccess(c, &types.GenerateAvatarUploadURLResponse{
|
||||
PostURL: result.PostURL,
|
||||
FormData: result.FormData,
|
||||
AvatarURL: result.FileURL,
|
||||
ExpiresIn: 900, // 15分钟 = 900秒
|
||||
}))
|
||||
ExpiresIn: 900,
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateAvatar 更新头像URL
|
||||
@@ -265,65 +172,39 @@ func GenerateAvatarUploadURL(c *gin.Context) {
|
||||
// @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,
|
||||
))
|
||||
userID, ok := GetUserIDFromContext(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
avatarURL := c.Query("avatar_url")
|
||||
if avatarURL == "" {
|
||||
c.JSON(http.StatusBadRequest, model.NewErrorResponse(
|
||||
model.CodeBadRequest,
|
||||
"头像URL不能为空",
|
||||
nil,
|
||||
))
|
||||
RespondBadRequest(c, "头像URL不能为空", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 更新头像
|
||||
if err := service.UpdateUserAvatar(userID.(int64), avatarURL); err != nil {
|
||||
loggerInstance.Error("更新头像失败",
|
||||
zap.Int64("user_id", userID.(int64)),
|
||||
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),
|
||||
)
|
||||
c.JSON(http.StatusInternalServerError, model.NewErrorResponse(
|
||||
model.CodeServerError,
|
||||
"更新头像失败",
|
||||
err,
|
||||
))
|
||||
RespondServerError(c, "更新头像失败", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取更新后的用户信息
|
||||
user, err := service.GetUserByID(userID.(int64))
|
||||
user, err := service.GetUserByID(userID)
|
||||
if err != nil || user == nil {
|
||||
c.JSON(http.StatusNotFound, model.NewErrorResponse(
|
||||
model.CodeNotFound,
|
||||
"用户不存在",
|
||||
err,
|
||||
))
|
||||
RespondNotFound(c, "用户不存在")
|
||||
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,
|
||||
}))
|
||||
RespondSuccess(c, UserToUserInfo(user))
|
||||
}
|
||||
|
||||
// ChangeEmail 更换邮箱
|
||||
@@ -340,79 +221,41 @@ func UpdateAvatar(c *gin.Context) {
|
||||
// @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,
|
||||
))
|
||||
userID, ok := GetUserIDFromContext(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var req types.ChangeEmailRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.NewErrorResponse(
|
||||
model.CodeBadRequest,
|
||||
"请求参数错误",
|
||||
err,
|
||||
))
|
||||
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),
|
||||
)
|
||||
c.JSON(http.StatusBadRequest, model.NewErrorResponse(
|
||||
model.CodeBadRequest,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
loggerInstance.Warn("验证码验证失败", zap.String("new_email", req.NewEmail), zap.Error(err))
|
||||
RespondBadRequest(c, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 更换邮箱
|
||||
if err := service.ChangeUserEmail(userID.(int64), req.NewEmail); err != nil {
|
||||
if err := service.ChangeUserEmail(userID, req.NewEmail); err != nil {
|
||||
loggerInstance.Error("更换邮箱失败",
|
||||
zap.Int64("user_id", userID.(int64)),
|
||||
zap.Int64("user_id", userID),
|
||||
zap.String("new_email", req.NewEmail),
|
||||
zap.Error(err),
|
||||
)
|
||||
c.JSON(http.StatusBadRequest, model.NewErrorResponse(
|
||||
model.CodeBadRequest,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
RespondBadRequest(c, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取更新后的用户信息
|
||||
user, err := service.GetUserByID(userID.(int64))
|
||||
user, err := service.GetUserByID(userID)
|
||||
if err != nil || user == nil {
|
||||
c.JSON(http.StatusNotFound, model.NewErrorResponse(
|
||||
model.CodeNotFound,
|
||||
"用户不存在",
|
||||
err,
|
||||
))
|
||||
RespondNotFound(c, "用户不存在")
|
||||
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,
|
||||
}))
|
||||
RespondSuccess(c, UserToUserInfo(user))
|
||||
}
|
||||
|
||||
// ResetYggdrasilPassword 重置Yggdrasil密码
|
||||
@@ -428,35 +271,19 @@ func ChangeEmail(c *gin.Context) {
|
||||
// @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()
|
||||
|
||||
// 从上下文获取用户ID
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
c.JSON(http.StatusUnauthorized, model.NewErrorResponse(
|
||||
model.CodeUnauthorized,
|
||||
"未授权",
|
||||
nil,
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
userId := userID.(int64)
|
||||
|
||||
// 重置Yggdrasil密码
|
||||
newPassword, err := service.ResetYggdrasilPassword(db, userId)
|
||||
newPassword, err := service.ResetYggdrasilPassword(db, userID)
|
||||
if err != nil {
|
||||
loggerInstance.Error("[ERROR] 重置Yggdrasil密码失败", zap.Error(err), zap.Int64("userId", userId))
|
||||
c.JSON(http.StatusInternalServerError, model.NewErrorResponse(
|
||||
model.CodeServerError,
|
||||
"重置Yggdrasil密码失败",
|
||||
nil,
|
||||
))
|
||||
loggerInstance.Error("重置Yggdrasil密码失败", zap.Error(err), zap.Int64("userId", userID))
|
||||
RespondServerError(c, "重置Yggdrasil密码失败", nil)
|
||||
return
|
||||
}
|
||||
|
||||
loggerInstance.Info("[INFO] Yggdrasil密码重置成功", zap.Int64("userId", userId))
|
||||
c.JSON(http.StatusOK, model.NewSuccessResponse(gin.H{
|
||||
"password": newPassword,
|
||||
}))
|
||||
loggerInstance.Info("Yggdrasil密码重置成功", zap.Int64("userId", userID))
|
||||
RespondSuccess(c, gin.H{"password": newPassword})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user