package repository import ( "context" "database/sql" "fmt" "strings" "github.com/luxsin/app-api/internal/cache" "github.com/luxsin/app-api/internal/model" "github.com/redis/go-redis/v9" ) type BrandRepository struct { db *sql.DB cache *cache.BrandCache } func NewBrandRepository(db *sql.DB, brandCache *cache.BrandCache) *BrandRepository { return &BrandRepository{db: db, cache: brandCache} } func (r *BrandRepository) List(ctx context.Context, brandName string) ([]model.Brand, error) { // 尝试从 Redis 获取全量品牌 list, err := r.cache.GetAll(ctx) if err == nil { // 缓存命中,在应用层过滤 if brandName = strings.TrimSpace(brandName); brandName != "" { list = filterBrands(list, brandName) } return list, nil } // 缓存未命中或 Redis 异常,降级到数据库 if err != redis.Nil { fmt.Printf("brand cache read failed, fallback to db: %v\n", err) } list, err = r.ListFromDB(ctx, brandName) if err != nil { return nil, err } // 回写缓存(全量数据) if allBrands, dbErr := r.ListFromDB(ctx, ""); dbErr == nil { if cacheErr := r.cache.SetAll(ctx, allBrands); cacheErr != nil { fmt.Printf("brand cache write failed: %v\n", cacheErr) } } return list, nil } // ListFromDB 直接从数据库查询(用于预热和缓存回写) func (r *BrandRepository) ListFromDB(ctx context.Context, brandName string) ([]model.Brand, error) { query := "SELECT id, name FROM brand" args := []any{} if strings.TrimSpace(brandName) != "" { query += " WHERE name LIKE ?" args = append(args, "%"+brandName+"%") } query += " ORDER BY name ASC" rows, err := r.db.QueryContext(ctx, query, args...) if err != nil { return nil, fmt.Errorf("query brand: %w", err) } defer rows.Close() list := make([]model.Brand, 0) for rows.Next() { var b model.Brand if err := rows.Scan(&b.ID, &b.Name); err != nil { return nil, fmt.Errorf("scan brand: %w", err) } list = append(list, b) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("iterate brand: %w", err) } return list, nil } // filterBrands 在应用层做模糊过滤 func filterBrands(list []model.Brand, brandName string) []model.Brand { filtered := make([]model.Brand, 0) for _, b := range list { if strings.Contains( strings.ToLower(b.Name), strings.ToLower(brandName), ) { filtered = append(filtered, b) } } return filtered }