Files
portal-mini-go/internal/pkg/session/session.go
T
2026-10-25 21:00:08 +08:00

79 lines
2.1 KiB
Go

// Package session 提供会话存储能力。
// 当前默认实现为进程内内存存储(无 Redis 环境),后续可替换为 Redis 实现。
package session
import (
"context"
"sync"
"time"
"portal-mini-go/internal/pkg/errors"
)
// Store 会话存储接口,便于后续切换为 Redis 等实现。
type Store interface {
Set(ctx context.Context, key string, userID uint64, ttl time.Duration) error
Get(ctx context.Context, key string) (uint64, error)
Delete(ctx context.Context, key string) error
}
// MaSessionPrefix 是小程序用户会话键前缀。
const MaSessionPrefix = "ma:session:"
// MaSessionKey 拼接小程序用户会话键(token 为 Login 签发的会话凭证)。
func MaSessionKey(token string) string { return MaSessionPrefix + token }
// MemoryStore 进程内会话存储,适用于无 Redis 的本地/测试环境。
// 注意:多实例部署时各进程不共享,仅供单实例使用。
type MemoryStore struct {
mu sync.RWMutex
data map[string]memoryEntry
now func() time.Time
}
type memoryEntry struct {
userID uint64
expireAt time.Time
}
// NewMemoryStore 创建内存会话存储。
func NewMemoryStore() *MemoryStore {
return &MemoryStore{
data: make(map[string]memoryEntry),
now: time.Now,
}
}
// Set 写入会话,key 映射到 userID,ttl 后过期。
func (m *MemoryStore) Set(_ context.Context, key string, userID uint64, ttl time.Duration) error {
m.mu.Lock()
defer m.mu.Unlock()
m.data[key] = memoryEntry{userID: userID, expireAt: m.now().Add(ttl)}
return nil
}
// Get 读取会话对应的 userID;不存在或已过期返回 ErrUnauthorized。
func (m *MemoryStore) Get(_ context.Context, key string) (uint64, error) {
m.mu.RLock()
e, ok := m.data[key]
m.mu.RUnlock()
if !ok {
return 0, errors.ErrUnauthorized
}
if m.now().After(e.expireAt) {
m.mu.Lock()
delete(m.data, key)
m.mu.Unlock()
return 0, errors.ErrUnauthorized
}
return e.userID, nil
}
// Delete 删除会话。
func (m *MemoryStore) Delete(_ context.Context, key string) error {
m.mu.Lock()
delete(m.data, key)
m.mu.Unlock()
return nil
}