// 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 }