""" 验证码领域实体 """ from dataclasses import dataclass, field from datetime import datetime, timedelta, timezone from uuid import uuid4 @dataclass(slots=True) class VerificationCode: """验证码(邮箱/手机统一)""" id: str recipient: str # 邮箱或手机号 code: str code_type: str # email_bind / phone_bind / email_login / phone_login / reset_password expires_at: datetime used_at: datetime | None = None attempts: int = 0 created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc)) @classmethod def create( cls, recipient: str, code_type: str, ttl_seconds: int = 300, custom_code: str | None = None, ) -> "VerificationCode": """创建验证码""" import random code = custom_code or "".join(random.choices("0123456789", k=6)) now = datetime.now(timezone.utc) return cls( id=uuid4().hex, recipient=recipient.strip(), code=code, code_type=code_type, expires_at=now + timedelta(seconds=ttl_seconds), created_at=now, ) @property def is_expired(self) -> bool: """是否已过期""" return datetime.now(timezone.utc) > self.expires_at @property def is_used(self) -> bool: """是否已使用""" return self.used_at is not None @property def is_valid(self) -> bool: """是否有效(未过期且未使用)""" return not self.is_expired and not self.is_used def mark_used(self) -> None: """标记为已使用""" self.used_at = datetime.now(timezone.utc) def increment_attempts(self) -> None: """增加尝试次数""" self.attempts += 1