init
This commit is contained in:
Vendored
+27
@@ -0,0 +1,27 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
type (
|
||||
Cache interface {
|
||||
// Del deletes cached values with keys.
|
||||
Del(keys ...string) error
|
||||
// DelCtx deletes cached values with keys.
|
||||
DelCtx(ctx context.Context, keys ...string) error
|
||||
// Get gets the cache with key and fills into v.
|
||||
Get(key string, val any) error
|
||||
// GetCtx gets the cache with key and fills into v.
|
||||
GetCtx(ctx context.Context, key string, val any) error
|
||||
// Set sets the cache with key and value.
|
||||
Set(key string, val any) error
|
||||
// SetCtx sets the cache with key and value.
|
||||
SetCtx(ctx context.Context, key string, val any) error
|
||||
// SetWithExpire sets the cache with key and v, using given expire.
|
||||
SetWithExpire(key string, val any, expire time.Duration) error
|
||||
// SetWithExpireCtx sets the cache with key and v, using given expire.
|
||||
SetWithExpireCtx(ctx context.Context, key string, val any, expire time.Duration) error
|
||||
}
|
||||
)
|
||||
Vendored
+120
@@ -0,0 +1,120 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var ErrNotFound = redis.Nil
|
||||
|
||||
type (
|
||||
// ExecCtxFn defines the sql exec method.
|
||||
ExecCtxFn func(conn *gorm.DB) error
|
||||
// IndexQueryCtxFn defines the query method that based on unique indexes.
|
||||
IndexQueryCtxFn func(conn *gorm.DB, v interface{}) (interface{}, error)
|
||||
// PrimaryQueryCtxFn defines the query method that based on primary keys.
|
||||
PrimaryQueryCtxFn func(conn *gorm.DB, v, primary interface{}) error
|
||||
// QueryCtxFn defines the query method.
|
||||
QueryCtxFn func(conn *gorm.DB, v interface{}) error
|
||||
|
||||
CachedConn struct {
|
||||
db *gorm.DB
|
||||
cache *redis.Client
|
||||
}
|
||||
)
|
||||
|
||||
// NewConn returns a CachedConn with a redis cluster cache.
|
||||
func NewConn(db *gorm.DB, c *redis.Client) CachedConn {
|
||||
return CachedConn{
|
||||
db: db,
|
||||
cache: c,
|
||||
}
|
||||
}
|
||||
|
||||
// DelCache deletes cache with keys.
|
||||
func (cc CachedConn) DelCache(keys ...string) error {
|
||||
return cc.cache.Del(context.Background(), keys...).Err()
|
||||
}
|
||||
|
||||
// DelCacheCtx deletes cache with keys.
|
||||
func (cc CachedConn) DelCacheCtx(ctx context.Context, keys ...string) error {
|
||||
return cc.cache.Del(ctx, keys...).Err()
|
||||
}
|
||||
|
||||
// GetCache unmarshals cache with given key into v.
|
||||
func (cc CachedConn) GetCache(key string, v interface{}) error {
|
||||
// query redis key
|
||||
val, err := cc.cache.Get(context.Background(), key).Result()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// unmarshal value
|
||||
return json.Unmarshal([]byte(val), v)
|
||||
}
|
||||
|
||||
// SetCache sets cache with key and v.
|
||||
func (cc CachedConn) SetCache(key string, v interface{}) error {
|
||||
// marshal value
|
||||
val, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// set redis key
|
||||
return cc.cache.Set(context.Background(), key, val, 0).Err()
|
||||
}
|
||||
|
||||
// ExecCtx runs given exec on given keys, and returns execution result.
|
||||
func (cc CachedConn) ExecCtx(ctx context.Context, execCtx ExecCtxFn, keys ...string) error {
|
||||
err := execCtx(cc.db.WithContext(ctx))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cc.DelCacheCtx(ctx, keys...); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExecNoCache runs exec with given sql statement, without affecting cache.
|
||||
func (cc CachedConn) ExecNoCache(exec ExecCtxFn) error {
|
||||
return cc.ExecNoCacheCtx(context.Background(), exec)
|
||||
}
|
||||
|
||||
// ExecNoCacheCtx runs exec with given sql statement, without affecting cache.
|
||||
func (cc CachedConn) ExecNoCacheCtx(ctx context.Context, execCtx ExecCtxFn) (err error) {
|
||||
return execCtx(cc.db.WithContext(ctx))
|
||||
}
|
||||
|
||||
func (cc CachedConn) QueryCtx(ctx context.Context, v interface{}, key string, query QueryCtxFn) (err error) {
|
||||
err = cc.GetCache(key, v)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrNotFound) {
|
||||
err = query(cc.db.WithContext(ctx), v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cc.SetCache(key, v)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// QueryNoCacheCtx runs query with given sql statement, without affecting cache.
|
||||
func (cc CachedConn) QueryNoCacheCtx(ctx context.Context, v interface{}, query QueryCtxFn) (err error) {
|
||||
return query(cc.db.WithContext(ctx), v)
|
||||
}
|
||||
|
||||
// TransactCtx runs given fn in transaction mode.
|
||||
func (cc CachedConn) TransactCtx(ctx context.Context, fn func(db *gorm.DB) error, opts ...*sql.TxOptions) error {
|
||||
return cc.db.WithContext(ctx).Transaction(fn, opts...)
|
||||
}
|
||||
|
||||
// Transact runs given fn in transaction mode.
|
||||
func (cc CachedConn) Transact(fn func(db *gorm.DB) error, opts ...*sql.TxOptions) error {
|
||||
return cc.TransactCtx(context.Background(), fn, opts...)
|
||||
}
|
||||
Vendored
+66
@@ -0,0 +1,66 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/perfect-panel/ppanel-server/pkg/orm"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/plugin/soft_delete"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Id int64 `gorm:"primarykey"`
|
||||
Email string `gorm:"index:idx_email;type:varchar(100);unique;not null;comment:电子邮箱"`
|
||||
Password string `gorm:"type:varchar(100);comment:用户密码;not null"`
|
||||
Avatar string `gorm:"type:varchar(200);default:'';comment:用户头像"`
|
||||
Balance int64 `gorm:"default:0;comment:用户余额"`
|
||||
Telegram int64 `gorm:"default:null;comment:Telegram账号"`
|
||||
ReferCode string `gorm:"type:varchar(20);default:'';comment:推荐码"`
|
||||
RefererId int64 `gorm:"comment:推荐人ID"`
|
||||
Enable bool `gorm:"default:true;not null;comment:账户是否可用"`
|
||||
IsAdmin bool `gorm:"default:false;not null;comment:是否管理员"`
|
||||
ValidEmail bool `gorm:"default:false;not null;comment:是否验证邮箱"`
|
||||
EnableEmailNotify bool `gorm:"default:false;not null;comment:是否启用邮件通知"`
|
||||
EnableTelegramNotify bool `gorm:"default:false;not null;comment:是否启用Telegram通知"`
|
||||
EnableBalanceNotify bool `gorm:"default:false;not null;comment:是否启用余额变动通知"`
|
||||
EnableLoginNotify bool `gorm:"default:false;not null;comment:是否启用登录通知"`
|
||||
EnableSubscribeNotify bool `gorm:"default:false;not null;comment:是否启用订阅通知"`
|
||||
EnableTradeNotify bool `gorm:"default:false;not null;comment:是否启用交易通知"`
|
||||
CreatedAt time.Time `gorm:"<-:create;comment:创建时间"`
|
||||
UpdatedAt time.Time `gorm:"comment:更新时间"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"default:null;comment:删除时间"`
|
||||
IsDel soft_delete.DeletedAt `gorm:"softDelete:flag,DeletedAtField:DeletedAt;comment:1:正常 0:删除"` // Use `1` `0` to identify
|
||||
}
|
||||
|
||||
func TestGormCacheCtx(t *testing.T) {
|
||||
t.Skipf("skip TestGormCacheCtx test")
|
||||
db, err := orm.ConnectMysql(orm.Mysql{
|
||||
Config: orm.Config{
|
||||
Addr: "localhost:3306",
|
||||
Config: "charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai",
|
||||
Dbname: "vpnboard",
|
||||
Username: "root",
|
||||
Password: "mylove520",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
rds := redis.NewClient(&redis.Options{
|
||||
Addr: "localhost:6379",
|
||||
})
|
||||
conn := NewConn(db, rds)
|
||||
var u User
|
||||
key := "user:id"
|
||||
err = conn.QueryCtx(context.Background(), &u, key, func(conn *gorm.DB, v interface{}) error {
|
||||
return conn.Where("id = ?", 1).First(v).Error
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
t.Logf("get cache success %+v", u)
|
||||
}
|
||||
Reference in New Issue
Block a user