refactor: Enhance texture handling and configuration

- Removed Swagger documentation import from the main server file.
- Updated TextureInfo struct to include UploaderUsername for better texture metadata.
- Modified texture repository methods to preload Uploader information when fetching textures by hash.
- Improved texture service to handle cases where Uploader information is missing, ensuring proper caching and retrieval.
- Added Redis configuration options in the environment variable setup for better flexibility.
This commit is contained in:
lafay
2026-01-10 03:15:27 +08:00
parent e761ff5be5
commit c5db489d72
7 changed files with 80 additions and 41 deletions

View File

@@ -32,8 +32,6 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"go.uber.org/zap" "go.uber.org/zap"
_ "carrotskin/docs" // Swagger docs
) )
func main() { func main() {

View File

@@ -87,9 +87,15 @@ func ProfilesToProfileInfos(profiles []*model.Profile) []*types.ProfileInfo {
// TextureToTextureInfo 将 Texture 模型转换为 TextureInfo 响应 // TextureToTextureInfo 将 Texture 模型转换为 TextureInfo 响应
func TextureToTextureInfo(texture *model.Texture) *types.TextureInfo { func TextureToTextureInfo(texture *model.Texture) *types.TextureInfo {
uploaderUsername := ""
if texture.Uploader != nil {
uploaderUsername = texture.Uploader.Username
}
return &types.TextureInfo{ return &types.TextureInfo{
ID: texture.ID, ID: texture.ID,
UploaderID: texture.UploaderID, UploaderID: texture.UploaderID,
UploaderUsername: uploaderUsername,
Name: texture.Name, Name: texture.Name,
Description: texture.Description, Description: texture.Description,
Type: types.TextureType(texture.Type), Type: types.TextureType(texture.Type),

View File

@@ -29,13 +29,13 @@ func (r *textureRepository) FindByID(ctx context.Context, id int64) (*model.Text
func (r *textureRepository) FindByHash(ctx context.Context, hash string) (*model.Texture, error) { func (r *textureRepository) FindByHash(ctx context.Context, hash string) (*model.Texture, error) {
var texture model.Texture var texture model.Texture
err := r.db.WithContext(ctx).Where("hash = ?", hash).First(&texture).Error err := r.db.WithContext(ctx).Preload("Uploader").Where("hash = ?", hash).First(&texture).Error
return handleNotFoundResult(&texture, err) return handleNotFoundResult(&texture, err)
} }
func (r *textureRepository) FindByHashAndUploaderID(ctx context.Context, hash string, uploaderID int64) (*model.Texture, error) { func (r *textureRepository) FindByHashAndUploaderID(ctx context.Context, hash string, uploaderID int64) (*model.Texture, error) {
var texture model.Texture var texture model.Texture
err := r.db.WithContext(ctx).Where("hash = ? AND uploader_id = ?", hash, uploaderID).First(&texture).Error err := r.db.WithContext(ctx).Preload("Uploader").Where("hash = ? AND uploader_id = ?", hash, uploaderID).First(&texture).Error
return handleNotFoundResult(&texture, err) return handleNotFoundResult(&texture, err)
} }

View File

@@ -55,6 +55,22 @@ func (s *textureService) GetByID(ctx context.Context, id int64) (*model.Texture,
if texture.Status == -1 { if texture.Status == -1 {
return nil, errors.New("材质已删除") return nil, errors.New("材质已删除")
} }
// 如果缓存中没有 Uploader 信息,重新查询数据库
if texture.Uploader == nil {
texture2, err := s.textureRepo.FindByID(ctx, id)
if err != nil {
return nil, err
}
if texture2 == nil {
return nil, ErrTextureNotFound
}
if texture2.Status == -1 {
return nil, errors.New("材质已删除")
}
// 更新缓存
s.cache.SetAsync(context.Background(), cacheKey, texture2, s.cache.Policy.TextureTTL)
return texture2, nil
}
return &texture, nil return &texture, nil
} }
@@ -365,7 +381,8 @@ func (s *textureService) UploadTexture(ctx context.Context, uploaderID int64, na
// 清除用户的 texture 列表缓存(所有分页) // 清除用户的 texture 列表缓存(所有分页)
s.cacheInv.BatchInvalidate(ctx, fmt.Sprintf("texture:user:%d:*", uploaderID)) s.cacheInv.BatchInvalidate(ctx, fmt.Sprintf("texture:user:%d:*", uploaderID))
return texture, nil // 重新查询以预加载 Uploader 关联
return s.textureRepo.FindByID(ctx, texture.ID)
} }
// parseTextureTypeInternal 解析材质类型 // parseTextureTypeInternal 解析材质类型

View File

@@ -123,6 +123,7 @@ const (
type TextureInfo struct { type TextureInfo struct {
ID int64 `json:"id" example:"1"` ID int64 `json:"id" example:"1"`
UploaderID int64 `json:"uploader_id" example:"1"` UploaderID int64 `json:"uploader_id" example:"1"`
UploaderUsername string `json:"uploader_username" example:"testuser"`
Name string `json:"name" example:"My Skin"` Name string `json:"name" example:"My Skin"`
Description string `json:"description,omitempty" example:"A cool skin"` Description string `json:"description,omitempty" example:"A cool skin"`
Type TextureType `json:"type" example:"SKIN"` Type TextureType `json:"type" example:"SKIN"`

View File

@@ -131,13 +131,14 @@ type SecurityConfig struct {
// Load 加载配置 - 完全从环境变量加载不依赖YAML文件 // Load 加载配置 - 完全从环境变量加载不依赖YAML文件
func Load() (*Config, error) { func Load() (*Config, error) {
// 加载.env文件如果存在 // 加载.env文件如果存在
_ = godotenv.Load(".env") if err := godotenv.Load(".env"); err != nil {
fmt.Printf("警告: 无法加载 .env 文件: %v\n", err)
}
// 设置默认值 // 设置默认值
setDefaults() setDefaults()
// 设置环境变量前缀 // 自动读取环境变量(不设置前缀,因为 BindEnv 已经明确指定了变量名)
viper.SetEnvPrefix("CARROTSKIN")
viper.AutomaticEnv() viper.AutomaticEnv()
// 手动设置环境变量映射 // 手动设置环境变量映射
@@ -302,6 +303,7 @@ func setupEnvMappings() {
// overrideFromEnv 从环境变量中覆盖配置 // overrideFromEnv 从环境变量中覆盖配置
func overrideFromEnv(config *Config) { func overrideFromEnv(config *Config) {
// 处理RustFS存储桶配置 // 处理RustFS存储桶配置
if texturesBucket := os.Getenv("RUSTFS_BUCKET_TEXTURES"); texturesBucket != "" { if texturesBucket := os.Getenv("RUSTFS_BUCKET_TEXTURES"); texturesBucket != "" {
if config.RustFS.Buckets == nil { if config.RustFS.Buckets == nil {
@@ -342,6 +344,24 @@ func overrideFromEnv(config *Config) {
} }
} }
// 处理Redis基本配置
if host := os.Getenv("REDIS_HOST"); host != "" {
config.Redis.Host = host
}
if port := os.Getenv("REDIS_PORT"); port != "" {
if val, err := strconv.Atoi(port); err == nil {
config.Redis.Port = val
}
}
if password := os.Getenv("REDIS_PASSWORD"); password != "" {
config.Redis.Password = password
}
if database := os.Getenv("REDIS_DATABASE"); database != "" {
if val, err := strconv.Atoi(database); err == nil {
config.Redis.Database = val
}
}
// 处理Redis连接池配置 // 处理Redis连接池配置
if poolSize := os.Getenv("REDIS_POOL_SIZE"); poolSize != "" { if poolSize := os.Getenv("REDIS_POOL_SIZE"); poolSize != "" {
if val, err := strconv.Atoi(poolSize); err == nil { if val, err := strconv.Atoi(poolSize); err == nil {

View File

@@ -1,9 +1,7 @@
package storage package storage
import ( import (
"context"
"testing" "testing"
"time"
"carrotskin/pkg/config" "carrotskin/pkg/config"
@@ -41,4 +39,3 @@ func TestNewStorage_SkipConnectWhenNoCreds(t *testing.T) {
t.Fatalf("NewStorage should not error when creds empty: %v", err) t.Fatalf("NewStorage should not error when creds empty: %v", err)
} }
} }