Merge pull request 'feat: 添加 PostgreSQL 服务并挂载数据库迁移脚本' #101

Merged
huanghaosheng merged 55 commits from develop into main 2026-06-14 19:25:37 +08:00
Showing only changes of commit ce13e5a048 - Show all commits

View File

@@ -0,0 +1,120 @@
package store
import (
"context"
"sync"
"time"
"github.com/google/uuid"
)
// MemUserRepository 基于内存的 UserRepository 实现(测试用)。
type MemUserRepository struct {
mu sync.RWMutex
users map[string]*User // id -> user
byUsername map[string]string // username -> id
refreshTokens map[string]string // tokenHash -> userID
tokenExpiry map[string]time.Time // tokenHash -> expiresAt
}
// NewMemUserRepository 创建 MemUserRepository。
func NewMemUserRepository() *MemUserRepository {
return &MemUserRepository{
users: make(map[string]*User),
byUsername: make(map[string]string),
refreshTokens: make(map[string]string),
tokenExpiry: make(map[string]time.Time),
}
}
func (r *MemUserRepository) Create(_ context.Context, username, passwordHash string) (string, error) {
r.mu.Lock()
defer r.mu.Unlock()
if _, exists := r.byUsername[username]; exists {
return "", ErrUsernameTaken
}
id := uuid.New().String()
now := time.Now()
user := &User{
ID: id,
Username: username,
PasswordHash: passwordHash,
CreatedAt: now,
UpdatedAt: now,
}
r.users[id] = user
r.byUsername[username] = id
return id, nil
}
func (r *MemUserRepository) FindByUsername(_ context.Context, username string) (*User, error) {
r.mu.RLock()
defer r.mu.RUnlock()
id, ok := r.byUsername[username]
if !ok {
return nil, ErrUserNotFound
}
u := r.users[id]
copy := *u
return &copy, nil
}
func (r *MemUserRepository) FindByID(_ context.Context, id string) (*User, error) {
r.mu.RLock()
defer r.mu.RUnlock()
u, ok := r.users[id]
if !ok {
return nil, ErrUserNotFound
}
copy := *u
return &copy, nil
}
func (r *MemUserRepository) SaveRefreshToken(_ context.Context, userID, tokenHash string, expiresAt time.Time) error {
r.mu.Lock()
defer r.mu.Unlock()
r.refreshTokens[tokenHash] = userID
r.tokenExpiry[tokenHash] = expiresAt
return nil
}
func (r *MemUserRepository) FindRefreshToken(_ context.Context, tokenHash string) (string, error) {
r.mu.RLock()
defer r.mu.RUnlock()
userID, ok := r.refreshTokens[tokenHash]
if !ok {
return "", ErrRefreshTokenNotFound
}
if time.Now().After(r.tokenExpiry[tokenHash]) {
return "", ErrRefreshTokenNotFound
}
return userID, nil
}
func (r *MemUserRepository) DeleteRefreshToken(_ context.Context, tokenHash string) error {
r.mu.Lock()
defer r.mu.Unlock()
delete(r.refreshTokens, tokenHash)
delete(r.tokenExpiry, tokenHash)
return nil
}
func (r *MemUserRepository) DeleteUserRefreshTokens(_ context.Context, userID string) error {
r.mu.Lock()
defer r.mu.Unlock()
for hash, uid := range r.refreshTokens {
if uid == userID {
delete(r.refreshTokens, hash)
delete(r.tokenExpiry, hash)
}
}
return nil
}