Files
backend/internal/handler/helpers.go
lafay 06539dc086 feat: Add public user information retrieval endpoint
- Introduced a new endpoint to fetch public user information without authentication.
- Implemented UserToPublicUserInfo function to format user data for the response.
- Updated UserService interface and user service implementation to support fetching users by username.
- Enhanced user handler to validate input parameters and check user status before responding.
2026-01-10 03:52:35 +08:00

230 lines
5.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package handler
import (
"carrotskin/internal/errors"
"carrotskin/internal/model"
"carrotskin/internal/types"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
// parseIntWithDefault 将字符串解析为整数,解析失败返回默认值
func parseIntWithDefault(s string, defaultVal int) int {
val, err := strconv.Atoi(s)
if err != nil {
return defaultVal
}
return val
}
// GetUserIDFromContext 从上下文获取用户ID如果不存在返回未授权响应
// 返回值: userID, ok (如果ok为false已经发送了错误响应)
func GetUserIDFromContext(c *gin.Context) (int64, bool) {
userIDValue, exists := c.Get("user_id")
if !exists {
c.JSON(http.StatusUnauthorized, model.NewErrorResponse(
model.CodeUnauthorized,
model.MsgUnauthorized,
nil,
))
return 0, false
}
// 安全的类型断言
userID, ok := userIDValue.(int64)
if !ok {
c.JSON(http.StatusInternalServerError, model.NewErrorResponse(
model.CodeServerError,
"用户ID类型错误",
nil,
))
return 0, false
}
return userID, 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,
}
}
// UserToPublicUserInfo 将 User 模型转换为 PublicUserInfo 响应
func UserToPublicUserInfo(user *model.User) *types.PublicUserInfo {
return &types.PublicUserInfo{
ID: user.ID,
Username: user.Username,
Avatar: user.Avatar,
Points: user.Points,
Role: user.Role,
Status: user.Status,
CreatedAt: user.CreatedAt,
}
}
// 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,
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 {
uploaderUsername := ""
if texture.Uploader != nil {
uploaderUsername = texture.Uploader.Username
}
return &types.TextureInfo{
ID: texture.ID,
UploaderID: texture.UploaderID,
UploaderUsername: uploaderUsername,
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) {
if err == nil {
return
}
// 使用errors.Is检查预定义错误
if errors.Is(err, errors.ErrUserNotFound) ||
errors.Is(err, errors.ErrProfileNotFound) ||
errors.Is(err, errors.ErrTextureNotFound) ||
errors.Is(err, errors.ErrNotFound) {
RespondNotFound(c, err.Error())
return
}
if errors.Is(err, errors.ErrProfileNoPermission) ||
errors.Is(err, errors.ErrTextureNoPermission) ||
errors.Is(err, errors.ErrForbidden) {
RespondForbidden(c, err.Error())
return
}
if errors.Is(err, errors.ErrUnauthorized) ||
errors.Is(err, errors.ErrInvalidToken) ||
errors.Is(err, errors.ErrTokenExpired) {
RespondUnauthorized(c, err.Error())
return
}
// 检查AppError类型
var appErr *errors.AppError
if errors.As(err, &appErr) {
c.JSON(appErr.Code, model.NewErrorResponse(
appErr.Code,
appErr.Message,
appErr.Err,
))
return
}
// 默认返回500错误
RespondServerError(c, err.Error(), err)
}