package model import ( "time" "gorm.io/gorm" ) // Session 用户登录会话 // // 用于支持令牌撤销: // - access token 携带 SessionID(JWT sid),中间件校验会话是否仍有效。 // - refresh token 同样携带 SessionID,刷新时轮换并校验。 // - 登出/封禁时撤销会话,旧 token 在 access TTL 内可见但 refresh 立即失效。 // // RefreshTokenHash 存储 refresh token 的 SHA256 哈希,便于按 refresh token 反查会话, // 不直接存储 token 明文以降低泄露风险。 type Session struct { ID string `json:"id" gorm:"type:varchar(36);primaryKey"` UserID string `json:"user_id" gorm:"type:varchar(36);index;not null"` RefreshTokenHash string `json:"-" gorm:"type:varchar(64);uniqueIndex;not null"` UserAgent string `json:"user_agent" gorm:"type:varchar(255)"` IP string `json:"ip" gorm:"type:varchar(45)"` IssuedAt time.Time `json:"issued_at"` ExpiresAt time.Time `json:"expires_at" gorm:"index"` RevokedAt *time.Time `json:"revoked_at,omitempty" gorm:"index"` LastUsedAt time.Time `json:"last_used_at"` CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime"` UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime"` } func (Session) TableName() string { return "sessions" } // BeforeCreate 创建前生成 UUID。 func (s *Session) BeforeCreate(tx *gorm.DB) error { SetUUIDIfEmpty(&s.ID) return nil } // IsRevoked 会话是否已撤销。 func (s *Session) IsRevoked() bool { return s != nil && s.RevokedAt != nil } // IsExpired 会话是否已过期(按 ExpiresAt)。 func (s *Session) IsExpired(now time.Time) bool { return s != nil && !s.ExpiresAt.IsZero() && now.After(s.ExpiresAt) } // IsValid 会话是否仍有效(未撤销且未过期)。 func (s *Session) IsValid(now time.Time) bool { return s != nil && !s.IsRevoked() && !s.IsExpired(now) }