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.
This commit is contained in:
@@ -32,6 +32,8 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
_ "carrotskin/docs"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
@@ -62,6 +62,19 @@ func UserToUserInfo(user *model.User) *types.UserInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 响应
|
// ProfileToProfileInfo 将 Profile 模型转换为 ProfileInfo 响应
|
||||||
func ProfileToProfileInfo(profile *model.Profile) *types.ProfileInfo {
|
func ProfileToProfileInfo(profile *model.Profile) *types.ProfileInfo {
|
||||||
return &types.ProfileInfo{
|
return &types.ProfileInfo{
|
||||||
|
|||||||
@@ -93,6 +93,10 @@ func registerAuthRoutes(v1 *gin.RouterGroup, h *AuthHandler) {
|
|||||||
|
|
||||||
// registerUserRoutes 注册用户路由
|
// registerUserRoutes 注册用户路由
|
||||||
func registerUserRoutes(v1 *gin.RouterGroup, h *UserHandler, jwtService *auth.JWTService) {
|
func registerUserRoutes(v1 *gin.RouterGroup, h *UserHandler, jwtService *auth.JWTService) {
|
||||||
|
// 公开用户信息路由(无需认证)
|
||||||
|
v1.GET("/users/public", h.GetPublicInfo)
|
||||||
|
|
||||||
|
// 需要认证的用户路由
|
||||||
userGroup := v1.Group("/user")
|
userGroup := v1.Group("/user")
|
||||||
userGroup.Use(middleware.AuthMiddleware(jwtService))
|
userGroup.Use(middleware.AuthMiddleware(jwtService))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package handler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"carrotskin/internal/container"
|
"carrotskin/internal/container"
|
||||||
|
"carrotskin/internal/model"
|
||||||
"carrotskin/internal/service"
|
"carrotskin/internal/service"
|
||||||
"carrotskin/internal/types"
|
"carrotskin/internal/types"
|
||||||
|
|
||||||
@@ -315,3 +316,55 @@ func (h *UserHandler) ResetYggdrasilPassword(c *gin.Context) {
|
|||||||
h.logger.Info("Yggdrasil密码重置成功", zap.Int64("userId", userID))
|
h.logger.Info("Yggdrasil密码重置成功", zap.Int64("userId", userID))
|
||||||
RespondSuccess(c, gin.H{"password": newPassword})
|
RespondSuccess(c, gin.H{"password": newPassword})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPublicInfo 获取用户公开信息
|
||||||
|
// @Summary 获取用户公开信息
|
||||||
|
// @Description 根据用户名或用户ID获取用户的公开信息(不包含敏感信息如邮箱)
|
||||||
|
// @Tags user
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param username query string false "用户名"
|
||||||
|
// @Param id query int false "用户ID"
|
||||||
|
// @Success 200 {object} model.Response{data=types.PublicUserInfo} "获取成功"
|
||||||
|
// @Failure 400 {object} model.ErrorResponse "参数错误"
|
||||||
|
// @Failure 404 {object} model.ErrorResponse "用户不存在"
|
||||||
|
// @Router /api/v1/users/public [get]
|
||||||
|
func (h *UserHandler) GetPublicInfo(c *gin.Context) {
|
||||||
|
username := c.Query("username")
|
||||||
|
idStr := c.Query("id")
|
||||||
|
|
||||||
|
// 至少需要提供一个参数
|
||||||
|
if username == "" && idStr == "" {
|
||||||
|
RespondBadRequest(c, "必须提供用户名或用户ID", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var user *model.User
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// 优先使用用户名查询
|
||||||
|
if username != "" {
|
||||||
|
user, err = h.container.UserService.GetByUsername(c.Request.Context(), username)
|
||||||
|
} else {
|
||||||
|
// 使用用户ID查询
|
||||||
|
id := parseIntWithDefault(idStr, 0)
|
||||||
|
if id == 0 {
|
||||||
|
RespondBadRequest(c, "无效的用户ID", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user, err = h.container.UserService.GetByID(c.Request.Context(), int64(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil || user == nil {
|
||||||
|
RespondNotFound(c, "用户不存在")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查用户状态
|
||||||
|
if user.Status != 1 {
|
||||||
|
RespondNotFound(c, "用户不可用")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
RespondSuccess(c, UserToPublicUserInfo(user))
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ type UserService interface {
|
|||||||
// 用户查询
|
// 用户查询
|
||||||
GetByID(ctx context.Context, id int64) (*model.User, error)
|
GetByID(ctx context.Context, id int64) (*model.User, error)
|
||||||
GetByEmail(ctx context.Context, email string) (*model.User, error)
|
GetByEmail(ctx context.Context, email string) (*model.User, error)
|
||||||
|
GetByUsername(ctx context.Context, username string) (*model.User, error)
|
||||||
|
|
||||||
// 用户更新
|
// 用户更新
|
||||||
UpdateInfo(ctx context.Context, user *model.User) error
|
UpdateInfo(ctx context.Context, user *model.User) error
|
||||||
|
|||||||
@@ -199,6 +199,14 @@ func (s *userService) GetByEmail(ctx context.Context, email string) (*model.User
|
|||||||
}, s.cache.Policy.UserEmailTTL)
|
}, s.cache.Policy.UserEmailTTL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *userService) GetByUsername(ctx context.Context, username string) (*model.User, error) {
|
||||||
|
// 使用 Cached 装饰器自动处理缓存
|
||||||
|
cacheKey := s.cacheKeys.UserByUsername(username)
|
||||||
|
return database.Cached(ctx, s.cache, cacheKey, func() (*model.User, error) {
|
||||||
|
return s.userRepo.FindByUsername(ctx, username)
|
||||||
|
}, s.cache.Policy.UserTTL)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *userService) UpdateInfo(ctx context.Context, user *model.User) error {
|
func (s *userService) UpdateInfo(ctx context.Context, user *model.User) error {
|
||||||
err := s.userRepo.Update(ctx, user)
|
err := s.userRepo.Update(ctx, user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -110,6 +110,18 @@ type UserInfo struct {
|
|||||||
UpdatedAt time.Time `json:"updated_at" example:"2025-10-01T10:00:00Z"`
|
UpdatedAt time.Time `json:"updated_at" example:"2025-10-01T10:00:00Z"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PublicUserInfo 用户公开信息
|
||||||
|
// @Description 用户公开信息(不包含敏感信息如邮箱)
|
||||||
|
type PublicUserInfo struct {
|
||||||
|
ID int64 `json:"id" example:"1"`
|
||||||
|
Username string `json:"username" example:"testuser"`
|
||||||
|
Avatar string `json:"avatar" example:"https://example.com/avatar.png"`
|
||||||
|
Points int `json:"points" example:"100"`
|
||||||
|
Role string `json:"role" example:"user"`
|
||||||
|
Status int16 `json:"status" example:"1"`
|
||||||
|
CreatedAt time.Time `json:"created_at" example:"2025-10-01T10:00:00Z"`
|
||||||
|
}
|
||||||
|
|
||||||
// TextureType 材质类型
|
// TextureType 材质类型
|
||||||
type TextureType string
|
type TextureType string
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user