feat: 添加种子数据初始化功能,重构多个处理程序以简化错误响应和用户验证
This commit is contained in:
@@ -1,15 +1,13 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"carrotskin/internal/model"
|
||||
"carrotskin/internal/service"
|
||||
"carrotskin/internal/types"
|
||||
"carrotskin/pkg/auth"
|
||||
"carrotskin/pkg/email"
|
||||
"carrotskin/pkg/logger"
|
||||
"carrotskin/pkg/redis"
|
||||
"net/http"
|
||||
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
@@ -28,59 +26,32 @@ func Register(c *gin.Context) {
|
||||
loggerInstance := logger.MustGetLogger()
|
||||
jwtService := auth.MustGetJWTService()
|
||||
redisClient := redis.MustGetClient()
|
||||
|
||||
|
||||
var req types.RegisterRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.NewErrorResponse(
|
||||
model.CodeBadRequest,
|
||||
"请求参数错误",
|
||||
err,
|
||||
))
|
||||
RespondBadRequest(c, "请求参数错误", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 验证邮箱验证码
|
||||
if err := service.VerifyCode(c.Request.Context(), redisClient, req.Email, req.VerificationCode, service.VerificationTypeRegister); err != nil {
|
||||
loggerInstance.Warn("验证码验证失败",
|
||||
zap.String("email", req.Email),
|
||||
zap.Error(err),
|
||||
)
|
||||
c.JSON(http.StatusBadRequest, model.NewErrorResponse(
|
||||
model.CodeBadRequest,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
loggerInstance.Warn("验证码验证失败", zap.String("email", req.Email), zap.Error(err))
|
||||
RespondBadRequest(c, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 调用service层注册用户(传递可选的头像URL)
|
||||
|
||||
// 注册用户
|
||||
user, token, err := service.RegisterUser(jwtService, req.Username, req.Password, req.Email, req.Avatar)
|
||||
if err != nil {
|
||||
loggerInstance.Error("用户注册失败", 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.LoginResponse{
|
||||
Token: token,
|
||||
UserInfo: &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, &types.LoginResponse{
|
||||
Token: token,
|
||||
UserInfo: UserToUserInfo(user),
|
||||
})
|
||||
}
|
||||
|
||||
// Login 用户登录
|
||||
@@ -97,53 +68,32 @@ func Register(c *gin.Context) {
|
||||
func Login(c *gin.Context) {
|
||||
loggerInstance := logger.MustGetLogger()
|
||||
jwtService := auth.MustGetJWTService()
|
||||
|
||||
redisClient := redis.MustGetClient()
|
||||
|
||||
var req types.LoginRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.NewErrorResponse(
|
||||
model.CodeBadRequest,
|
||||
"请求参数错误",
|
||||
err,
|
||||
))
|
||||
RespondBadRequest(c, "请求参数错误", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取IP和UserAgent
|
||||
|
||||
ipAddress := c.ClientIP()
|
||||
userAgent := c.GetHeader("User-Agent")
|
||||
|
||||
// 调用service层登录
|
||||
user, token, err := service.LoginUser(jwtService, req.Username, req.Password, ipAddress, userAgent)
|
||||
|
||||
user, token, err := service.LoginUserWithRateLimit(redisClient, jwtService, req.Username, req.Password, ipAddress, userAgent)
|
||||
if err != nil {
|
||||
loggerInstance.Warn("用户登录失败",
|
||||
zap.String("username_or_email", req.Username),
|
||||
zap.String("ip", ipAddress),
|
||||
zap.Error(err),
|
||||
)
|
||||
c.JSON(http.StatusUnauthorized, model.NewErrorResponse(
|
||||
model.CodeUnauthorized,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
RespondUnauthorized(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 返回响应
|
||||
c.JSON(http.StatusOK, model.NewSuccessResponse(&types.LoginResponse{
|
||||
Token: token,
|
||||
UserInfo: &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, &types.LoginResponse{
|
||||
Token: token,
|
||||
UserInfo: UserToUserInfo(user),
|
||||
})
|
||||
}
|
||||
|
||||
// SendVerificationCode 发送验证码
|
||||
@@ -160,35 +110,24 @@ func SendVerificationCode(c *gin.Context) {
|
||||
loggerInstance := logger.MustGetLogger()
|
||||
redisClient := redis.MustGetClient()
|
||||
emailService := email.MustGetService()
|
||||
|
||||
|
||||
var req types.SendVerificationCodeRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.NewErrorResponse(
|
||||
model.CodeBadRequest,
|
||||
"请求参数错误",
|
||||
err,
|
||||
))
|
||||
RespondBadRequest(c, "请求参数错误", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 发送验证码
|
||||
if err := service.SendVerificationCode(c.Request.Context(), redisClient, emailService, req.Email, req.Type); err != nil {
|
||||
loggerInstance.Error("发送验证码失败",
|
||||
zap.String("email", req.Email),
|
||||
zap.String("type", req.Type),
|
||||
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(gin.H{
|
||||
"message": "验证码已发送,请查收邮件",
|
||||
}))
|
||||
RespondSuccess(c, gin.H{"message": "验证码已发送,请查收邮件"})
|
||||
}
|
||||
|
||||
// ResetPassword 重置密码
|
||||
@@ -204,46 +143,26 @@ func SendVerificationCode(c *gin.Context) {
|
||||
func ResetPassword(c *gin.Context) {
|
||||
loggerInstance := logger.MustGetLogger()
|
||||
redisClient := redis.MustGetClient()
|
||||
|
||||
|
||||
var req types.ResetPasswordRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.NewErrorResponse(
|
||||
model.CodeBadRequest,
|
||||
"请求参数错误",
|
||||
err,
|
||||
))
|
||||
RespondBadRequest(c, "请求参数错误", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证验证码
|
||||
if err := service.VerifyCode(c.Request.Context(), redisClient, req.Email, req.VerificationCode, service.VerificationTypeResetPassword); err != nil {
|
||||
loggerInstance.Warn("验证码验证失败",
|
||||
zap.String("email", req.Email),
|
||||
zap.Error(err),
|
||||
)
|
||||
c.JSON(http.StatusBadRequest, model.NewErrorResponse(
|
||||
model.CodeBadRequest,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
loggerInstance.Warn("验证码验证失败", zap.String("email", req.Email), zap.Error(err))
|
||||
RespondBadRequest(c, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 重置密码
|
||||
if err := service.ResetUserPassword(req.Email, req.NewPassword); err != nil {
|
||||
loggerInstance.Error("重置密码失败",
|
||||
zap.String("email", req.Email),
|
||||
zap.Error(err),
|
||||
)
|
||||
c.JSON(http.StatusInternalServerError, model.NewErrorResponse(
|
||||
model.CodeServerError,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
loggerInstance.Error("重置密码失败", zap.String("email", req.Email), zap.Error(err))
|
||||
RespondServerError(c, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, model.NewSuccessResponse(gin.H{
|
||||
"message": "密码重置成功",
|
||||
}))
|
||||
RespondSuccess(c, gin.H{"message": "密码重置成功"})
|
||||
}
|
||||
|
||||
160
internal/handler/helpers.go
Normal file
160
internal/handler/helpers.go
Normal file
@@ -0,0 +1,160 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"carrotskin/internal/model"
|
||||
"carrotskin/internal/types"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// GetUserIDFromContext 从上下文获取用户ID,如果不存在返回未授权响应
|
||||
// 返回值: userID, ok (如果ok为false,已经发送了错误响应)
|
||||
func GetUserIDFromContext(c *gin.Context) (int64, bool) {
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
c.JSON(http.StatusUnauthorized, model.NewErrorResponse(
|
||||
model.CodeUnauthorized,
|
||||
model.MsgUnauthorized,
|
||||
nil,
|
||||
))
|
||||
return 0, false
|
||||
}
|
||||
return userID.(int64), true
|
||||
}
|
||||
|
||||
// UserToUserInfo 将 User 模型转换为 UserInfo 响应
|
||||
func UserToUserInfo(user *model.User) *types.UserInfo {
|
||||
return &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,
|
||||
}
|
||||
}
|
||||
|
||||
// ProfileToProfileInfo 将 Profile 模型转换为 ProfileInfo 响应
|
||||
func ProfileToProfileInfo(profile *model.Profile) *types.ProfileInfo {
|
||||
return &types.ProfileInfo{
|
||||
UUID: profile.UUID,
|
||||
UserID: profile.UserID,
|
||||
Name: profile.Name,
|
||||
SkinID: profile.SkinID,
|
||||
CapeID: profile.CapeID,
|
||||
IsActive: profile.IsActive,
|
||||
LastUsedAt: profile.LastUsedAt,
|
||||
CreatedAt: profile.CreatedAt,
|
||||
UpdatedAt: profile.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// ProfilesToProfileInfos 批量转换 Profile 模型为 ProfileInfo 响应
|
||||
func ProfilesToProfileInfos(profiles []*model.Profile) []*types.ProfileInfo {
|
||||
result := make([]*types.ProfileInfo, 0, len(profiles))
|
||||
for _, profile := range profiles {
|
||||
result = append(result, ProfileToProfileInfo(profile))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// TextureToTextureInfo 将 Texture 模型转换为 TextureInfo 响应
|
||||
func TextureToTextureInfo(texture *model.Texture) *types.TextureInfo {
|
||||
return &types.TextureInfo{
|
||||
ID: texture.ID,
|
||||
UploaderID: texture.UploaderID,
|
||||
Name: texture.Name,
|
||||
Description: texture.Description,
|
||||
Type: types.TextureType(texture.Type),
|
||||
URL: texture.URL,
|
||||
Hash: texture.Hash,
|
||||
Size: texture.Size,
|
||||
IsPublic: texture.IsPublic,
|
||||
DownloadCount: texture.DownloadCount,
|
||||
FavoriteCount: texture.FavoriteCount,
|
||||
IsSlim: texture.IsSlim,
|
||||
Status: texture.Status,
|
||||
CreatedAt: texture.CreatedAt,
|
||||
UpdatedAt: texture.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// TexturesToTextureInfos 批量转换 Texture 模型为 TextureInfo 响应
|
||||
func TexturesToTextureInfos(textures []*model.Texture) []*types.TextureInfo {
|
||||
result := make([]*types.TextureInfo, len(textures))
|
||||
for i, texture := range textures {
|
||||
result[i] = TextureToTextureInfo(texture)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// RespondBadRequest 返回400错误响应
|
||||
func RespondBadRequest(c *gin.Context, message string, err error) {
|
||||
c.JSON(http.StatusBadRequest, model.NewErrorResponse(
|
||||
model.CodeBadRequest,
|
||||
message,
|
||||
err,
|
||||
))
|
||||
}
|
||||
|
||||
// RespondUnauthorized 返回401错误响应
|
||||
func RespondUnauthorized(c *gin.Context, message string) {
|
||||
c.JSON(http.StatusUnauthorized, model.NewErrorResponse(
|
||||
model.CodeUnauthorized,
|
||||
message,
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
// RespondForbidden 返回403错误响应
|
||||
func RespondForbidden(c *gin.Context, message string) {
|
||||
c.JSON(http.StatusForbidden, model.NewErrorResponse(
|
||||
model.CodeForbidden,
|
||||
message,
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
// RespondNotFound 返回404错误响应
|
||||
func RespondNotFound(c *gin.Context, message string) {
|
||||
c.JSON(http.StatusNotFound, model.NewErrorResponse(
|
||||
model.CodeNotFound,
|
||||
message,
|
||||
nil,
|
||||
))
|
||||
}
|
||||
|
||||
// RespondServerError 返回500错误响应
|
||||
func RespondServerError(c *gin.Context, message string, err error) {
|
||||
c.JSON(http.StatusInternalServerError, model.NewErrorResponse(
|
||||
model.CodeServerError,
|
||||
message,
|
||||
err,
|
||||
))
|
||||
}
|
||||
|
||||
// RespondSuccess 返回成功响应
|
||||
func RespondSuccess(c *gin.Context, data interface{}) {
|
||||
c.JSON(http.StatusOK, model.NewSuccessResponse(data))
|
||||
}
|
||||
|
||||
// RespondWithError 根据错误消息自动选择状态码
|
||||
func RespondWithError(c *gin.Context, err error) {
|
||||
msg := err.Error()
|
||||
switch msg {
|
||||
case "档案不存在", "用户不存在", "材质不存在":
|
||||
RespondNotFound(c, msg)
|
||||
case "无权操作此档案", "无权操作此材质":
|
||||
RespondForbidden(c, msg)
|
||||
case "未授权":
|
||||
RespondUnauthorized(c, msg)
|
||||
default:
|
||||
RespondServerError(c, msg, nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"carrotskin/internal/model"
|
||||
"carrotskin/internal/service"
|
||||
"carrotskin/internal/types"
|
||||
"carrotskin/pkg/database"
|
||||
"carrotskin/pkg/logger"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
@@ -26,70 +24,37 @@ import (
|
||||
// @Failure 500 {object} model.ErrorResponse "服务器错误"
|
||||
// @Router /api/v1/profile [post]
|
||||
func CreateProfile(c *gin.Context) {
|
||||
loggerInstance := logger.MustGetLogger()
|
||||
// 获取用户ID
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
c.JSON(http.StatusUnauthorized, model.NewErrorResponse(
|
||||
model.CodeUnauthorized,
|
||||
"未授权",
|
||||
nil,
|
||||
))
|
||||
userID, ok := GetUserIDFromContext(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// 解析请求
|
||||
var req types.CreateProfileRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.NewErrorResponse(
|
||||
model.CodeBadRequest,
|
||||
"请求参数错误: "+err.Error(),
|
||||
nil,
|
||||
))
|
||||
RespondBadRequest(c, "请求参数错误: "+err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: 从配置或数据库读取限制
|
||||
maxProfiles := 5
|
||||
maxProfiles := service.GetMaxProfilesPerUser()
|
||||
db := database.MustGetDB()
|
||||
// 检查档案数量限制
|
||||
if err := service.CheckProfileLimit(db, userID.(int64), maxProfiles); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.NewErrorResponse(
|
||||
model.CodeBadRequest,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
|
||||
if err := service.CheckProfileLimit(db, userID, maxProfiles); err != nil {
|
||||
RespondBadRequest(c, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 创建档案
|
||||
profile, err := service.CreateProfile(db, userID.(int64), req.Name)
|
||||
profile, err := service.CreateProfile(db, userID, req.Name)
|
||||
if err != nil {
|
||||
loggerInstance.Error("创建档案失败",
|
||||
zap.Int64("user_id", userID.(int64)),
|
||||
logger.MustGetLogger().Error("创建档案失败",
|
||||
zap.Int64("user_id", userID),
|
||||
zap.String("name", req.Name),
|
||||
zap.Error(err),
|
||||
)
|
||||
c.JSON(http.StatusInternalServerError, model.NewErrorResponse(
|
||||
model.CodeServerError,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
RespondServerError(c, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 返回成功响应
|
||||
c.JSON(http.StatusOK, model.NewSuccessResponse(&types.ProfileInfo{
|
||||
UUID: profile.UUID,
|
||||
UserID: profile.UserID,
|
||||
Name: profile.Name,
|
||||
SkinID: profile.SkinID,
|
||||
CapeID: profile.CapeID,
|
||||
IsActive: profile.IsActive,
|
||||
LastUsedAt: profile.LastUsedAt,
|
||||
CreatedAt: profile.CreatedAt,
|
||||
UpdatedAt: profile.UpdatedAt,
|
||||
}))
|
||||
RespondSuccess(c, ProfileToProfileInfo(profile))
|
||||
}
|
||||
|
||||
// GetProfiles 获取档案列表
|
||||
@@ -104,50 +69,22 @@ func CreateProfile(c *gin.Context) {
|
||||
// @Failure 500 {object} model.ErrorResponse "服务器错误"
|
||||
// @Router /api/v1/profile [get]
|
||||
func GetProfiles(c *gin.Context) {
|
||||
loggerInstance := logger.MustGetLogger()
|
||||
// 获取用户ID
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
c.JSON(http.StatusUnauthorized, model.NewErrorResponse(
|
||||
model.CodeUnauthorized,
|
||||
"未授权",
|
||||
nil,
|
||||
))
|
||||
userID, ok := GetUserIDFromContext(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// 查询档案列表
|
||||
profiles, err := service.GetUserProfiles(database.MustGetDB(), userID.(int64))
|
||||
profiles, err := service.GetUserProfiles(database.MustGetDB(), userID)
|
||||
if err != nil {
|
||||
loggerInstance.Error("获取档案列表失败",
|
||||
zap.Int64("user_id", userID.(int64)),
|
||||
logger.MustGetLogger().Error("获取档案列表失败",
|
||||
zap.Int64("user_id", userID),
|
||||
zap.Error(err),
|
||||
)
|
||||
c.JSON(http.StatusInternalServerError, model.NewErrorResponse(
|
||||
model.CodeServerError,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
RespondServerError(c, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 转换为响应格式
|
||||
result := make([]*types.ProfileInfo, 0, len(profiles))
|
||||
for _, profile := range profiles {
|
||||
result = append(result, &types.ProfileInfo{
|
||||
UUID: profile.UUID,
|
||||
UserID: profile.UserID,
|
||||
Name: profile.Name,
|
||||
SkinID: profile.SkinID,
|
||||
CapeID: profile.CapeID,
|
||||
IsActive: profile.IsActive,
|
||||
LastUsedAt: profile.LastUsedAt,
|
||||
CreatedAt: profile.CreatedAt,
|
||||
UpdatedAt: profile.UpdatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, model.NewSuccessResponse(result))
|
||||
RespondSuccess(c, ProfilesToProfileInfos(profiles))
|
||||
}
|
||||
|
||||
// GetProfile 获取档案详情
|
||||
@@ -162,36 +99,19 @@ func GetProfiles(c *gin.Context) {
|
||||
// @Failure 500 {object} model.ErrorResponse "服务器错误"
|
||||
// @Router /api/v1/profile/{uuid} [get]
|
||||
func GetProfile(c *gin.Context) {
|
||||
loggerInstance := logger.MustGetLogger()
|
||||
uuid := c.Param("uuid")
|
||||
|
||||
// 查询档案
|
||||
profile, err := service.GetProfileByUUID(database.MustGetDB(), uuid)
|
||||
if err != nil {
|
||||
loggerInstance.Error("获取档案失败",
|
||||
logger.MustGetLogger().Error("获取档案失败",
|
||||
zap.String("uuid", uuid),
|
||||
zap.Error(err),
|
||||
)
|
||||
c.JSON(http.StatusNotFound, model.NewErrorResponse(
|
||||
model.CodeNotFound,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
RespondNotFound(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 返回成功响应
|
||||
c.JSON(http.StatusOK, model.NewSuccessResponse(&types.ProfileInfo{
|
||||
UUID: profile.UUID,
|
||||
UserID: profile.UserID,
|
||||
Name: profile.Name,
|
||||
SkinID: profile.SkinID,
|
||||
CapeID: profile.CapeID,
|
||||
IsActive: profile.IsActive,
|
||||
LastUsedAt: profile.LastUsedAt,
|
||||
CreatedAt: profile.CreatedAt,
|
||||
UpdatedAt: profile.UpdatedAt,
|
||||
}))
|
||||
RespondSuccess(c, ProfileToProfileInfo(profile))
|
||||
}
|
||||
|
||||
// UpdateProfile 更新档案
|
||||
@@ -211,72 +131,36 @@ func GetProfile(c *gin.Context) {
|
||||
// @Failure 500 {object} model.ErrorResponse "服务器错误"
|
||||
// @Router /api/v1/profile/{uuid} [put]
|
||||
func UpdateProfile(c *gin.Context) {
|
||||
loggerInstance := logger.MustGetLogger()
|
||||
userID, ok := GetUserIDFromContext(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
uuid := c.Param("uuid")
|
||||
|
||||
// 获取用户ID
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
c.JSON(http.StatusUnauthorized, model.NewErrorResponse(
|
||||
model.CodeUnauthorized,
|
||||
"未授权",
|
||||
nil,
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
// 解析请求
|
||||
var req types.UpdateProfileRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.NewErrorResponse(
|
||||
model.CodeBadRequest,
|
||||
"请求参数错误: "+err.Error(),
|
||||
nil,
|
||||
))
|
||||
RespondBadRequest(c, "请求参数错误: "+err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 更新档案
|
||||
var namePtr *string
|
||||
if req.Name != "" {
|
||||
namePtr = &req.Name
|
||||
}
|
||||
|
||||
profile, err := service.UpdateProfile(database.MustGetDB(), uuid, userID.(int64), namePtr, req.SkinID, req.CapeID)
|
||||
profile, err := service.UpdateProfile(database.MustGetDB(), uuid, userID, namePtr, req.SkinID, req.CapeID)
|
||||
if err != nil {
|
||||
loggerInstance.Error("更新档案失败",
|
||||
logger.MustGetLogger().Error("更新档案失败",
|
||||
zap.String("uuid", uuid),
|
||||
zap.Int64("user_id", userID.(int64)),
|
||||
zap.Int64("user_id", userID),
|
||||
zap.Error(err),
|
||||
)
|
||||
|
||||
statusCode := http.StatusInternalServerError
|
||||
if err.Error() == "档案不存在" {
|
||||
statusCode = http.StatusNotFound
|
||||
} else if err.Error() == "无权操作此档案" {
|
||||
statusCode = http.StatusForbidden
|
||||
}
|
||||
|
||||
c.JSON(statusCode, model.NewErrorResponse(
|
||||
model.CodeServerError,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
RespondWithError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 返回成功响应
|
||||
c.JSON(http.StatusOK, model.NewSuccessResponse(&types.ProfileInfo{
|
||||
UUID: profile.UUID,
|
||||
UserID: profile.UserID,
|
||||
Name: profile.Name,
|
||||
SkinID: profile.SkinID,
|
||||
CapeID: profile.CapeID,
|
||||
IsActive: profile.IsActive,
|
||||
LastUsedAt: profile.LastUsedAt,
|
||||
CreatedAt: profile.CreatedAt,
|
||||
UpdatedAt: profile.UpdatedAt,
|
||||
}))
|
||||
RespondSuccess(c, ProfileToProfileInfo(profile))
|
||||
}
|
||||
|
||||
// DeleteProfile 删除档案
|
||||
@@ -294,48 +178,25 @@ func UpdateProfile(c *gin.Context) {
|
||||
// @Failure 500 {object} model.ErrorResponse "服务器错误"
|
||||
// @Router /api/v1/profile/{uuid} [delete]
|
||||
func DeleteProfile(c *gin.Context) {
|
||||
loggerInstance := logger.MustGetLogger()
|
||||
userID, ok := GetUserIDFromContext(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
uuid := c.Param("uuid")
|
||||
|
||||
// 获取用户ID
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
c.JSON(http.StatusUnauthorized, model.NewErrorResponse(
|
||||
model.CodeUnauthorized,
|
||||
"未授权",
|
||||
nil,
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
// 删除档案
|
||||
err := service.DeleteProfile(database.MustGetDB(), uuid, userID.(int64))
|
||||
err := service.DeleteProfile(database.MustGetDB(), uuid, userID)
|
||||
if err != nil {
|
||||
loggerInstance.Error("删除档案失败",
|
||||
logger.MustGetLogger().Error("删除档案失败",
|
||||
zap.String("uuid", uuid),
|
||||
zap.Int64("user_id", userID.(int64)),
|
||||
zap.Int64("user_id", userID),
|
||||
zap.Error(err),
|
||||
)
|
||||
|
||||
statusCode := http.StatusInternalServerError
|
||||
if err.Error() == "档案不存在" {
|
||||
statusCode = http.StatusNotFound
|
||||
} else if err.Error() == "无权操作此档案" {
|
||||
statusCode = http.StatusForbidden
|
||||
}
|
||||
|
||||
c.JSON(statusCode, model.NewErrorResponse(
|
||||
model.CodeServerError,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
RespondWithError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 返回成功响应
|
||||
c.JSON(http.StatusOK, model.NewSuccessResponse(gin.H{
|
||||
"message": "删除成功",
|
||||
}))
|
||||
RespondSuccess(c, gin.H{"message": "删除成功"})
|
||||
}
|
||||
|
||||
// SetActiveProfile 设置活跃档案
|
||||
@@ -353,46 +214,23 @@ func DeleteProfile(c *gin.Context) {
|
||||
// @Failure 500 {object} model.ErrorResponse "服务器错误"
|
||||
// @Router /api/v1/profile/{uuid}/activate [post]
|
||||
func SetActiveProfile(c *gin.Context) {
|
||||
loggerInstance := logger.MustGetLogger()
|
||||
userID, ok := GetUserIDFromContext(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
uuid := c.Param("uuid")
|
||||
|
||||
// 获取用户ID
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
c.JSON(http.StatusUnauthorized, model.NewErrorResponse(
|
||||
model.CodeUnauthorized,
|
||||
"未授权",
|
||||
nil,
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
// 设置活跃状态
|
||||
err := service.SetActiveProfile(database.MustGetDB(), uuid, userID.(int64))
|
||||
err := service.SetActiveProfile(database.MustGetDB(), uuid, userID)
|
||||
if err != nil {
|
||||
loggerInstance.Error("设置活跃档案失败",
|
||||
logger.MustGetLogger().Error("设置活跃档案失败",
|
||||
zap.String("uuid", uuid),
|
||||
zap.Int64("user_id", userID.(int64)),
|
||||
zap.Int64("user_id", userID),
|
||||
zap.Error(err),
|
||||
)
|
||||
|
||||
statusCode := http.StatusInternalServerError
|
||||
if err.Error() == "档案不存在" {
|
||||
statusCode = http.StatusNotFound
|
||||
} else if err.Error() == "无权操作此档案" {
|
||||
statusCode = http.StatusForbidden
|
||||
}
|
||||
|
||||
c.JSON(statusCode, model.NewErrorResponse(
|
||||
model.CodeServerError,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
RespondWithError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 返回成功响应
|
||||
c.JSON(http.StatusOK, model.NewSuccessResponse(gin.H{
|
||||
"message": "设置成功",
|
||||
}))
|
||||
RespondSuccess(c, gin.H{"message": "设置成功"})
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"carrotskin/pkg/database"
|
||||
"carrotskin/pkg/logger"
|
||||
"carrotskin/pkg/storage"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -27,59 +26,44 @@ import (
|
||||
// @Failure 400 {object} model.ErrorResponse "请求参数错误"
|
||||
// @Router /api/v1/texture/upload-url [post]
|
||||
func GenerateTextureUploadURL(c *gin.Context) {
|
||||
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.GenerateTextureUploadURLRequest
|
||||
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.GenerateTextureUploadURL(
|
||||
c.Request.Context(),
|
||||
storageClient,
|
||||
cfg,
|
||||
userID.(int64),
|
||||
userID,
|
||||
req.FileName,
|
||||
string(req.TextureType),
|
||||
)
|
||||
if err != nil {
|
||||
logger.MustGetLogger().Error("生成材质上传URL失败",
|
||||
zap.Int64("user_id", userID.(int64)),
|
||||
zap.Int64("user_id", userID),
|
||||
zap.String("file_name", req.FileName),
|
||||
zap.String("texture_type", string(req.TextureType)),
|
||||
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.GenerateTextureUploadURLResponse{
|
||||
RespondSuccess(c, &types.GenerateTextureUploadURLResponse{
|
||||
PostURL: result.PostURL,
|
||||
FormData: result.FormData,
|
||||
TextureURL: result.FileURL,
|
||||
ExpiresIn: 900, // 15分钟 = 900秒
|
||||
}))
|
||||
ExpiresIn: 900,
|
||||
})
|
||||
}
|
||||
|
||||
// CreateTexture 创建材质记录
|
||||
@@ -94,40 +78,25 @@ func GenerateTextureUploadURL(c *gin.Context) {
|
||||
// @Failure 400 {object} model.ErrorResponse "请求参数错误"
|
||||
// @Router /api/v1/texture [post]
|
||||
func CreateTexture(c *gin.Context) {
|
||||
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.CreateTextureRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.NewErrorResponse(
|
||||
model.CodeBadRequest,
|
||||
"请求参数错误",
|
||||
err,
|
||||
))
|
||||
RespondBadRequest(c, "请求参数错误", err)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: 从配置或数据库读取限制
|
||||
maxTextures := 100
|
||||
if err := service.CheckTextureUploadLimit(database.MustGetDB(), userID.(int64), maxTextures); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.NewErrorResponse(
|
||||
model.CodeBadRequest,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
maxTextures := service.GetMaxTexturesPerUser()
|
||||
if err := service.CheckTextureUploadLimit(database.MustGetDB(), userID, maxTextures); err != nil {
|
||||
RespondBadRequest(c, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// 创建材质
|
||||
texture, err := service.CreateTexture(database.MustGetDB(),
|
||||
userID.(int64),
|
||||
userID,
|
||||
req.Name,
|
||||
req.Description,
|
||||
string(req.Type),
|
||||
@@ -139,36 +108,15 @@ func CreateTexture(c *gin.Context) {
|
||||
)
|
||||
if err != nil {
|
||||
logger.MustGetLogger().Error("创建材质失败",
|
||||
zap.Int64("user_id", userID.(int64)),
|
||||
zap.Int64("user_id", userID),
|
||||
zap.String("name", req.Name),
|
||||
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.TextureInfo{
|
||||
ID: texture.ID,
|
||||
UploaderID: texture.UploaderID,
|
||||
Name: texture.Name,
|
||||
Description: texture.Description,
|
||||
Type: types.TextureType(texture.Type),
|
||||
URL: texture.URL,
|
||||
Hash: texture.Hash,
|
||||
Size: texture.Size,
|
||||
IsPublic: texture.IsPublic,
|
||||
DownloadCount: texture.DownloadCount,
|
||||
FavoriteCount: texture.FavoriteCount,
|
||||
IsSlim: texture.IsSlim,
|
||||
Status: texture.Status,
|
||||
CreatedAt: texture.CreatedAt,
|
||||
UpdatedAt: texture.UpdatedAt,
|
||||
}))
|
||||
RespondSuccess(c, TextureToTextureInfo(texture))
|
||||
}
|
||||
|
||||
// GetTexture 获取材质详情
|
||||
@@ -182,44 +130,19 @@ func CreateTexture(c *gin.Context) {
|
||||
// @Failure 404 {object} model.ErrorResponse "材质不存在"
|
||||
// @Router /api/v1/texture/{id} [get]
|
||||
func GetTexture(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseInt(idStr, 10, 64)
|
||||
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.NewErrorResponse(
|
||||
model.CodeBadRequest,
|
||||
"无效的材质ID",
|
||||
err,
|
||||
))
|
||||
RespondBadRequest(c, "无效的材质ID", err)
|
||||
return
|
||||
}
|
||||
|
||||
texture, err := service.GetTextureByID(database.MustGetDB(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, model.NewErrorResponse(
|
||||
model.CodeNotFound,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
RespondNotFound(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, model.NewSuccessResponse(&types.TextureInfo{
|
||||
ID: texture.ID,
|
||||
UploaderID: texture.UploaderID,
|
||||
Name: texture.Name,
|
||||
Description: texture.Description,
|
||||
Type: types.TextureType(texture.Type),
|
||||
URL: texture.URL,
|
||||
Hash: texture.Hash,
|
||||
Size: texture.Size,
|
||||
IsPublic: texture.IsPublic,
|
||||
DownloadCount: texture.DownloadCount,
|
||||
FavoriteCount: texture.FavoriteCount,
|
||||
IsSlim: texture.IsSlim,
|
||||
Status: texture.Status,
|
||||
CreatedAt: texture.CreatedAt,
|
||||
UpdatedAt: texture.UpdatedAt,
|
||||
}))
|
||||
RespondSuccess(c, TextureToTextureInfo(texture))
|
||||
}
|
||||
|
||||
// SearchTextures 搜索材质
|
||||
@@ -253,41 +176,12 @@ func SearchTextures(c *gin.Context) {
|
||||
|
||||
textures, total, err := service.SearchTextures(database.MustGetDB(), keyword, textureType, publicOnly, page, pageSize)
|
||||
if err != nil {
|
||||
logger.MustGetLogger().Error("搜索材质失败",
|
||||
zap.String("keyword", keyword),
|
||||
zap.Error(err),
|
||||
)
|
||||
c.JSON(http.StatusInternalServerError, model.NewErrorResponse(
|
||||
model.CodeServerError,
|
||||
"搜索材质失败",
|
||||
err,
|
||||
))
|
||||
logger.MustGetLogger().Error("搜索材质失败", zap.String("keyword", keyword), zap.Error(err))
|
||||
RespondServerError(c, "搜索材质失败", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 转换为TextureInfo
|
||||
textureInfos := make([]*types.TextureInfo, len(textures))
|
||||
for i, texture := range textures {
|
||||
textureInfos[i] = &types.TextureInfo{
|
||||
ID: texture.ID,
|
||||
UploaderID: texture.UploaderID,
|
||||
Name: texture.Name,
|
||||
Description: texture.Description,
|
||||
Type: types.TextureType(texture.Type),
|
||||
URL: texture.URL,
|
||||
Hash: texture.Hash,
|
||||
Size: texture.Size,
|
||||
IsPublic: texture.IsPublic,
|
||||
DownloadCount: texture.DownloadCount,
|
||||
FavoriteCount: texture.FavoriteCount,
|
||||
IsSlim: texture.IsSlim,
|
||||
Status: texture.Status,
|
||||
CreatedAt: texture.CreatedAt,
|
||||
UpdatedAt: texture.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, model.NewPaginationResponse(textureInfos, total, page, pageSize))
|
||||
c.JSON(200, model.NewPaginationResponse(TexturesToTextureInfos(textures), total, page, pageSize))
|
||||
}
|
||||
|
||||
// UpdateTexture 更新材质
|
||||
@@ -303,69 +197,35 @@ func SearchTextures(c *gin.Context) {
|
||||
// @Failure 403 {object} model.ErrorResponse "无权操作"
|
||||
// @Router /api/v1/texture/{id} [put]
|
||||
func UpdateTexture(c *gin.Context) {
|
||||
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
|
||||
}
|
||||
|
||||
idStr := c.Param("id")
|
||||
textureID, err := strconv.ParseInt(idStr, 10, 64)
|
||||
textureID, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.NewErrorResponse(
|
||||
model.CodeBadRequest,
|
||||
"无效的材质ID",
|
||||
err,
|
||||
))
|
||||
RespondBadRequest(c, "无效的材质ID", err)
|
||||
return
|
||||
}
|
||||
|
||||
var req types.UpdateTextureRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.NewErrorResponse(
|
||||
model.CodeBadRequest,
|
||||
"请求参数错误",
|
||||
err,
|
||||
))
|
||||
RespondBadRequest(c, "请求参数错误", err)
|
||||
return
|
||||
}
|
||||
|
||||
texture, err := service.UpdateTexture(database.MustGetDB(), textureID, userID.(int64), req.Name, req.Description, req.IsPublic)
|
||||
texture, err := service.UpdateTexture(database.MustGetDB(), textureID, userID, req.Name, req.Description, req.IsPublic)
|
||||
if err != nil {
|
||||
logger.MustGetLogger().Error("更新材质失败",
|
||||
zap.Int64("user_id", userID.(int64)),
|
||||
zap.Int64("user_id", userID),
|
||||
zap.Int64("texture_id", textureID),
|
||||
zap.Error(err),
|
||||
)
|
||||
c.JSON(http.StatusForbidden, model.NewErrorResponse(
|
||||
model.CodeForbidden,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
RespondForbidden(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, model.NewSuccessResponse(&types.TextureInfo{
|
||||
ID: texture.ID,
|
||||
UploaderID: texture.UploaderID,
|
||||
Name: texture.Name,
|
||||
Description: texture.Description,
|
||||
Type: types.TextureType(texture.Type),
|
||||
URL: texture.URL,
|
||||
Hash: texture.Hash,
|
||||
Size: texture.Size,
|
||||
IsPublic: texture.IsPublic,
|
||||
DownloadCount: texture.DownloadCount,
|
||||
FavoriteCount: texture.FavoriteCount,
|
||||
IsSlim: texture.IsSlim,
|
||||
Status: texture.Status,
|
||||
CreatedAt: texture.CreatedAt,
|
||||
UpdatedAt: texture.UpdatedAt,
|
||||
}))
|
||||
RespondSuccess(c, TextureToTextureInfo(texture))
|
||||
}
|
||||
|
||||
// DeleteTexture 删除材质
|
||||
@@ -380,42 +240,28 @@ func UpdateTexture(c *gin.Context) {
|
||||
// @Failure 403 {object} model.ErrorResponse "无权操作"
|
||||
// @Router /api/v1/texture/{id} [delete]
|
||||
func DeleteTexture(c *gin.Context) {
|
||||
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
|
||||
}
|
||||
|
||||
idStr := c.Param("id")
|
||||
textureID, err := strconv.ParseInt(idStr, 10, 64)
|
||||
textureID, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.NewErrorResponse(
|
||||
model.CodeBadRequest,
|
||||
"无效的材质ID",
|
||||
err,
|
||||
))
|
||||
RespondBadRequest(c, "无效的材质ID", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := service.DeleteTexture(database.MustGetDB(), textureID, userID.(int64)); err != nil {
|
||||
if err := service.DeleteTexture(database.MustGetDB(), textureID, userID); err != nil {
|
||||
logger.MustGetLogger().Error("删除材质失败",
|
||||
zap.Int64("user_id", userID.(int64)),
|
||||
zap.Int64("user_id", userID),
|
||||
zap.Int64("texture_id", textureID),
|
||||
zap.Error(err),
|
||||
)
|
||||
c.JSON(http.StatusForbidden, model.NewErrorResponse(
|
||||
model.CodeForbidden,
|
||||
err.Error(),
|
||||
nil,
|
||||
))
|
||||
RespondForbidden(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, model.NewSuccessResponse(nil))
|
||||
RespondSuccess(c, nil)
|
||||
}
|
||||
|
||||
// ToggleFavorite 切换收藏状态
|
||||
@@ -429,45 +275,29 @@ func DeleteTexture(c *gin.Context) {
|
||||
// @Success 200 {object} model.Response "切换成功"
|
||||
// @Router /api/v1/texture/{id}/favorite [post]
|
||||
func ToggleFavorite(c *gin.Context) {
|
||||
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
|
||||
}
|
||||
|
||||
idStr := c.Param("id")
|
||||
textureID, err := strconv.ParseInt(idStr, 10, 64)
|
||||
textureID, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.NewErrorResponse(
|
||||
model.CodeBadRequest,
|
||||
"无效的材质ID",
|
||||
err,
|
||||
))
|
||||
RespondBadRequest(c, "无效的材质ID", err)
|
||||
return
|
||||
}
|
||||
|
||||
isFavorited, err := service.ToggleTextureFavorite(database.MustGetDB(), userID.(int64), textureID)
|
||||
isFavorited, err := service.ToggleTextureFavorite(database.MustGetDB(), userID, textureID)
|
||||
if err != nil {
|
||||
logger.MustGetLogger().Error("切换收藏状态失败",
|
||||
zap.Int64("user_id", userID.(int64)),
|
||||
zap.Int64("user_id", userID),
|
||||
zap.Int64("texture_id", textureID),
|
||||
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(map[string]bool{
|
||||
"is_favorited": isFavorited,
|
||||
}))
|
||||
RespondSuccess(c, map[string]bool{"is_favorited": isFavorited})
|
||||
}
|
||||
|
||||
// GetUserTextures 获取用户上传的材质列表
|
||||
@@ -482,56 +312,22 @@ func ToggleFavorite(c *gin.Context) {
|
||||
// @Success 200 {object} model.PaginationResponse "获取成功"
|
||||
// @Router /api/v1/texture/my [get]
|
||||
func GetUserTextures(c *gin.Context) {
|
||||
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
|
||||
}
|
||||
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||||
|
||||
textures, total, err := service.GetUserTextures(database.MustGetDB(), userID.(int64), page, pageSize)
|
||||
textures, total, err := service.GetUserTextures(database.MustGetDB(), userID, page, pageSize)
|
||||
if err != nil {
|
||||
logger.MustGetLogger().Error("获取用户材质列表失败",
|
||||
zap.Int64("user_id", userID.(int64)),
|
||||
zap.Error(err),
|
||||
)
|
||||
c.JSON(http.StatusInternalServerError, model.NewErrorResponse(
|
||||
model.CodeServerError,
|
||||
"获取材质列表失败",
|
||||
err,
|
||||
))
|
||||
logger.MustGetLogger().Error("获取用户材质列表失败", zap.Int64("user_id", userID), zap.Error(err))
|
||||
RespondServerError(c, "获取材质列表失败", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 转换为TextureInfo
|
||||
textureInfos := make([]*types.TextureInfo, len(textures))
|
||||
for i, texture := range textures {
|
||||
textureInfos[i] = &types.TextureInfo{
|
||||
ID: texture.ID,
|
||||
UploaderID: texture.UploaderID,
|
||||
Name: texture.Name,
|
||||
Description: texture.Description,
|
||||
Type: types.TextureType(texture.Type),
|
||||
URL: texture.URL,
|
||||
Hash: texture.Hash,
|
||||
Size: texture.Size,
|
||||
IsPublic: texture.IsPublic,
|
||||
DownloadCount: texture.DownloadCount,
|
||||
FavoriteCount: texture.FavoriteCount,
|
||||
IsSlim: texture.IsSlim,
|
||||
Status: texture.Status,
|
||||
CreatedAt: texture.CreatedAt,
|
||||
UpdatedAt: texture.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, model.NewPaginationResponse(textureInfos, total, page, pageSize))
|
||||
c.JSON(200, model.NewPaginationResponse(TexturesToTextureInfos(textures), total, page, pageSize))
|
||||
}
|
||||
|
||||
// GetUserFavorites 获取用户收藏的材质列表
|
||||
@@ -546,54 +342,20 @@ func GetUserTextures(c *gin.Context) {
|
||||
// @Success 200 {object} model.PaginationResponse "获取成功"
|
||||
// @Router /api/v1/texture/favorites [get]
|
||||
func GetUserFavorites(c *gin.Context) {
|
||||
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
|
||||
}
|
||||
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||||
|
||||
textures, total, err := service.GetUserTextureFavorites(database.MustGetDB(), userID.(int64), page, pageSize)
|
||||
textures, total, err := service.GetUserTextureFavorites(database.MustGetDB(), userID, page, pageSize)
|
||||
if err != nil {
|
||||
logger.MustGetLogger().Error("获取用户收藏列表失败",
|
||||
zap.Int64("user_id", userID.(int64)),
|
||||
zap.Error(err),
|
||||
)
|
||||
c.JSON(http.StatusInternalServerError, model.NewErrorResponse(
|
||||
model.CodeServerError,
|
||||
"获取收藏列表失败",
|
||||
err,
|
||||
))
|
||||
logger.MustGetLogger().Error("获取用户收藏列表失败", zap.Int64("user_id", userID), zap.Error(err))
|
||||
RespondServerError(c, "获取收藏列表失败", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 转换为TextureInfo
|
||||
textureInfos := make([]*types.TextureInfo, len(textures))
|
||||
for i, texture := range textures {
|
||||
textureInfos[i] = &types.TextureInfo{
|
||||
ID: texture.ID,
|
||||
UploaderID: texture.UploaderID,
|
||||
Name: texture.Name,
|
||||
Description: texture.Description,
|
||||
Type: types.TextureType(texture.Type),
|
||||
URL: texture.URL,
|
||||
Hash: texture.Hash,
|
||||
Size: texture.Size,
|
||||
IsPublic: texture.IsPublic,
|
||||
DownloadCount: texture.DownloadCount,
|
||||
FavoriteCount: texture.FavoriteCount,
|
||||
IsSlim: texture.IsSlim,
|
||||
Status: texture.Status,
|
||||
CreatedAt: texture.CreatedAt,
|
||||
UpdatedAt: texture.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, model.NewPaginationResponse(textureInfos, total, page, pageSize))
|
||||
c.JSON(200, model.NewPaginationResponse(TexturesToTextureInfos(textures), total, page, pageSize))
|
||||
}
|
||||
|
||||
@@ -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})
|
||||
}
|
||||
|
||||
@@ -405,12 +405,8 @@ func SignOut(c *gin.Context) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
password, err := service.GetPasswordByUserId(db, user.ID)
|
||||
if err != nil {
|
||||
loggerInstance.Error("[ERROR] 邮箱查找失败", zap.Any("UserId:", user.ID), zap.Error(err))
|
||||
}
|
||||
// 验证密码
|
||||
if password != request.Password {
|
||||
if err := service.VerifyPassword(db, request.Password, user.ID); err != nil {
|
||||
loggerInstance.Warn("[WARN] 登出失败: 密码错误", zap.Any("用户ID:", user.ID))
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": ErrWrongPassword})
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user