feat(common): 重构 share 包并添加缓存扫描功能
- 移除了 sessionManager 结构体和相关方法 - 新增 cacheStore 结构体的 Scan 方法,用于扫描匹配模式的键 - 新增 cacheStore 结构体的 MGet 方法,用于批量获取多个键的值 - 优化了代码结构,提高了缓存操作的灵活性和效率
This commit is contained in:
1
.vscode/launch.json
vendored
1
.vscode/launch.json
vendored
@@ -14,6 +14,7 @@
|
|||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
"program": "${workspaceFolder}/login",
|
"program": "${workspaceFolder}/login",
|
||||||
"console": "integratedTerminal"
|
"console": "integratedTerminal"
|
||||||
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Launch logic1",
|
"name": "Launch logic1",
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
package share
|
package share
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"blazing/cool"
|
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gogf/gf/v2/errors/gerror"
|
"github.com/gogf/gf/v2/errors/gerror"
|
||||||
"github.com/gogf/gf/v2/os/gcache"
|
"github.com/gogf/gf/v2/os/gcache"
|
||||||
"github.com/gogf/gf/v2/os/gctx"
|
|
||||||
"github.com/gogf/gf/v2/util/gconv"
|
"github.com/gogf/gf/v2/util/gconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ShareManager = newSessionManager()
|
var ShareManager = newSessionManager()
|
||||||
var (
|
var (
|
||||||
// ErrCacheMiss 表示缓存未命中
|
// ErrCacheMiss 表示缓存未命中
|
||||||
@@ -26,22 +26,6 @@ type cacheStore[T any] struct {
|
|||||||
prefix string // 缓存键前缀
|
prefix string // 缓存键前缀
|
||||||
}
|
}
|
||||||
|
|
||||||
// newSessionStore 创建会话缓存实例
|
|
||||||
func newSessionStore() *cacheStore[uint32] {
|
|
||||||
return &cacheStore[uint32]{
|
|
||||||
manager: cool.CacheManager,
|
|
||||||
prefix: "blazing:session:",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// newUserOnlineStore 创建用户在线状态缓存实例
|
|
||||||
func newUserOnlineStore() *cacheStore[uint16] {
|
|
||||||
return &cacheStore[uint16]{
|
|
||||||
manager: cool.CacheManager,
|
|
||||||
prefix: "blazing:useronline:",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 生成带前缀的缓存键
|
// 生成带前缀的缓存键
|
||||||
func (s *cacheStore[T]) formatKey(key string) string {
|
func (s *cacheStore[T]) formatKey(key string) string {
|
||||||
return s.prefix + strings.TrimSpace(key)
|
return s.prefix + strings.TrimSpace(key)
|
||||||
@@ -126,56 +110,48 @@ func (s *cacheStore[T]) GetOrSet(ctx context.Context, key string, defaultValue T
|
|||||||
return converted, nil
|
return converted, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// sessionManager 会话管理器
|
// Scan 扫描匹配模式的键(先获取所有键,再内存筛选)
|
||||||
type sessionManager struct {
|
func (s *cacheStore[T]) Scan(ctx context.Context, pattern string) ([]string, error) {
|
||||||
sessionStore *cacheStore[uint32] // 会话缓存
|
// 1. 获取所有键
|
||||||
userOnlineStore *cacheStore[uint16] // 用户在线状态缓存
|
allKeys, err := s.manager.Keys(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, gerror.Wrapf(err, "获取所有键失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
// newSessionManager 创建会话管理器
|
// 2. 筛选符合模式的键(使用字符串匹配,支持简单通配符*)
|
||||||
func newSessionManager() *sessionManager {
|
var matchedKeys []string
|
||||||
return &sessionManager{
|
for _, key := range allKeys {
|
||||||
sessionStore: newSessionStore(),
|
// 简单模式匹配:支持*匹配任意字符(可根据需求扩展为glob匹配)
|
||||||
userOnlineStore: newUserOnlineStore(),
|
if matchSimplePattern(key.(string), pattern) {
|
||||||
|
matchedKeys = append(matchedKeys, key.(string))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return matchedKeys, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetSession 通过会话ID获取用户ID
|
// 简单模式匹配(支持*通配符)
|
||||||
func (m *sessionManager) GetSession(sessionID string) (uint32, error) {
|
func matchSimplePattern(str, pattern string) bool {
|
||||||
return m.sessionStore.Get(context.Background(), sessionID)
|
// 替换pattern中的*为正则匹配符,转义其他特殊字符
|
||||||
|
regexPattern := strings.ReplaceAll(regexp.QuoteMeta(pattern), "\\*", ".*")
|
||||||
|
regexPattern = "^" + regexPattern + "$" // 全匹配
|
||||||
|
matched, _ := regexp.MatchString(regexPattern, str)
|
||||||
|
return matched
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveSession 保存会话信息
|
// MGet 批量获取多个键的值(循环调用Get实现)
|
||||||
func (m *sessionManager) SaveSession(sessionID string, userID uint32) error {
|
func (s *cacheStore[T]) MGet(ctx context.Context, keys []string) ([]interface{}, error) {
|
||||||
return m.sessionStore.Set(gctx.New(), sessionID, userID, time.Hour*24)
|
values := make([]interface{}, len(keys))
|
||||||
|
for i, key := range keys {
|
||||||
|
formattedKey := s.formatKey(key) // 格式化键(添加前缀)
|
||||||
|
result, err := s.manager.Get(ctx, formattedKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, gerror.Wrapf(err, "批量获取缓存失败,键: %s", key)
|
||||||
}
|
}
|
||||||
|
if result.IsEmpty() {
|
||||||
// DeleteSession 删除会话
|
values[i] = nil // 未命中时存nil
|
||||||
func (m *sessionManager) DeleteSession(sessionID string) error {
|
} else {
|
||||||
return m.sessionStore.Del(gctx.New(), sessionID)
|
values[i] = result.Val()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SessionExists 检查会话是否存在
|
|
||||||
func (m *sessionManager) SessionExists(sessionID string) (bool, error) {
|
|
||||||
return m.sessionStore.Contains(context.Background(), sessionID)
|
|
||||||
}
|
}
|
||||||
|
return values, nil
|
||||||
// SetUserOnline 设置用户在线状态
|
|
||||||
func (m *sessionManager) SetUserOnline(userID uint32, serverID uint16) error {
|
|
||||||
return m.userOnlineStore.Set(gctx.New(), gconv.String(userID), serverID, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUserOnline 获取用户在线状态
|
|
||||||
func (m *sessionManager) GetUserOnline(userID uint32) (uint16, error) {
|
|
||||||
return m.userOnlineStore.Get(context.Background(), gconv.String(userID))
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteUserOnline 删除用户在线状态
|
|
||||||
func (m *sessionManager) DeleteUserOnline(userID uint32) error {
|
|
||||||
return m.userOnlineStore.Del(gctx.New(), gconv.String(userID))
|
|
||||||
}
|
|
||||||
|
|
||||||
// UserOnlineExists 检查用户在线状态是否存在
|
|
||||||
func (m *sessionManager) UserOnlineExists(userID uint32) (bool, error) {
|
|
||||||
return m.userOnlineStore.Contains(context.Background(), gconv.String(userID))
|
|
||||||
}
|
}
|
||||||
|
|||||||
210
common/data/share/task.go
Normal file
210
common/data/share/task.go
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
package share
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blazing/cool"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gogf/gf/v2/os/gctx"
|
||||||
|
"github.com/gogf/gf/v2/util/gconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// newDailyCounterStore 创建每日计数器缓存实例
|
||||||
|
func newDailyCounterStore() *cacheStore[int64] {
|
||||||
|
return &cacheStore[int64]{
|
||||||
|
manager: cool.CacheManager,
|
||||||
|
prefix: "blazing:dailycounter:",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// newCounterLimitStore 创建计数器限制配置缓存实例
|
||||||
|
func newCounterLimitStore() *cacheStore[int64] {
|
||||||
|
return &cacheStore[int64]{
|
||||||
|
manager: cool.CacheManager,
|
||||||
|
prefix: "blazing:counterlimit:",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// counterManager 计数器管理器(每日计数+限制配置+批量查询)
|
||||||
|
type counterManager struct {
|
||||||
|
dailyCounterStore *cacheStore[int64] // 每日计数器缓存
|
||||||
|
counterLimitStore *cacheStore[int64] // 计数器限制配置缓存
|
||||||
|
}
|
||||||
|
|
||||||
|
// newCounterManager 创建计数器管理器实例
|
||||||
|
func newCounterManager() *counterManager {
|
||||||
|
return &counterManager{
|
||||||
|
dailyCounterStore: newDailyCounterStore(),
|
||||||
|
counterLimitStore: newCounterLimitStore(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// genDailyKey 生成单个奖品类型的每日计数键
|
||||||
|
// 格式:{userID}:{rewardType}:{date}(userID和rewardType为uint32)
|
||||||
|
func (m *counterManager) genDailyKey(userID uint32, rewardType uint32) string {
|
||||||
|
date := time.Now().Format("20060102") // 当日日期(YYYYMMDD)
|
||||||
|
return fmt.Sprintf("%d:%d:%s", userID, rewardType, date)
|
||||||
|
}
|
||||||
|
|
||||||
|
// genUserDailyPrefix 生成用户当日所有计数的键前缀(用于扫描)
|
||||||
|
func (m *counterManager) genUserDailyPrefix(userID uint32) string {
|
||||||
|
date := time.Now().Format("20060102")
|
||||||
|
return fmt.Sprintf("%d:*:%s", userID, date) // 格式:{userID}:*:{date}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDailyCount 获取用户当日某奖品的计数
|
||||||
|
func (m *counterManager) GetDailyCount(userID uint32, rewardType uint32) (int64, error) {
|
||||||
|
key := m.genDailyKey(userID, rewardType)
|
||||||
|
return m.dailyCounterStore.Get(context.Background(), key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IncrDailyCount 增加用户当日某奖品的计数(自动设置次日0点过期)
|
||||||
|
func (m *counterManager) IncrDailyCount(userID uint32, rewardType uint32) (int64, error) {
|
||||||
|
key := m.genDailyKey(userID, rewardType)
|
||||||
|
|
||||||
|
// 获取当前计数(不存在则视为0)
|
||||||
|
current, err := m.dailyCounterStore.Get(context.Background(), key)
|
||||||
|
if err != nil && err != ErrCacheMiss {
|
||||||
|
return 0, fmt.Errorf("获取当前计数失败: %w", err)
|
||||||
|
}
|
||||||
|
if err == ErrCacheMiss {
|
||||||
|
current = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算新计数
|
||||||
|
newValue := current + 1
|
||||||
|
|
||||||
|
// 计算过期时间:到次日0点(确保当日数据自动重置)
|
||||||
|
tomorrow := time.Now().AddDate(0, 0, 1)
|
||||||
|
resetTime := time.Date(tomorrow.Year(), tomorrow.Month(), tomorrow.Day(), 0, 0, 0, 0, time.Local)
|
||||||
|
ttl := resetTime.Sub(time.Now())
|
||||||
|
|
||||||
|
// 保存新计数并设置过期时间
|
||||||
|
if err := m.dailyCounterStore.Set(gctx.New(), key, newValue, ttl); err != nil {
|
||||||
|
return 0, fmt.Errorf("保存计数失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetDailyCount 重置用户当日某奖品的计数
|
||||||
|
func (m *counterManager) ResetDailyCount(userID uint32, rewardType uint32) error {
|
||||||
|
key := m.genDailyKey(userID, rewardType)
|
||||||
|
return m.dailyCounterStore.Del(gctx.New(), key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCounterLimit 设置某奖品类型的每日限制
|
||||||
|
func (m *counterManager) SetCounterLimit(rewardType uint32, limit int64) error {
|
||||||
|
key := fmt.Sprintf("%d", rewardType) // 限制键直接使用奖品类型(uint32)
|
||||||
|
return m.counterLimitStore.Set(gctx.New(), key, limit, time.Hour*24*7) // 限制配置保留7天
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCounterLimit 获取某奖品类型的每日限制
|
||||||
|
func (m *counterManager) GetCounterLimit(rewardType uint32) (int64, error) {
|
||||||
|
key := fmt.Sprintf("%d", rewardType)
|
||||||
|
return m.counterLimitStore.Get(context.Background(), key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsExceedLimit 检查用户当日某奖品的计数是否超过限制
|
||||||
|
func (m *counterManager) IsExceedLimit(userID uint32, rewardType uint32) (bool, error) {
|
||||||
|
count, err := m.GetDailyCount(userID, rewardType)
|
||||||
|
if err != nil && err != ErrCacheMiss {
|
||||||
|
return false, fmt.Errorf("获取计数失败: %w", err)
|
||||||
|
}
|
||||||
|
// 计数不存在时视为0
|
||||||
|
if err == ErrCacheMiss {
|
||||||
|
count = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
limit, err := m.GetCounterLimit(rewardType)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("获取限制失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return count >= limit, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserAllDailyCounts 获取用户当日所有奖品类型的计数
|
||||||
|
func (m *counterManager) GetUserAllDailyCounts(userID uint32) (map[uint32]int64, error) {
|
||||||
|
// 1. 生成扫描模式(包含缓存前缀,使用*通配符)
|
||||||
|
// 格式:blazing:dailycounter:{userID}:*:{今日日期}
|
||||||
|
today := time.Now().Format("20060102")
|
||||||
|
matchPattern := fmt.Sprintf(
|
||||||
|
"%s%d:*:%s",
|
||||||
|
m.dailyCounterStore.prefix,
|
||||||
|
userID,
|
||||||
|
today,
|
||||||
|
)
|
||||||
|
|
||||||
|
// 2. 扫描所有匹配的键(通过Scan方法筛选)
|
||||||
|
keys, err := m.dailyCounterStore.Scan(context.Background(), matchPattern)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("扫描键失败: %w", err)
|
||||||
|
}
|
||||||
|
if len(keys) == 0 {
|
||||||
|
return make(map[uint32]int64), nil // 无数据时返回空map
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 提取核心键(去除缓存前缀)
|
||||||
|
coreKeys := make([]string, 0, len(keys))
|
||||||
|
prefixLen := len(m.dailyCounterStore.prefix)
|
||||||
|
for _, fullKey := range keys {
|
||||||
|
if strings.HasPrefix(fullKey, m.dailyCounterStore.prefix) {
|
||||||
|
coreKey := fullKey[prefixLen:] // 核心键格式:userID:rewardType:date
|
||||||
|
coreKeys = append(coreKeys, coreKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 批量获取计数
|
||||||
|
values, err := m.dailyCounterStore.MGet(context.Background(), coreKeys)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("批量获取计数失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 解析结果
|
||||||
|
result := make(map[uint32]int64, len(coreKeys))
|
||||||
|
for i, coreKey := range coreKeys {
|
||||||
|
parts := strings.Split(coreKey, ":")
|
||||||
|
if len(parts) != 3 {
|
||||||
|
fmt.Printf("[WARN] 忽略格式错误的键: %s\n", coreKey)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证用户ID(防止异常键干扰)
|
||||||
|
parsedUserID, err := strconv.ParseUint(parts[0], 10, 32)
|
||||||
|
if err != nil || uint32(parsedUserID) != userID {
|
||||||
|
fmt.Printf("[WARN] 忽略用户不匹配的键: %s\n", coreKey)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证日期(确保是今日)
|
||||||
|
if parts[2] != today {
|
||||||
|
fmt.Printf("[WARN] 忽略过期键: %s(日期不匹配)\n", coreKey)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析rewardType
|
||||||
|
rewardType, err := strconv.ParseUint(parts[1], 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("[WARN] 解析rewardType失败: %s\n", coreKey)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析计数
|
||||||
|
val := values[i]
|
||||||
|
if val == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
count := gconv.Int64(val)
|
||||||
|
|
||||||
|
result[uint32(rewardType)] = count
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 全局计数器管理器实例(根据实际需求初始化)
|
||||||
|
var CounterManager = newCounterManager()
|
||||||
80
common/data/share/user.go
Normal file
80
common/data/share/user.go
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
package share
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blazing/cool"
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gogf/gf/v2/os/gctx"
|
||||||
|
"github.com/gogf/gf/v2/util/gconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// newSessionStore 创建会话缓存实例
|
||||||
|
func newSessionStore() *cacheStore[uint32] {
|
||||||
|
return &cacheStore[uint32]{
|
||||||
|
manager: cool.CacheManager,
|
||||||
|
prefix: "blazing:session:",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// newUserOnlineStore 创建用户在线状态缓存实例
|
||||||
|
func newUserOnlineStore() *cacheStore[uint16] {
|
||||||
|
return &cacheStore[uint16]{
|
||||||
|
manager: cool.CacheManager,
|
||||||
|
prefix: "blazing:useronline:",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sessionManager 会话管理器
|
||||||
|
type sessionManager struct {
|
||||||
|
sessionStore *cacheStore[uint32] // 会话缓存
|
||||||
|
userOnlineStore *cacheStore[uint16] // 用户在线状态缓存
|
||||||
|
}
|
||||||
|
|
||||||
|
// newSessionManager 创建会话管理器
|
||||||
|
func newSessionManager() *sessionManager {
|
||||||
|
return &sessionManager{
|
||||||
|
sessionStore: newSessionStore(),
|
||||||
|
userOnlineStore: newUserOnlineStore(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSession 通过会话ID获取用户ID
|
||||||
|
func (m *sessionManager) GetSession(sessionID string) (uint32, error) {
|
||||||
|
return m.sessionStore.Get(context.Background(), sessionID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveSession 保存会话信息
|
||||||
|
func (m *sessionManager) SaveSession(sessionID string, userID uint32) error {
|
||||||
|
return m.sessionStore.Set(gctx.New(), sessionID, userID, time.Hour*24)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteSession 删除会话
|
||||||
|
func (m *sessionManager) DeleteSession(sessionID string) error {
|
||||||
|
return m.sessionStore.Del(gctx.New(), sessionID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionExists 检查会话是否存在
|
||||||
|
func (m *sessionManager) SessionExists(sessionID string) (bool, error) {
|
||||||
|
return m.sessionStore.Contains(context.Background(), sessionID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUserOnline 设置用户在线状态
|
||||||
|
func (m *sessionManager) SetUserOnline(userID uint32, serverID uint16) error {
|
||||||
|
return m.userOnlineStore.Set(gctx.New(), gconv.String(userID), serverID, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserOnline 获取用户在线状态
|
||||||
|
func (m *sessionManager) GetUserOnline(userID uint32) (uint16, error) {
|
||||||
|
return m.userOnlineStore.Get(context.Background(), gconv.String(userID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteUserOnline 删除用户在线状态
|
||||||
|
func (m *sessionManager) DeleteUserOnline(userID uint32) error {
|
||||||
|
return m.userOnlineStore.Del(gctx.New(), gconv.String(userID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserOnlineExists 检查用户在线状态是否存在
|
||||||
|
func (m *sessionManager) UserOnlineExists(userID uint32) (bool, error) {
|
||||||
|
return m.userOnlineStore.Contains(context.Background(), gconv.String(userID))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user