- 实现账号增删改查接口和逻辑 - 支持账号状态更新及状态历史记录功能 - 提供账号列表、历史和统计信息查询API - 实现账号轮询机制,支持按使用时间轮询获取账号 - 增加账号登录流程及批量登录功能,集成接码平台和平台API - 管理账号订单容量,支持容量检查与账号登录触发 - 提供账号池状态统计接口 - 账号历史记录查询支持多种变更类型文本展示 - 密码等敏感信息采用脱敏展示 - 完善日志记录和错误处理机制,保证业务稳定运行
220 lines
6.4 KiB
Go
220 lines
6.4 KiB
Go
package cache
|
||
|
||
import (
|
||
"context"
|
||
"github.com/gogf/gf/v2/frame/g"
|
||
"reflect"
|
||
"strings"
|
||
"sync"
|
||
"time"
|
||
|
||
"github.com/gogf/gf/v2/errors/gerror"
|
||
"github.com/gogf/gf/v2/os/glog"
|
||
|
||
"github.com/duke-git/lancet/v2/slice"
|
||
"github.com/gogf/gf/v2/net/gtrace"
|
||
"github.com/gogf/gf/v2/os/gcache"
|
||
"github.com/gogf/gf/v2/util/gconv"
|
||
"github.com/gogf/gf/v2/util/guid"
|
||
"go.opentelemetry.io/otel/trace"
|
||
)
|
||
|
||
var (
|
||
cache *Cache
|
||
)
|
||
|
||
type Cache struct {
|
||
*gcache.Cache
|
||
}
|
||
|
||
type TraceContext struct {
|
||
TraceID string
|
||
SpanID string
|
||
ParentID string
|
||
Attributes map[string]string
|
||
}
|
||
|
||
type PrefixEnum string
|
||
|
||
const (
|
||
PrefixRedeemType PrefixEnum = "redeem_type"
|
||
PrefixRedeemWithPaymentType PrefixEnum = "redeem_with_payment_type"
|
||
PrefixRedeemAppleAccountLimitedType PrefixEnum = "redeem_apple_account_limited_type"
|
||
PrefixAccountLimiterType PrefixEnum = "account_limiter_type"
|
||
PrefixJDAccountQueryCache PrefixEnum = "jd_account_query_cache"
|
||
PrefixJDAccountQueryBalanceWithCookie PrefixEnum = "jd_account_query_cache_with_cookie"
|
||
PrefixWalmartAccountQueryCache PrefixEnum = "walmart_account_query_cache"
|
||
PrefixWalmartAccountQueryBalanceWithCookie PrefixEnum = "walmart_account_query_cache_with_cookie"
|
||
PrefixAppleMachineAccount PrefixEnum = "MachineCurrentAccountId"
|
||
PrefixAppleAccount PrefixEnum = "apple_account"
|
||
PrefixAppleDuplicatedOrder PrefixEnum = "apple_duplicated_order"
|
||
PrefixJdPaymentCheck PrefixEnum = "jd_payment_check" // 支付状态检查缓存
|
||
PrefixJdCardExtract PrefixEnum = "jd_card_extract" // 卡密提取锁定缓存
|
||
PrefixTrace PrefixEnum = "trace"
|
||
PrefixTracePigAccount PrefixEnum = "trace_pig_account"
|
||
)
|
||
|
||
func (e PrefixEnum) Key(key interface{}) string {
|
||
return gconv.String(e) + ":" + gconv.String(key)
|
||
}
|
||
|
||
func NewCache() *Cache {
|
||
if cache == nil {
|
||
sync.OnceFunc(func() {
|
||
cache = &Cache{
|
||
Cache: gcache.New(),
|
||
}
|
||
cache.SetAdapter(gcache.NewAdapterRedis(g.Redis()))
|
||
})()
|
||
}
|
||
return cache
|
||
}
|
||
|
||
// Incr 设置计数器缓存
|
||
func (i *Cache) Incr(ctx context.Context, key string, duration time.Duration) (err error) {
|
||
result, err := i.Get(ctx, key)
|
||
if result == nil || result.IsNil() {
|
||
_ = i.Set(ctx, key, 1, duration)
|
||
return
|
||
}
|
||
_, _, err = i.Update(ctx, key, result.Int()+1)
|
||
return
|
||
}
|
||
|
||
// Decr 递减缓存
|
||
func (i *Cache) Decr(ctx context.Context, key string, duration time.Duration) (err error) {
|
||
result, err := i.Get(ctx, key)
|
||
if result == nil || result.IsNil() {
|
||
_ = i.Set(ctx, key, 0, duration)
|
||
return
|
||
}
|
||
_, _, err = i.Update(ctx, key, result.Int()-1)
|
||
return
|
||
}
|
||
|
||
// GetPrefixKeyNum 获取指定以键为开头的键值对个数
|
||
func (i *Cache) GetPrefixKeyNum(ctx context.Context, key interface{}) (count int) {
|
||
keys, _ := i.Keys(ctx)
|
||
count = slice.CountBy(keys, func(index int, item interface{}) bool {
|
||
return strings.HasPrefix(item.(string), key.(string))
|
||
})
|
||
return
|
||
}
|
||
|
||
// GetPrefixKey 获取指定以键为开头的键值对个数
|
||
func (i *Cache) GetPrefixKey(ctx context.Context, key interface{}) (keys []interface{}) {
|
||
keys, _ = i.Keys(ctx)
|
||
keys = slice.Filter(keys, func(index int, item interface{}) bool {
|
||
return strings.HasPrefix(item.(string), key.(string))
|
||
})
|
||
return keys
|
||
}
|
||
|
||
// SaveTrace 存储追踪信息到Redis中
|
||
// SaveTraceToRedis 保存追踪信息到Redis
|
||
func (i *Cache) SaveTrace(ctx context.Context, key string) error {
|
||
spanCtx := trace.SpanContextFromContext(ctx)
|
||
if spanCtx.IsValid() {
|
||
return i.Set(ctx, PrefixTrace.Key(key), spanCtx, time.Minute*10)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// GetTrace 获取追踪信息(恢复追踪能力)
|
||
func (i *Cache) GetTrace(ctx context.Context, key string) context.Context {
|
||
value, err := i.Get(ctx, PrefixTrace.Key(key))
|
||
if err != nil || value == nil || value.IsNil() || reflect.TypeOf(value.Interface()) != reflect.TypeOf(trace.SpanContext{}) {
|
||
return ctx
|
||
}
|
||
spanCtx, ok := value.Interface().(trace.SpanContext)
|
||
if !ok {
|
||
glog.Error(ctx, "获取追踪信息失败,类型错误")
|
||
return ctx
|
||
}
|
||
return trace.ContextWithRemoteSpanContext(ctx, spanCtx)
|
||
}
|
||
|
||
// GetTraceID 获取追踪的唯一id
|
||
func (i *Cache) GetTraceID(ctx context.Context) string {
|
||
return gtrace.GetSpanID(ctx) + "-" + guid.S()
|
||
}
|
||
|
||
// DeleteTrace 删除追踪信息
|
||
func (i *Cache) DeleteTrace(ctx context.Context, key string) {
|
||
_, _ = i.Remove(ctx, PrefixTrace.Key(key))
|
||
}
|
||
|
||
// TryLock 尝试获取分布式锁
|
||
// key: 锁的键名
|
||
// ttl: 锁的过期时间
|
||
// 返回值: 是否获取成功,锁的唯一标识,错误信息
|
||
func (i *Cache) TryLock(ctx context.Context, key string, ttl time.Duration) (bool, string, error) {
|
||
lockValue := guid.S() // 生成唯一标识
|
||
|
||
// 使用自定义cache模块的SetIfNotExist方法实现分布式锁
|
||
// SetIfNotExist返回true表示设置成功(key不存在),false表示key已存在
|
||
success, err := i.SetIfNotExist(ctx, key, lockValue, ttl)
|
||
if err != nil {
|
||
return false, "", err // 其他错误
|
||
}
|
||
|
||
if !success {
|
||
return false, "", nil // key已存在,获取锁失败
|
||
}
|
||
|
||
return true, lockValue, nil
|
||
}
|
||
|
||
// Lock 获取分布式锁(阻塞式)
|
||
// key: 锁的键名
|
||
// ttl: 锁的过期时间
|
||
// timeout: 获取锁的超时时间
|
||
// 返回值: 锁的唯一标识,错误信息
|
||
func (i *Cache) Lock(ctx context.Context, key string, ttl time.Duration, timeout time.Duration) (string, error) {
|
||
startTime := time.Now()
|
||
|
||
for {
|
||
success, lockValue, err := i.TryLock(ctx, key, ttl)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
if success {
|
||
return lockValue, nil
|
||
}
|
||
|
||
// 检查是否超时
|
||
if time.Since(startTime) > timeout {
|
||
return "", gerror.New("获取分布式锁超时")
|
||
}
|
||
|
||
// 短暂等待后重试
|
||
time.Sleep(time.Millisecond * 10)
|
||
}
|
||
}
|
||
|
||
// Unlock 释放分布式锁
|
||
// key: 锁的键名
|
||
// lockValue: 锁的唯一标识(用于确保只有锁的持有者才能释放锁)
|
||
func (i *Cache) Unlock(ctx context.Context, key string, lockValue string) error {
|
||
// 获取当前锁的值
|
||
currentValue, err := i.Get(ctx, key)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
if currentValue == nil || currentValue.IsNil() {
|
||
// 锁不存在,无需释放
|
||
return nil
|
||
}
|
||
|
||
// 检查锁的值是否匹配,确保只有锁的持有者才能释放锁
|
||
if currentValue.String() != lockValue {
|
||
return gerror.New("无权释放分布式锁,锁的持有者不匹配")
|
||
}
|
||
|
||
// 删除锁
|
||
_, err = i.Remove(ctx, key)
|
||
return err
|
||
}
|