chore: 初始化仓库,排除二进制文件和覆盖率文件
Some checks failed
SonarQube Analysis / sonarqube (push) Has been cancelled
Test / test (push) Has been cancelled
Test / lint (push) Has been cancelled
Test / build (push) Has been cancelled

This commit is contained in:
lan
2025-11-28 23:30:49 +08:00
commit 4b4980820f
107 changed files with 20755 additions and 0 deletions

50
pkg/redis/manager.go Normal file
View File

@@ -0,0 +1,50 @@
package redis
import (
"carrotskin/pkg/config"
"fmt"
"sync"
"go.uber.org/zap"
)
var (
// clientInstance 全局Redis客户端实例
clientInstance *Client
// once 确保只初始化一次
once sync.Once
// initError 初始化错误
initError error
)
// Init 初始化Redis客户端线程安全只会执行一次
func Init(cfg config.RedisConfig, logger *zap.Logger) error {
once.Do(func() {
clientInstance, initError = New(cfg, logger)
if initError != nil {
return
}
})
return initError
}
// GetClient 获取Redis客户端实例线程安全
func GetClient() (*Client, error) {
if clientInstance == nil {
return nil, fmt.Errorf("Redis客户端未初始化请先调用 redis.Init()")
}
return clientInstance, nil
}
// MustGetClient 获取Redis客户端实例如果未初始化则panic
func MustGetClient() *Client {
client, err := GetClient()
if err != nil {
panic(err)
}
return client
}

53
pkg/redis/manager_test.go Normal file
View File

@@ -0,0 +1,53 @@
package redis
import (
"carrotskin/pkg/config"
"testing"
"go.uber.org/zap/zaptest"
)
// TestGetClient_NotInitialized 测试未初始化时获取Redis客户端
func TestGetClient_NotInitialized(t *testing.T) {
_, err := GetClient()
if err == nil {
t.Error("未初始化时应该返回错误")
}
expectedError := "Redis客户端未初始化请先调用 redis.Init()"
if err.Error() != expectedError {
t.Errorf("错误消息 = %q, want %q", err.Error(), expectedError)
}
}
// TestMustGetClient_Panic 测试MustGetClient在未初始化时panic
func TestMustGetClient_Panic(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Error("MustGetClient 应该在未初始化时panic")
}
}()
_ = MustGetClient()
}
// TestInit_Redis 测试Redis初始化逻辑
func TestInit_Redis(t *testing.T) {
cfg := config.RedisConfig{
Host: "localhost",
Port: 6379,
Password: "",
Database: 0,
PoolSize: 10,
}
logger := zaptest.NewLogger(t)
// 验证Init函数存在且可调用
// 注意:实际连接可能失败,这是可以接受的
err := Init(cfg, logger)
if err != nil {
t.Logf("Init() 返回错误可能正常如果Redis未运行: %v", err)
}
}

174
pkg/redis/redis.go Normal file
View File

@@ -0,0 +1,174 @@
package redis
import (
"context"
"errors"
"fmt"
"time"
"carrotskin/pkg/config"
"github.com/redis/go-redis/v9"
"go.uber.org/zap"
)
// Client Redis客户端包装
type Client struct {
*redis.Client
logger *zap.Logger
}
// New 创建Redis客户端
func New(cfg config.RedisConfig, logger *zap.Logger) (*Client, error) {
// 创建Redis客户端
rdb := redis.NewClient(&redis.Options{
Addr: fmt.Sprintf("%s:%d", cfg.Host, cfg.Port),
Password: cfg.Password,
DB: cfg.Database,
PoolSize: cfg.PoolSize,
DialTimeout: 5 * time.Second,
ReadTimeout: 3 * time.Second,
WriteTimeout: 3 * time.Second,
})
// 测试连接
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := rdb.Ping(ctx).Err(); err != nil {
return nil, fmt.Errorf("Redis连接失败: %w", err)
}
logger.Info("Redis连接成功",
zap.String("host", cfg.Host),
zap.Int("port", cfg.Port),
zap.Int("database", cfg.Database),
)
return &Client{
Client: rdb,
logger: logger,
}, nil
}
// Close 关闭Redis连接
func (c *Client) Close() error {
c.logger.Info("正在关闭Redis连接")
return c.Client.Close()
}
// Set 设置键值对(带过期时间)
func (c *Client) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error {
return c.Client.Set(ctx, key, value, expiration).Err()
}
// Get 获取键值
func (c *Client) Get(ctx context.Context, key string) (string, error) {
return c.Client.Get(ctx, key).Result()
}
// Del 删除键
func (c *Client) Del(ctx context.Context, keys ...string) error {
return c.Client.Del(ctx, keys...).Err()
}
// Exists 检查键是否存在
func (c *Client) Exists(ctx context.Context, keys ...string) (int64, error) {
return c.Client.Exists(ctx, keys...).Result()
}
// Expire 设置键的过期时间
func (c *Client) Expire(ctx context.Context, key string, expiration time.Duration) error {
return c.Client.Expire(ctx, key, expiration).Err()
}
// Incr 自增
func (c *Client) Incr(ctx context.Context, key string) (int64, error) {
return c.Client.Incr(ctx, key).Result()
}
// Decr 自减
func (c *Client) Decr(ctx context.Context, key string) (int64, error) {
return c.Client.Decr(ctx, key).Result()
}
// HSet 设置哈希字段
func (c *Client) HSet(ctx context.Context, key string, values ...interface{}) error {
return c.Client.HSet(ctx, key, values...).Err()
}
// HGet 获取哈希字段
func (c *Client) HGet(ctx context.Context, key, field string) (string, error) {
return c.Client.HGet(ctx, key, field).Result()
}
// HGetAll 获取所有哈希字段
func (c *Client) HGetAll(ctx context.Context, key string) (map[string]string, error) {
return c.Client.HGetAll(ctx, key).Result()
}
// HDel 删除哈希字段
func (c *Client) HDel(ctx context.Context, key string, fields ...string) error {
return c.Client.HDel(ctx, key, fields...).Err()
}
// SAdd 添加集合成员
func (c *Client) SAdd(ctx context.Context, key string, members ...interface{}) error {
return c.Client.SAdd(ctx, key, members...).Err()
}
// SMembers 获取集合所有成员
func (c *Client) SMembers(ctx context.Context, key string) ([]string, error) {
return c.Client.SMembers(ctx, key).Result()
}
// SRem 删除集合成员
func (c *Client) SRem(ctx context.Context, key string, members ...interface{}) error {
return c.Client.SRem(ctx, key, members...).Err()
}
// SIsMember 检查是否是集合成员
func (c *Client) SIsMember(ctx context.Context, key string, member interface{}) (bool, error) {
return c.Client.SIsMember(ctx, key, member).Result()
}
// ZAdd 添加有序集合成员
func (c *Client) ZAdd(ctx context.Context, key string, members ...redis.Z) error {
return c.Client.ZAdd(ctx, key, members...).Err()
}
// ZRange 获取有序集合范围内的成员
func (c *Client) ZRange(ctx context.Context, key string, start, stop int64) ([]string, error) {
return c.Client.ZRange(ctx, key, start, stop).Result()
}
// ZRem 删除有序集合成员
func (c *Client) ZRem(ctx context.Context, key string, members ...interface{}) error {
return c.Client.ZRem(ctx, key, members...).Err()
}
// Pipeline 创建管道
func (c *Client) Pipeline() redis.Pipeliner {
return c.Client.Pipeline()
}
// TxPipeline 创建事务管道
func (c *Client) TxPipeline() redis.Pipeliner {
return c.Client.TxPipeline()
}
func (c *Client) Nil(err error) bool {
return errors.Is(err, redis.Nil)
}
// GetBytes 从Redis读取key对应的字节数据统一处理错误
func (c *Client) GetBytes(ctx context.Context, key string) ([]byte, error) {
val, err := c.Client.Get(ctx, key).Bytes()
if err != nil {
if errors.Is(err, redis.Nil) { // 处理key不存在的情况返回nil无错误
return nil, nil
}
return nil, err // 其他错误(如连接失败)
}
return val, nil
}