完善服务端材质渲染(未测试),删除profile表中不必要的isActive字段及相关接口
This commit is contained in:
@@ -35,16 +35,17 @@ type Container struct {
|
||||
YggdrasilRepo repository.YggdrasilRepository
|
||||
|
||||
// Service层
|
||||
UserService service.UserService
|
||||
ProfileService service.ProfileService
|
||||
TextureService service.TextureService
|
||||
TokenService service.TokenService
|
||||
YggdrasilService service.YggdrasilService
|
||||
VerificationService service.VerificationService
|
||||
UploadService service.UploadService
|
||||
SecurityService service.SecurityService
|
||||
CaptchaService service.CaptchaService
|
||||
SignatureService *service.SignatureService
|
||||
UserService service.UserService
|
||||
ProfileService service.ProfileService
|
||||
TextureService service.TextureService
|
||||
TokenService service.TokenService
|
||||
YggdrasilService service.YggdrasilService
|
||||
VerificationService service.VerificationService
|
||||
UploadService service.UploadService
|
||||
SecurityService service.SecurityService
|
||||
CaptchaService service.CaptchaService
|
||||
SignatureService *service.SignatureService
|
||||
TextureRenderService service.TextureRenderService
|
||||
}
|
||||
|
||||
// NewContainer 创建依赖容器
|
||||
@@ -89,6 +90,7 @@ func NewContainer(
|
||||
c.UserService = service.NewUserService(c.UserRepo, c.ConfigRepo, jwtService, redisClient, cacheManager, logger)
|
||||
c.ProfileService = service.NewProfileService(c.ProfileRepo, c.UserRepo, cacheManager, logger)
|
||||
c.TextureService = service.NewTextureService(c.TextureRepo, c.UserRepo, storageClient, cacheManager, logger)
|
||||
c.TextureRenderService = service.NewTextureRenderService(c.TextureRepo, storageClient, cacheManager, logger)
|
||||
|
||||
// 获取Yggdrasil私钥并创建JWT服务(TokenService需要)
|
||||
// 注意:这里仍然需要预先初始化,因为TokenService在创建时需要YggdrasilJWT
|
||||
|
||||
@@ -70,7 +70,6 @@ func ProfileToProfileInfo(profile *model.Profile) *types.ProfileInfo {
|
||||
Name: profile.Name,
|
||||
SkinID: profile.SkinID,
|
||||
CapeID: profile.CapeID,
|
||||
IsActive: profile.IsActive,
|
||||
LastUsedAt: profile.LastUsedAt,
|
||||
CreatedAt: profile.CreatedAt,
|
||||
UpdatedAt: profile.UpdatedAt,
|
||||
@@ -173,24 +172,24 @@ func RespondWithError(c *gin.Context, err error) {
|
||||
}
|
||||
|
||||
// 使用errors.Is检查预定义错误
|
||||
if errors.Is(err, errors.ErrUserNotFound) ||
|
||||
errors.Is(err, errors.ErrProfileNotFound) ||
|
||||
errors.Is(err, errors.ErrTextureNotFound) ||
|
||||
errors.Is(err, errors.ErrNotFound) {
|
||||
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) {
|
||||
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) {
|
||||
if errors.Is(err, errors.ErrUnauthorized) ||
|
||||
errors.Is(err, errors.ErrInvalidToken) ||
|
||||
errors.Is(err, errors.ErrTokenExpired) {
|
||||
RespondUnauthorized(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -207,39 +207,3 @@ func (h *ProfileHandler) Delete(c *gin.Context) {
|
||||
|
||||
RespondSuccess(c, gin.H{"message": "删除成功"})
|
||||
}
|
||||
|
||||
// SetActive 设置活跃档案
|
||||
// @Summary 设置活跃档案
|
||||
// @Description 将指定档案设置为活跃状态
|
||||
// @Tags profile
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param uuid path string true "档案UUID"
|
||||
// @Success 200 {object} model.Response "设置成功"
|
||||
// @Failure 403 {object} model.ErrorResponse "无权操作"
|
||||
// @Router /api/v1/profile/{uuid}/activate [post]
|
||||
func (h *ProfileHandler) SetActive(c *gin.Context) {
|
||||
userID, ok := GetUserIDFromContext(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
uuid := c.Param("uuid")
|
||||
if uuid == "" {
|
||||
RespondBadRequest(c, "UUID不能为空", nil)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.container.ProfileService.SetActive(c.Request.Context(), uuid, userID); err != nil {
|
||||
h.logger.Error("设置活跃档案失败",
|
||||
zap.String("uuid", uuid),
|
||||
zap.Int64("user_id", userID),
|
||||
zap.Error(err),
|
||||
)
|
||||
RespondWithError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
RespondSuccess(c, gin.H{"message": "设置成功"})
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"carrotskin/pkg/auth"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
swaggerFiles "github.com/swaggo/files"
|
||||
ginSwagger "github.com/swaggo/gin-swagger"
|
||||
)
|
||||
|
||||
// Handlers 集中管理所有Handler
|
||||
@@ -38,6 +40,9 @@ func RegisterRoutesWithDI(router *gin.Engine, c *container.Container) {
|
||||
// 健康检查路由
|
||||
router.GET("/health", HealthCheck)
|
||||
|
||||
// Swagger文档路由
|
||||
router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
||||
|
||||
// 创建Handler实例
|
||||
h := NewHandlers(c)
|
||||
|
||||
@@ -147,7 +152,6 @@ func registerProfileRoutesWithDI(v1 *gin.RouterGroup, h *ProfileHandler, jwtServ
|
||||
profileAuth.GET("/", h.List)
|
||||
profileAuth.PUT("/:uuid", h.Update)
|
||||
profileAuth.DELETE("/:uuid", h.Delete)
|
||||
profileAuth.POST("/:uuid/activate", h.SetActive)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,11 @@ import (
|
||||
// Profile Minecraft 档案模型
|
||||
type Profile struct {
|
||||
UUID string `gorm:"column:uuid;type:varchar(36);primaryKey" json:"uuid"`
|
||||
UserID int64 `gorm:"column:user_id;not null;index:idx_profiles_user_created,priority:1;index:idx_profiles_user_active,priority:1" json:"user_id"`
|
||||
UserID int64 `gorm:"column:user_id;not null;index:idx_profiles_user_created,priority:1" json:"user_id"`
|
||||
Name string `gorm:"column:name;type:varchar(16);not null;uniqueIndex:idx_profiles_name" json:"name"` // Minecraft 角色名
|
||||
SkinID *int64 `gorm:"column:skin_id;type:bigint;index:idx_profiles_skin_id" json:"skin_id,omitempty"`
|
||||
CapeID *int64 `gorm:"column:cape_id;type:bigint;index:idx_profiles_cape_id" json:"cape_id,omitempty"`
|
||||
RSAPrivateKey string `gorm:"column:rsa_private_key;type:text;not null" json:"-"` // RSA 私钥不返回给前端
|
||||
IsActive bool `gorm:"column:is_active;not null;default:true;index:idx_profiles_user_active,priority:2" json:"is_active"`
|
||||
LastUsedAt *time.Time `gorm:"column:last_used_at;type:timestamp;index:idx_profiles_last_used,sort:desc" json:"last_used_at,omitempty"`
|
||||
CreatedAt time.Time `gorm:"column:created_at;type:timestamp;not null;default:CURRENT_TIMESTAMP;index:idx_profiles_user_created,priority:2,sort:desc" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at;type:timestamp;not null;default:CURRENT_TIMESTAMP" json:"updated_at"`
|
||||
@@ -33,7 +32,6 @@ type ProfileResponse struct {
|
||||
UUID string `json:"uuid"`
|
||||
Name string `json:"name"`
|
||||
Textures ProfileTexturesData `json:"textures"`
|
||||
IsActive bool `json:"is_active"`
|
||||
LastUsedAt *time.Time `json:"last_used_at,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ type ProfileRepository interface {
|
||||
Delete(ctx context.Context, uuid string) error
|
||||
BatchDelete(ctx context.Context, uuids []string) (int64, error) // 批量删除
|
||||
CountByUserID(ctx context.Context, userID int64) (int64, error)
|
||||
SetActive(ctx context.Context, uuid string, userID int64) error
|
||||
UpdateLastUsedAt(ctx context.Context, uuid string) error
|
||||
GetByNames(ctx context.Context, names []string) ([]*model.Profile, error)
|
||||
GetKeyPair(ctx context.Context, profileId string) (*model.KeyPair, error)
|
||||
|
||||
@@ -109,20 +109,6 @@ func (r *profileRepository) CountByUserID(ctx context.Context, userID int64) (in
|
||||
return count, err
|
||||
}
|
||||
|
||||
func (r *profileRepository) SetActive(ctx context.Context, uuid string, userID int64) error {
|
||||
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Model(&model.Profile{}).
|
||||
Where("user_id = ?", userID).
|
||||
Update("is_active", false).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return tx.Model(&model.Profile{}).
|
||||
Where("uuid = ? AND user_id = ?", uuid, userID).
|
||||
Update("is_active", true).Error
|
||||
})
|
||||
}
|
||||
|
||||
func (r *profileRepository) UpdateLastUsedAt(ctx context.Context, uuid string) error {
|
||||
return r.db.WithContext(ctx).Model(&model.Profile{}).
|
||||
Where("uuid = ?", uuid).
|
||||
|
||||
@@ -42,41 +42,6 @@ func TestProfileRepository_QueryConditions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestProfileRepository_SetActiveLogic 测试设置活跃档案的逻辑
|
||||
func TestProfileRepository_SetActiveLogic(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
uuid string
|
||||
userID int64
|
||||
otherProfiles int
|
||||
wantAllInactive bool
|
||||
}{
|
||||
{
|
||||
name: "设置一个档案为活跃,其他应该变为非活跃",
|
||||
uuid: "profile-1",
|
||||
userID: 1,
|
||||
otherProfiles: 2,
|
||||
wantAllInactive: true,
|
||||
},
|
||||
{
|
||||
name: "只有一个档案时",
|
||||
uuid: "profile-1",
|
||||
userID: 1,
|
||||
otherProfiles: 0,
|
||||
wantAllInactive: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// 验证逻辑:设置一个档案为活跃时,应该先将所有档案设为非活跃
|
||||
if !tt.wantAllInactive {
|
||||
t.Error("Setting active profile should first set all profiles to inactive")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestProfileRepository_CountLogic 测试统计逻辑
|
||||
func TestProfileRepository_CountLogic(t *testing.T) {
|
||||
tests := []struct {
|
||||
@@ -109,30 +74,30 @@ func TestProfileRepository_CountLogic(t *testing.T) {
|
||||
// TestProfileRepository_UpdateFieldsLogic 测试更新字段逻辑
|
||||
func TestProfileRepository_UpdateFieldsLogic(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
uuid string
|
||||
updates map[string]interface{}
|
||||
name string
|
||||
uuid string
|
||||
updates map[string]interface{}
|
||||
wantValid bool
|
||||
}{
|
||||
{
|
||||
name: "有效的更新",
|
||||
uuid: "123e4567-e89b-12d3-a456-426614174000",
|
||||
updates: map[string]interface{}{
|
||||
"name": "NewName",
|
||||
"name": "NewName",
|
||||
"skin_id": int64(1),
|
||||
},
|
||||
wantValid: true,
|
||||
},
|
||||
{
|
||||
name: "UUID为空",
|
||||
uuid: "",
|
||||
updates: map[string]interface{}{"name": "NewName"},
|
||||
name: "UUID为空",
|
||||
uuid: "",
|
||||
updates: map[string]interface{}{"name": "NewName"},
|
||||
wantValid: false,
|
||||
},
|
||||
{
|
||||
name: "更新字段为空",
|
||||
uuid: "123e4567-e89b-12d3-a456-426614174000",
|
||||
updates: map[string]interface{}{},
|
||||
name: "更新字段为空",
|
||||
uuid: "123e4567-e89b-12d3-a456-426614174000",
|
||||
updates: map[string]interface{}{},
|
||||
wantValid: true, // 空更新也是有效的,只是不会更新任何字段
|
||||
},
|
||||
}
|
||||
@@ -150,24 +115,24 @@ func TestProfileRepository_UpdateFieldsLogic(t *testing.T) {
|
||||
// TestProfileRepository_FindOneProfileLogic 测试查找单个档案的逻辑
|
||||
func TestProfileRepository_FindOneProfileLogic(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
name string
|
||||
profileCount int
|
||||
wantError bool
|
||||
wantError bool
|
||||
}{
|
||||
{
|
||||
name: "有档案时返回第一个",
|
||||
name: "有档案时返回第一个",
|
||||
profileCount: 1,
|
||||
wantError: false,
|
||||
wantError: false,
|
||||
},
|
||||
{
|
||||
name: "多个档案时返回第一个",
|
||||
name: "多个档案时返回第一个",
|
||||
profileCount: 3,
|
||||
wantError: false,
|
||||
wantError: false,
|
||||
},
|
||||
{
|
||||
name: "没有档案时应该错误",
|
||||
name: "没有档案时应该错误",
|
||||
profileCount: 0,
|
||||
wantError: true,
|
||||
wantError: true,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -181,4 +146,3 @@ func TestProfileRepository_FindOneProfileLogic(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,6 @@ type ProfileService interface {
|
||||
Delete(ctx context.Context, uuid string, userID int64) error
|
||||
|
||||
// 档案状态
|
||||
SetActive(ctx context.Context, uuid string, userID int64) error
|
||||
CheckLimit(ctx context.Context, userID int64, maxProfiles int) error
|
||||
|
||||
// 批量查询
|
||||
|
||||
@@ -214,10 +214,6 @@ func (m *MockProfileRepository) CountByUserID(ctx context.Context, userID int64)
|
||||
return int64(len(m.userProfiles[userID])), nil
|
||||
}
|
||||
|
||||
func (m *MockProfileRepository) SetActive(ctx context.Context, uuid string, userID int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockProfileRepository) UpdateLastUsedAt(ctx context.Context, uuid string) error {
|
||||
return nil
|
||||
}
|
||||
@@ -808,10 +804,6 @@ func (m *MockProfileService) Delete(uuid string, userID int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockProfileService) SetActive(uuid string, userID int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockProfileService) CheckLimit(userID int64, maxProfiles int) error {
|
||||
count := 0
|
||||
for _, profile := range m.profiles {
|
||||
|
||||
@@ -77,18 +77,12 @@ func (s *profileService) Create(ctx context.Context, userID int64, name string)
|
||||
UserID: userID,
|
||||
Name: name,
|
||||
RSAPrivateKey: privateKey,
|
||||
IsActive: true,
|
||||
}
|
||||
|
||||
if err := s.profileRepo.Create(ctx, profile); err != nil {
|
||||
return nil, fmt.Errorf("创建档案失败: %w", err)
|
||||
}
|
||||
|
||||
// 设置活跃状态
|
||||
if err := s.profileRepo.SetActive(ctx, profileUUID, userID); err != nil {
|
||||
return nil, fmt.Errorf("设置活跃状态失败: %w", err)
|
||||
}
|
||||
|
||||
// 清除用户的 profile 列表缓存
|
||||
s.cacheInv.OnCreate(ctx, s.cacheKeys.ProfileList(userID))
|
||||
|
||||
@@ -220,34 +214,6 @@ func (s *profileService) Delete(ctx context.Context, uuid string, userID int64)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *profileService) SetActive(ctx context.Context, uuid string, userID int64) error {
|
||||
// 获取档案并验证权限
|
||||
profile, err := s.profileRepo.FindByUUID(ctx, uuid)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return ErrProfileNotFound
|
||||
}
|
||||
return fmt.Errorf("查询档案失败: %w", err)
|
||||
}
|
||||
|
||||
if profile.UserID != userID {
|
||||
return ErrProfileNoPermission
|
||||
}
|
||||
|
||||
if err := s.profileRepo.SetActive(ctx, uuid, userID); err != nil {
|
||||
return fmt.Errorf("设置活跃状态失败: %w", err)
|
||||
}
|
||||
|
||||
if err := s.profileRepo.UpdateLastUsedAt(ctx, uuid); err != nil {
|
||||
return fmt.Errorf("更新使用时间失败: %w", err)
|
||||
}
|
||||
|
||||
// 清除该用户所有 profile 的缓存(因为活跃状态改变了)
|
||||
s.cacheInv.BatchInvalidate(ctx, s.cacheKeys.ProfilePattern(userID))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *profileService) CheckLimit(ctx context.Context, userID int64, maxProfiles int) error {
|
||||
count, err := s.profileRepo.CountByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
|
||||
@@ -80,15 +80,6 @@ func TestProfileService_StatusValidation(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestProfileService_IsActiveDefault 测试Profile默认活跃状态
|
||||
func TestProfileService_IsActiveDefault(t *testing.T) {
|
||||
// 新创建的档案默认为活跃状态
|
||||
isActive := true
|
||||
if !isActive {
|
||||
t.Error("新创建的Profile应该默认为活跃状态")
|
||||
}
|
||||
}
|
||||
|
||||
// TestUpdateProfile_PermissionCheck 测试更新Profile的权限检查逻辑
|
||||
func TestUpdateProfile_PermissionCheck(t *testing.T) {
|
||||
tests := []struct {
|
||||
@@ -191,38 +182,6 @@ func TestDeleteProfile_PermissionCheck(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestSetActiveProfile_PermissionCheck 测试设置活跃Profile的权限检查
|
||||
func TestSetActiveProfile_PermissionCheck(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
profileUserID int64
|
||||
requestUserID int64
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "用户ID匹配,允许设置",
|
||||
profileUserID: 1,
|
||||
requestUserID: 1,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "用户ID不匹配,拒绝设置",
|
||||
profileUserID: 1,
|
||||
requestUserID: 2,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
hasError := tt.profileUserID != tt.requestUserID
|
||||
if hasError != tt.wantErr {
|
||||
t.Errorf("Permission check failed: got %v, want %v", hasError, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestCheckProfileLimit_Logic 测试Profile数量限制检查逻辑
|
||||
func TestCheckProfileLimit_Logic(t *testing.T) {
|
||||
tests := []struct {
|
||||
@@ -642,8 +601,8 @@ func TestProfileServiceImpl_GetByUserID(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestProfileServiceImpl_Update_And_SetActive 测试 Update 与 SetActive
|
||||
func TestProfileServiceImpl_Update_And_SetActive(t *testing.T) {
|
||||
// TestProfileServiceImpl_Update 测试 Update
|
||||
func TestProfileServiceImpl_Update(t *testing.T) {
|
||||
profileRepo := NewMockProfileRepository()
|
||||
userRepo := NewMockUserRepository()
|
||||
logger := zap.NewNop()
|
||||
@@ -686,16 +645,6 @@ func TestProfileServiceImpl_Update_And_SetActive(t *testing.T) {
|
||||
if _, err := svc.Update(ctx, "u1", 1, stringPtr("Duplicate"), nil, nil); err == nil {
|
||||
t.Fatalf("Update 在名称重复时应返回错误")
|
||||
}
|
||||
|
||||
// SetActive 正常
|
||||
if err := svc.SetActive(ctx, "u1", 1); err != nil {
|
||||
t.Fatalf("SetActive 正常情况失败: %v", err)
|
||||
}
|
||||
|
||||
// SetActive 无权限
|
||||
if err := svc.SetActive(ctx, "u1", 2); err == nil {
|
||||
t.Fatalf("SetActive 在无权限时应返回错误")
|
||||
}
|
||||
}
|
||||
|
||||
// TestProfileServiceImpl_CheckLimit_And_GetByNames 测试 CheckLimit / GetByNames / GetByProfileName
|
||||
|
||||
@@ -203,10 +203,9 @@ func TestTokenServiceImpl_Create(t *testing.T) {
|
||||
|
||||
// 预置Profile
|
||||
testProfile := &model.Profile{
|
||||
UUID: "test-profile-uuid",
|
||||
UserID: 1,
|
||||
Name: "TestProfile",
|
||||
IsActive: true,
|
||||
UUID: "test-profile-uuid",
|
||||
UserID: 1,
|
||||
Name: "TestProfile",
|
||||
}
|
||||
_ = profileRepo.Create(context.Background(), testProfile)
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ type RegisterRequest struct {
|
||||
Username string `json:"username" binding:"required,min=3,max=50" example:"newuser"`
|
||||
Email string `json:"email" binding:"required,email" example:"user@example.com"`
|
||||
Password string `json:"password" binding:"required,min=6,max=128" example:"password123"`
|
||||
VerificationCode string `json:"verification_code" binding:"required,len=6" example:"123456"` // 邮箱验证码
|
||||
VerificationCode string `json:"verification_code" binding:"required,len=6" example:"123456"` // 邮箱验证码
|
||||
Avatar string `json:"avatar" binding:"omitempty,url" example:"https://rustfs.example.com/avatars/user_1/avatar.png"` // 可选,用户自定义头像
|
||||
}
|
||||
|
||||
@@ -158,7 +158,6 @@ type ProfileInfo struct {
|
||||
Name string `json:"name" example:"PlayerName"`
|
||||
SkinID *int64 `json:"skin_id,omitempty" example:"1"`
|
||||
CapeID *int64 `json:"cape_id,omitempty" example:"2"`
|
||||
IsActive bool `json:"is_active" example:"true"`
|
||||
LastUsedAt *time.Time `json:"last_used_at,omitempty" example:"2025-10-01T12:00:00Z"`
|
||||
CreatedAt time.Time `json:"created_at" example:"2025-10-01T10:00:00Z"`
|
||||
UpdatedAt time.Time `json:"updated_at" example:"2025-10-01T10:00:00Z"`
|
||||
@@ -212,4 +211,13 @@ type SystemConfigResponse struct {
|
||||
RegistrationEnabled bool `json:"registration_enabled" example:"true"`
|
||||
MaxTexturesPerUser int `json:"max_textures_per_user" example:"100"`
|
||||
MaxProfilesPerUser int `json:"max_profiles_per_user" example:"5"`
|
||||
}
|
||||
}
|
||||
|
||||
// RenderResponse 材质渲染响应
|
||||
type RenderResponse struct {
|
||||
URL string `json:"url" example:"https://rustfs.example.com/renders/xxx.png"`
|
||||
ContentType string `json:"content_type" example:"image/png"`
|
||||
ETag string `json:"etag,omitempty" example:"abc123def456"`
|
||||
Size int64 `json:"size" example:"2048"`
|
||||
LastModified *time.Time `json:"last_modified,omitempty" example:"2025-10-01T12:00:00Z"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user