Files
backend/internal/handler/routes.go

257 lines
8.0 KiB
Go
Raw Normal View History

package handler
import (
"carrotskin/internal/container"
"carrotskin/internal/middleware"
"carrotskin/pkg/auth"
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
)
// Handlers 集中管理所有Handler
type Handlers struct {
2025-12-03 10:58:39 +08:00
Auth *AuthHandler
User *UserHandler
Texture *TextureHandler
Profile *ProfileHandler
Captcha *CaptchaHandler
Yggdrasil *YggdrasilHandler
feat(yggdrasil): 实现好友系统与 profiles 查询接口 按 MinecraftServices 文档实现好友系四组接口与 profiles 服务: 好友系统(/api/yggdrasil/minecraftservices/*) - Friends(1.3):列表查询、ADD/REMOVE 操作(发请求/接受/拒绝/撤回/删除) - 状态枚举+单向记录模型,接受请求时合并两方向避免好友列表重复 - Player Attributes(1.2):friendsPreferences / 脏词过滤 / 聊天偏好读写 - Upsert 用 map 显式写字段规避 gorm 对带 default 零值布尔字段的忽略 - Presence(1.1):Redis key+TTL 上报与好友在线状态批量查询 - Blocklist(1.6):屏蔽列表查询(含 120s Redis 缓存),Block/Unblock 内部方法 Profiles 服务 - getManyByName(2.1):POST /api/profiles/minecraft,返回 [{id,name}] - toLowerCase 规范化、去重、空名过滤、maxBatch=10 超限拒绝 - getByName(2.2):GET /api/users/profiles/minecraft/:name,返回 {id,name},未找到返回 404 路由与基础设施 - 路由按官方 host 前缀一一转发 - api.mojang.com/* -> /api/yggdrasil/api/* - api.minecraftservices.com/* -> /api/yggdrasil/minecraftservices/* - 新增 Friend/PlayerAttributes 模型与 AutoMigrate 注册 - 新增 FriendsService 与 Container 装配 - 抽 extractBearerToken helper,统一 MinecraftServices 错误响应 修复 - texture_service: ToggleFavorite 在 db 为 nil 时降级非事务执行 修复 TestTextureServiceImpl_ToggleFavorite 空指针 panic - texture_service_test: UploadTexture 用例改用真实 SHA-256 命中 mock 修复 4 个子用例因文件大小/Hash 不匹配的失败 - profile_repository: GetByNames/FindByName 改 LOWER() 大小写不敏感 与客户端 toLowerCase 规范化对齐 测试 - 好友模型、ADD/REMOVE/接受、属性、屏蔽、Bearer 解析单测全绿 - profiles 查询规范化、去重、超限、大小写不敏感单测全绿 - go build ./... && go test ./... 全部通过
2026-07-09 20:44:41 +08:00
Friends *YggdrasilFriendsHandler
2025-12-03 10:58:39 +08:00
CustomSkin *CustomSkinHandler
Admin *AdminHandler
}
// NewHandlers 创建所有Handler实例
func NewHandlers(c *container.Container) *Handlers {
return &Handlers{
2025-12-03 10:58:39 +08:00
Auth: NewAuthHandler(c),
User: NewUserHandler(c),
Texture: NewTextureHandler(c),
Profile: NewProfileHandler(c),
Captcha: NewCaptchaHandler(c),
Yggdrasil: NewYggdrasilHandler(c),
feat(yggdrasil): 实现好友系统与 profiles 查询接口 按 MinecraftServices 文档实现好友系四组接口与 profiles 服务: 好友系统(/api/yggdrasil/minecraftservices/*) - Friends(1.3):列表查询、ADD/REMOVE 操作(发请求/接受/拒绝/撤回/删除) - 状态枚举+单向记录模型,接受请求时合并两方向避免好友列表重复 - Player Attributes(1.2):friendsPreferences / 脏词过滤 / 聊天偏好读写 - Upsert 用 map 显式写字段规避 gorm 对带 default 零值布尔字段的忽略 - Presence(1.1):Redis key+TTL 上报与好友在线状态批量查询 - Blocklist(1.6):屏蔽列表查询(含 120s Redis 缓存),Block/Unblock 内部方法 Profiles 服务 - getManyByName(2.1):POST /api/profiles/minecraft,返回 [{id,name}] - toLowerCase 规范化、去重、空名过滤、maxBatch=10 超限拒绝 - getByName(2.2):GET /api/users/profiles/minecraft/:name,返回 {id,name},未找到返回 404 路由与基础设施 - 路由按官方 host 前缀一一转发 - api.mojang.com/* -> /api/yggdrasil/api/* - api.minecraftservices.com/* -> /api/yggdrasil/minecraftservices/* - 新增 Friend/PlayerAttributes 模型与 AutoMigrate 注册 - 新增 FriendsService 与 Container 装配 - 抽 extractBearerToken helper,统一 MinecraftServices 错误响应 修复 - texture_service: ToggleFavorite 在 db 为 nil 时降级非事务执行 修复 TestTextureServiceImpl_ToggleFavorite 空指针 panic - texture_service_test: UploadTexture 用例改用真实 SHA-256 命中 mock 修复 4 个子用例因文件大小/Hash 不匹配的失败 - profile_repository: GetByNames/FindByName 改 LOWER() 大小写不敏感 与客户端 toLowerCase 规范化对齐 测试 - 好友模型、ADD/REMOVE/接受、属性、屏蔽、Bearer 解析单测全绿 - profiles 查询规范化、去重、超限、大小写不敏感单测全绿 - go build ./... && go test ./... 全部通过
2026-07-09 20:44:41 +08:00
Friends: NewYggdrasilFriendsHandler(c),
2025-12-03 10:58:39 +08:00
CustomSkin: NewCustomSkinHandler(c),
Admin: NewAdminHandler(c),
}
}
// RegisterRoutesWithDI 使用依赖注入注册所有路由
func RegisterRoutesWithDI(router *gin.Engine, c *container.Container) {
2025-12-03 10:58:39 +08:00
// 健康检查路由
router.GET("/health", NewHealthCheck(c.DB, c.Redis))
// Swagger文档路由
if c.Config != nil && c.Config.Server.SwaggerEnabled {
router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
}
// 创建Handler实例
h := NewHandlers(c)
// API路由组
apiGroup := router.Group("/api")
v1 := apiGroup.Group("/v1")
{
// 认证路由无需JWT
registerAuthRoutes(v1, h.Auth)
// 用户路由需要JWT认证
registerUserRoutes(v1, h.User, c.JWT)
// 材质路由
registerTextureRoutes(v1, h.Texture, c.JWT)
// 档案路由
registerProfileRoutesWithDI(v1, h.Profile, c.JWT)
// 验证码路由
registerCaptchaRoutesWithDI(v1, h.Captcha)
2025-12-03 10:58:39 +08:00
// CustomSkinAPI 路由
registerCustomSkinRoutes(v1, h.CustomSkin)
// 管理员路由(需要管理员权限)
registerAdminRoutes(v1, c, h.Admin)
}
// Yggdrasil API路由组独立于v1路径为 /api/yggdrasil
feat(yggdrasil): 实现好友系统与 profiles 查询接口 按 MinecraftServices 文档实现好友系四组接口与 profiles 服务: 好友系统(/api/yggdrasil/minecraftservices/*) - Friends(1.3):列表查询、ADD/REMOVE 操作(发请求/接受/拒绝/撤回/删除) - 状态枚举+单向记录模型,接受请求时合并两方向避免好友列表重复 - Player Attributes(1.2):friendsPreferences / 脏词过滤 / 聊天偏好读写 - Upsert 用 map 显式写字段规避 gorm 对带 default 零值布尔字段的忽略 - Presence(1.1):Redis key+TTL 上报与好友在线状态批量查询 - Blocklist(1.6):屏蔽列表查询(含 120s Redis 缓存),Block/Unblock 内部方法 Profiles 服务 - getManyByName(2.1):POST /api/profiles/minecraft,返回 [{id,name}] - toLowerCase 规范化、去重、空名过滤、maxBatch=10 超限拒绝 - getByName(2.2):GET /api/users/profiles/minecraft/:name,返回 {id,name},未找到返回 404 路由与基础设施 - 路由按官方 host 前缀一一转发 - api.mojang.com/* -> /api/yggdrasil/api/* - api.minecraftservices.com/* -> /api/yggdrasil/minecraftservices/* - 新增 Friend/PlayerAttributes 模型与 AutoMigrate 注册 - 新增 FriendsService 与 Container 装配 - 抽 extractBearerToken helper,统一 MinecraftServices 错误响应 修复 - texture_service: ToggleFavorite 在 db 为 nil 时降级非事务执行 修复 TestTextureServiceImpl_ToggleFavorite 空指针 panic - texture_service_test: UploadTexture 用例改用真实 SHA-256 命中 mock 修复 4 个子用例因文件大小/Hash 不匹配的失败 - profile_repository: GetByNames/FindByName 改 LOWER() 大小写不敏感 与客户端 toLowerCase 规范化对齐 测试 - 好友模型、ADD/REMOVE/接受、属性、屏蔽、Bearer 解析单测全绿 - profiles 查询规范化、去重、超限、大小写不敏感单测全绿 - go build ./... && go test ./... 全部通过
2026-07-09 20:44:41 +08:00
registerYggdrasilRoutesWithDI(apiGroup, h)
}
// registerAuthRoutes 注册认证路由
func registerAuthRoutes(v1 *gin.RouterGroup, h *AuthHandler) {
authGroup := v1.Group("/auth")
{
authGroup.POST("/register", h.Register)
authGroup.POST("/login", h.Login)
authGroup.POST("/send-code", h.SendVerificationCode)
authGroup.POST("/reset-password", h.ResetPassword)
}
}
// registerUserRoutes 注册用户路由
func registerUserRoutes(v1 *gin.RouterGroup, h *UserHandler, jwtService *auth.JWTService) {
// 公开用户信息路由(无需认证)
v1.GET("/users/public", h.GetPublicInfo)
// 需要认证的用户路由
userGroup := v1.Group("/user")
userGroup.Use(middleware.AuthMiddleware(jwtService))
{
userGroup.GET("/profile", h.GetProfile)
userGroup.PUT("/profile", h.UpdateProfile)
// 头像相关
userGroup.POST("/avatar/upload", h.UploadAvatar) // 直接上传头像文件
userGroup.PUT("/avatar", h.UpdateAvatar) // 更新头像URL外部URL
// 更换邮箱
userGroup.POST("/change-email", h.ChangeEmail)
// Yggdrasil密码相关
userGroup.POST("/yggdrasil-password/reset", h.ResetYggdrasilPassword)
}
}
// registerTextureRoutes 注册材质路由
func registerTextureRoutes(v1 *gin.RouterGroup, h *TextureHandler, jwtService *auth.JWTService) {
textureGroup := v1.Group("/texture")
{
// 公开路由(无需认证)
textureGroup.GET("", h.Search)
textureGroup.GET("/:id", h.Get)
// 需要认证的路由
textureAuth := textureGroup.Group("")
textureAuth.Use(middleware.AuthMiddleware(jwtService))
{
textureAuth.POST("/upload", h.Upload) // 直接上传文件
textureAuth.PUT("/:id", h.Update)
textureAuth.DELETE("/:id", h.Delete)
textureAuth.POST("/:id/favorite", h.ToggleFavorite)
textureAuth.GET("/my", h.GetUserTextures)
textureAuth.GET("/favorites", h.GetUserFavorites)
}
}
}
// registerProfileRoutesWithDI 注册档案路由(依赖注入版本)
func registerProfileRoutesWithDI(v1 *gin.RouterGroup, h *ProfileHandler, jwtService *auth.JWTService) {
profileGroup := v1.Group("/profile")
{
// 公开路由(无需认证)
profileGroup.GET("/:uuid", h.Get)
// 需要认证的路由
profileAuth := profileGroup.Group("")
profileAuth.Use(middleware.AuthMiddleware(jwtService))
{
// 同时支持 /api/v1/profile 和 /api/v1/profile/ 两种形式返回列表与创建
profileAuth.GET("", h.List)
profileAuth.POST("", h.Create)
profileAuth.POST("/", h.Create)
profileAuth.GET("/", h.List)
profileAuth.PUT("/:uuid", h.Update)
profileAuth.DELETE("/:uuid", h.Delete)
}
}
}
// registerCaptchaRoutesWithDI 注册验证码路由(依赖注入版本)
func registerCaptchaRoutesWithDI(v1 *gin.RouterGroup, h *CaptchaHandler) {
captchaGroup := v1.Group("/captcha")
{
captchaGroup.GET("/generate", h.Generate)
captchaGroup.POST("/verify", h.Verify)
}
}
// registerYggdrasilRoutesWithDI 注册Yggdrasil API路由依赖注入版本
feat(yggdrasil): 实现好友系统与 profiles 查询接口 按 MinecraftServices 文档实现好友系四组接口与 profiles 服务: 好友系统(/api/yggdrasil/minecraftservices/*) - Friends(1.3):列表查询、ADD/REMOVE 操作(发请求/接受/拒绝/撤回/删除) - 状态枚举+单向记录模型,接受请求时合并两方向避免好友列表重复 - Player Attributes(1.2):friendsPreferences / 脏词过滤 / 聊天偏好读写 - Upsert 用 map 显式写字段规避 gorm 对带 default 零值布尔字段的忽略 - Presence(1.1):Redis key+TTL 上报与好友在线状态批量查询 - Blocklist(1.6):屏蔽列表查询(含 120s Redis 缓存),Block/Unblock 内部方法 Profiles 服务 - getManyByName(2.1):POST /api/profiles/minecraft,返回 [{id,name}] - toLowerCase 规范化、去重、空名过滤、maxBatch=10 超限拒绝 - getByName(2.2):GET /api/users/profiles/minecraft/:name,返回 {id,name},未找到返回 404 路由与基础设施 - 路由按官方 host 前缀一一转发 - api.mojang.com/* -> /api/yggdrasil/api/* - api.minecraftservices.com/* -> /api/yggdrasil/minecraftservices/* - 新增 Friend/PlayerAttributes 模型与 AutoMigrate 注册 - 新增 FriendsService 与 Container 装配 - 抽 extractBearerToken helper,统一 MinecraftServices 错误响应 修复 - texture_service: ToggleFavorite 在 db 为 nil 时降级非事务执行 修复 TestTextureServiceImpl_ToggleFavorite 空指针 panic - texture_service_test: UploadTexture 用例改用真实 SHA-256 命中 mock 修复 4 个子用例因文件大小/Hash 不匹配的失败 - profile_repository: GetByNames/FindByName 改 LOWER() 大小写不敏感 与客户端 toLowerCase 规范化对齐 测试 - 好友模型、ADD/REMOVE/接受、属性、屏蔽、Bearer 解析单测全绿 - profiles 查询规范化、去重、超限、大小写不敏感单测全绿 - go build ./... && go test ./... 全部通过
2026-07-09 20:44:41 +08:00
func registerYggdrasilRoutesWithDI(v1 *gin.RouterGroup, h *Handlers) {
ygg := v1.Group("/yggdrasil")
{
feat(yggdrasil): 实现好友系统与 profiles 查询接口 按 MinecraftServices 文档实现好友系四组接口与 profiles 服务: 好友系统(/api/yggdrasil/minecraftservices/*) - Friends(1.3):列表查询、ADD/REMOVE 操作(发请求/接受/拒绝/撤回/删除) - 状态枚举+单向记录模型,接受请求时合并两方向避免好友列表重复 - Player Attributes(1.2):friendsPreferences / 脏词过滤 / 聊天偏好读写 - Upsert 用 map 显式写字段规避 gorm 对带 default 零值布尔字段的忽略 - Presence(1.1):Redis key+TTL 上报与好友在线状态批量查询 - Blocklist(1.6):屏蔽列表查询(含 120s Redis 缓存),Block/Unblock 内部方法 Profiles 服务 - getManyByName(2.1):POST /api/profiles/minecraft,返回 [{id,name}] - toLowerCase 规范化、去重、空名过滤、maxBatch=10 超限拒绝 - getByName(2.2):GET /api/users/profiles/minecraft/:name,返回 {id,name},未找到返回 404 路由与基础设施 - 路由按官方 host 前缀一一转发 - api.mojang.com/* -> /api/yggdrasil/api/* - api.minecraftservices.com/* -> /api/yggdrasil/minecraftservices/* - 新增 Friend/PlayerAttributes 模型与 AutoMigrate 注册 - 新增 FriendsService 与 Container 装配 - 抽 extractBearerToken helper,统一 MinecraftServices 错误响应 修复 - texture_service: ToggleFavorite 在 db 为 nil 时降级非事务执行 修复 TestTextureServiceImpl_ToggleFavorite 空指针 panic - texture_service_test: UploadTexture 用例改用真实 SHA-256 命中 mock 修复 4 个子用例因文件大小/Hash 不匹配的失败 - profile_repository: GetByNames/FindByName 改 LOWER() 大小写不敏感 与客户端 toLowerCase 规范化对齐 测试 - 好友模型、ADD/REMOVE/接受、属性、屏蔽、Bearer 解析单测全绿 - profiles 查询规范化、去重、超限、大小写不敏感单测全绿 - go build ./... && go test ./... 全部通过
2026-07-09 20:44:41 +08:00
ygg.GET("", h.Yggdrasil.GetMetaData)
ygg.POST("/minecraftservices/player/certificates", h.Yggdrasil.GetPlayerCertificates)
// MinecraftServices 好友系接口(文档 1.1~1.3、1.6
mcServices := ygg.Group("/minecraftservices")
{
mcServices.GET("/friends", h.Friends.GetFriends)
mcServices.PUT("/friends", h.Friends.UpdateFriends)
mcServices.GET("/player/attributes", h.Friends.GetAttributes)
mcServices.POST("/player/attributes", h.Friends.UpdateAttributes)
mcServices.POST("/presence", h.Friends.UpdatePresence)
mcServices.GET("/privacy/blocklist", h.Friends.GetBlocklist)
// MC 1.21.7 /whitelist add 查询单用户名 UUIDauthlib-injector issue #277
mcServices.GET("/minecraft/profile/lookup/name/:name", h.Yggdrasil.LookupProfileByName)
feat(yggdrasil): 实现好友系统与 profiles 查询接口 按 MinecraftServices 文档实现好友系四组接口与 profiles 服务: 好友系统(/api/yggdrasil/minecraftservices/*) - Friends(1.3):列表查询、ADD/REMOVE 操作(发请求/接受/拒绝/撤回/删除) - 状态枚举+单向记录模型,接受请求时合并两方向避免好友列表重复 - Player Attributes(1.2):friendsPreferences / 脏词过滤 / 聊天偏好读写 - Upsert 用 map 显式写字段规避 gorm 对带 default 零值布尔字段的忽略 - Presence(1.1):Redis key+TTL 上报与好友在线状态批量查询 - Blocklist(1.6):屏蔽列表查询(含 120s Redis 缓存),Block/Unblock 内部方法 Profiles 服务 - getManyByName(2.1):POST /api/profiles/minecraft,返回 [{id,name}] - toLowerCase 规范化、去重、空名过滤、maxBatch=10 超限拒绝 - getByName(2.2):GET /api/users/profiles/minecraft/:name,返回 {id,name},未找到返回 404 路由与基础设施 - 路由按官方 host 前缀一一转发 - api.mojang.com/* -> /api/yggdrasil/api/* - api.minecraftservices.com/* -> /api/yggdrasil/minecraftservices/* - 新增 Friend/PlayerAttributes 模型与 AutoMigrate 注册 - 新增 FriendsService 与 Container 装配 - 抽 extractBearerToken helper,统一 MinecraftServices 错误响应 修复 - texture_service: ToggleFavorite 在 db 为 nil 时降级非事务执行 修复 TestTextureServiceImpl_ToggleFavorite 空指针 panic - texture_service_test: UploadTexture 用例改用真实 SHA-256 命中 mock 修复 4 个子用例因文件大小/Hash 不匹配的失败 - profile_repository: GetByNames/FindByName 改 LOWER() 大小写不敏感 与客户端 toLowerCase 规范化对齐 测试 - 好友模型、ADD/REMOVE/接受、属性、屏蔽、Bearer 解析单测全绿 - profiles 查询规范化、去重、超限、大小写不敏感单测全绿 - go build ./... && go test ./... 全部通过
2026-07-09 20:44:41 +08:00
}
authserver := ygg.Group("/authserver")
{
feat(yggdrasil): 实现好友系统与 profiles 查询接口 按 MinecraftServices 文档实现好友系四组接口与 profiles 服务: 好友系统(/api/yggdrasil/minecraftservices/*) - Friends(1.3):列表查询、ADD/REMOVE 操作(发请求/接受/拒绝/撤回/删除) - 状态枚举+单向记录模型,接受请求时合并两方向避免好友列表重复 - Player Attributes(1.2):friendsPreferences / 脏词过滤 / 聊天偏好读写 - Upsert 用 map 显式写字段规避 gorm 对带 default 零值布尔字段的忽略 - Presence(1.1):Redis key+TTL 上报与好友在线状态批量查询 - Blocklist(1.6):屏蔽列表查询(含 120s Redis 缓存),Block/Unblock 内部方法 Profiles 服务 - getManyByName(2.1):POST /api/profiles/minecraft,返回 [{id,name}] - toLowerCase 规范化、去重、空名过滤、maxBatch=10 超限拒绝 - getByName(2.2):GET /api/users/profiles/minecraft/:name,返回 {id,name},未找到返回 404 路由与基础设施 - 路由按官方 host 前缀一一转发 - api.mojang.com/* -> /api/yggdrasil/api/* - api.minecraftservices.com/* -> /api/yggdrasil/minecraftservices/* - 新增 Friend/PlayerAttributes 模型与 AutoMigrate 注册 - 新增 FriendsService 与 Container 装配 - 抽 extractBearerToken helper,统一 MinecraftServices 错误响应 修复 - texture_service: ToggleFavorite 在 db 为 nil 时降级非事务执行 修复 TestTextureServiceImpl_ToggleFavorite 空指针 panic - texture_service_test: UploadTexture 用例改用真实 SHA-256 命中 mock 修复 4 个子用例因文件大小/Hash 不匹配的失败 - profile_repository: GetByNames/FindByName 改 LOWER() 大小写不敏感 与客户端 toLowerCase 规范化对齐 测试 - 好友模型、ADD/REMOVE/接受、属性、屏蔽、Bearer 解析单测全绿 - profiles 查询规范化、去重、超限、大小写不敏感单测全绿 - go build ./... && go test ./... 全部通过
2026-07-09 20:44:41 +08:00
authserver.POST("/authenticate", h.Yggdrasil.Authenticate)
authserver.POST("/validate", h.Yggdrasil.ValidToken)
authserver.POST("/refresh", h.Yggdrasil.RefreshToken)
authserver.POST("/invalidate", h.Yggdrasil.InvalidToken)
authserver.POST("/signout", h.Yggdrasil.SignOut)
}
sessionServer := ygg.Group("/sessionserver")
{
feat(yggdrasil): 实现好友系统与 profiles 查询接口 按 MinecraftServices 文档实现好友系四组接口与 profiles 服务: 好友系统(/api/yggdrasil/minecraftservices/*) - Friends(1.3):列表查询、ADD/REMOVE 操作(发请求/接受/拒绝/撤回/删除) - 状态枚举+单向记录模型,接受请求时合并两方向避免好友列表重复 - Player Attributes(1.2):friendsPreferences / 脏词过滤 / 聊天偏好读写 - Upsert 用 map 显式写字段规避 gorm 对带 default 零值布尔字段的忽略 - Presence(1.1):Redis key+TTL 上报与好友在线状态批量查询 - Blocklist(1.6):屏蔽列表查询(含 120s Redis 缓存),Block/Unblock 内部方法 Profiles 服务 - getManyByName(2.1):POST /api/profiles/minecraft,返回 [{id,name}] - toLowerCase 规范化、去重、空名过滤、maxBatch=10 超限拒绝 - getByName(2.2):GET /api/users/profiles/minecraft/:name,返回 {id,name},未找到返回 404 路由与基础设施 - 路由按官方 host 前缀一一转发 - api.mojang.com/* -> /api/yggdrasil/api/* - api.minecraftservices.com/* -> /api/yggdrasil/minecraftservices/* - 新增 Friend/PlayerAttributes 模型与 AutoMigrate 注册 - 新增 FriendsService 与 Container 装配 - 抽 extractBearerToken helper,统一 MinecraftServices 错误响应 修复 - texture_service: ToggleFavorite 在 db 为 nil 时降级非事务执行 修复 TestTextureServiceImpl_ToggleFavorite 空指针 panic - texture_service_test: UploadTexture 用例改用真实 SHA-256 命中 mock 修复 4 个子用例因文件大小/Hash 不匹配的失败 - profile_repository: GetByNames/FindByName 改 LOWER() 大小写不敏感 与客户端 toLowerCase 规范化对齐 测试 - 好友模型、ADD/REMOVE/接受、属性、屏蔽、Bearer 解析单测全绿 - profiles 查询规范化、去重、超限、大小写不敏感单测全绿 - go build ./... && go test ./... 全部通过
2026-07-09 20:44:41 +08:00
sessionServer.GET("/session/minecraft/profile/:uuid", h.Yggdrasil.GetProfileByUUID)
sessionServer.POST("/session/minecraft/join", h.Yggdrasil.JoinServer)
sessionServer.GET("/session/minecraft/hasJoined", h.Yggdrasil.HasJoinedServer)
}
api := ygg.Group("/api")
{
feat(yggdrasil): 实现好友系统与 profiles 查询接口 按 MinecraftServices 文档实现好友系四组接口与 profiles 服务: 好友系统(/api/yggdrasil/minecraftservices/*) - Friends(1.3):列表查询、ADD/REMOVE 操作(发请求/接受/拒绝/撤回/删除) - 状态枚举+单向记录模型,接受请求时合并两方向避免好友列表重复 - Player Attributes(1.2):friendsPreferences / 脏词过滤 / 聊天偏好读写 - Upsert 用 map 显式写字段规避 gorm 对带 default 零值布尔字段的忽略 - Presence(1.1):Redis key+TTL 上报与好友在线状态批量查询 - Blocklist(1.6):屏蔽列表查询(含 120s Redis 缓存),Block/Unblock 内部方法 Profiles 服务 - getManyByName(2.1):POST /api/profiles/minecraft,返回 [{id,name}] - toLowerCase 规范化、去重、空名过滤、maxBatch=10 超限拒绝 - getByName(2.2):GET /api/users/profiles/minecraft/:name,返回 {id,name},未找到返回 404 路由与基础设施 - 路由按官方 host 前缀一一转发 - api.mojang.com/* -> /api/yggdrasil/api/* - api.minecraftservices.com/* -> /api/yggdrasil/minecraftservices/* - 新增 Friend/PlayerAttributes 模型与 AutoMigrate 注册 - 新增 FriendsService 与 Container 装配 - 抽 extractBearerToken helper,统一 MinecraftServices 错误响应 修复 - texture_service: ToggleFavorite 在 db 为 nil 时降级非事务执行 修复 TestTextureServiceImpl_ToggleFavorite 空指针 panic - texture_service_test: UploadTexture 用例改用真实 SHA-256 命中 mock 修复 4 个子用例因文件大小/Hash 不匹配的失败 - profile_repository: GetByNames/FindByName 改 LOWER() 大小写不敏感 与客户端 toLowerCase 规范化对齐 测试 - 好友模型、ADD/REMOVE/接受、属性、屏蔽、Bearer 解析单测全绿 - profiles 查询规范化、去重、超限、大小写不敏感单测全绿 - go build ./... && go test ./... 全部通过
2026-07-09 20:44:41 +08:00
// 对应官方 api.mojang.com/profiles/minecraft文档 2.1:批量按用户名查档)
api.POST("/profiles/minecraft", h.Yggdrasil.GetProfilesByName)
// 对应官方 api.mojang.com/users/profiles/minecraft/{name}(文档 2.2:单个用户名查档)
api.GET("/users/profiles/minecraft/:name", h.Yggdrasil.GetProfileByName)
}
}
}
// registerAdminRoutes 注册管理员路由
func registerAdminRoutes(v1 *gin.RouterGroup, c *container.Container, h *AdminHandler) {
admin := v1.Group("/admin")
admin.Use(middleware.AuthMiddleware(c.JWT))
admin.Use(middleware.RequireAdmin())
{
// 用户管理
admin.GET("/users", h.GetUserList)
admin.GET("/users/:id", h.GetUserDetail)
admin.PUT("/users/role", h.SetUserRole)
admin.PUT("/users/status", h.SetUserStatus)
// 材质管理(审核)
admin.GET("/textures", h.GetTextureList)
admin.DELETE("/textures/:id", h.DeleteTexture)
// 权限管理
admin.GET("/permissions", h.GetPermissions)
}
}
2025-12-03 10:58:39 +08:00
// registerCustomSkinRoutes 注册CustomSkinAPI路由
// CustomSkinAPI 协议要求根地址必须以 / 结尾
// 路由格式:
// - {ROOT}/{USERNAME}.json - 获取玩家信息
// - {ROOT}/textures/{hash} - 获取资源文件
//
// 根路径为 /api/v1/csl/
func registerCustomSkinRoutes(v1 *gin.RouterGroup, h *CustomSkinHandler) {
// CustomSkinAPI 路由组
csl := v1.Group("/csl")
{
// 获取玩家信息: {ROOT}/{USERNAME}.json
csl.GET("/:username", h.GetPlayerInfo)
// 获取资源文件: {ROOT}/textures/{hash}
csl.GET("/textures/:hash", h.GetTexture)
}
}