update
This commit is contained in:
72
internal/repository/refresh_token_repository.go
Normal file
72
internal/repository/refresh_token_repository.go
Normal file
@ -0,0 +1,72 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"fashionapi/internal/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// RefreshTokenRepository 刷新令牌数据访问接口。
|
||||
//
|
||||
// refresh token 落库是该账号体系能做「续期 + 踢下线」的关键:
|
||||
// 服务端持有每个 login 会话的行,吊销它即可强行让对应用户重新登录。
|
||||
type RefreshTokenRepository interface {
|
||||
// Create 写入一条刷新令牌记录。
|
||||
Create(ctx context.Context, rt *model.RefreshToken) error
|
||||
// FindByHash 按 SHA256(raw) 查找,不存在时返回 ErrNotFound。
|
||||
FindByHash(ctx context.Context, hash string) (*model.RefreshToken, error)
|
||||
// Revoke 将指定 id 的令牌置为已吊销。
|
||||
Revoke(ctx context.Context, id uint64) error
|
||||
// RevokeAllByUser 吊销某用户的所有令牌(全设备登出 / 踢下线),返回受影响行数。
|
||||
RevokeAllByUser(ctx context.Context, userID uint32) (int64, error)
|
||||
// DeleteExpired 清理已过期或已吊销的令牌,避免表无限增长。
|
||||
DeleteExpired(ctx context.Context) (int64, error)
|
||||
}
|
||||
|
||||
type refreshTokenRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewRefreshTokenRepository 创建刷新令牌仓储。
|
||||
func NewRefreshTokenRepository(db *gorm.DB) RefreshTokenRepository {
|
||||
return &refreshTokenRepository{db: db}
|
||||
}
|
||||
|
||||
func (r *refreshTokenRepository) Create(ctx context.Context, rt *model.RefreshToken) error {
|
||||
return r.db.WithContext(ctx).Create(rt).Error
|
||||
}
|
||||
|
||||
func (r *refreshTokenRepository) FindByHash(ctx context.Context, hash string) (*model.RefreshToken, error) {
|
||||
var rt model.RefreshToken
|
||||
err := r.db.WithContext(ctx).Where("token_hash = ?", hash).First(&rt).Error
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &rt, nil
|
||||
}
|
||||
|
||||
func (r *refreshTokenRepository) Revoke(ctx context.Context, id uint64) error {
|
||||
return r.db.WithContext(ctx).Model(&model.RefreshToken{}).
|
||||
Where("id = ?", id).Update("revoked", 1).Error
|
||||
}
|
||||
|
||||
func (r *refreshTokenRepository) RevokeAllByUser(ctx context.Context, userID uint32) (int64, error) {
|
||||
res := r.db.WithContext(ctx).Model(&model.RefreshToken{}).
|
||||
Where("user_id = ?", userID).Update("revoked", 1)
|
||||
return res.RowsAffected, res.Error
|
||||
}
|
||||
|
||||
func (r *refreshTokenRepository) DeleteExpired(ctx context.Context) (int64, error) {
|
||||
now := uint32(time.Now().Unix())
|
||||
res := r.db.WithContext(ctx).
|
||||
Where("expires_at < ? OR revoked = ?", now, 1).
|
||||
Delete(&model.RefreshToken{})
|
||||
return res.RowsAffected, res.Error
|
||||
}
|
||||
@ -16,6 +16,8 @@ import (
|
||||
type UserRepository interface {
|
||||
// FindByID 按主键查找用户。不存在时返回 ErrNotFound。
|
||||
FindByID(ctx context.Context, id uint32) (*model.User, error)
|
||||
// FindByAccount 按用户名或邮箱查找(忽略已删除账号),供登录校验。
|
||||
FindByAccount(ctx context.Context, account string) (*model.User, error)
|
||||
}
|
||||
|
||||
type userRepository struct {
|
||||
@ -33,6 +35,15 @@ func (r *userRepository) FindByID(ctx context.Context, id uint32) (*model.User,
|
||||
return wrapUser(&u, err)
|
||||
}
|
||||
|
||||
func (r *userRepository) FindByAccount(ctx context.Context, account string) (*model.User, error) {
|
||||
var u model.User
|
||||
err := r.db.WithContext(ctx).
|
||||
Where("username = ? OR email = ?", account, account).
|
||||
Where("is_deleted = ?", 0).
|
||||
First(&u).Error
|
||||
return wrapUser(&u, err)
|
||||
}
|
||||
|
||||
func wrapUser(u *model.User, err error) (*model.User, error) {
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
|
||||
Reference in New Issue
Block a user