Files
Hulumath-Web/backend/accounts/models.py
T
Jacky 1f98f7152d
CI / test (push) Failing after 1m31s
v0.2 Preview: Construction Plan.
2026-08-08 21:25:27 +08:00

91 lines
3.4 KiB
Python

import uuid
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.utils import timezone
class User(AbstractUser):
class Track(models.TextChoices):
BEGINNER = "beginner", "入门"
STANDARD = "standard", "标准"
ADVANCED = "advanced", "进阶"
OPEN = "open", "Open"
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
nickname = models.CharField(max_length=40)
avatar_url = models.URLField(blank=True)
bio = models.CharField(max_length=200, blank=True)
track = models.CharField(max_length=16, choices=Track.choices, default=Track.STANDARD)
rating = models.PositiveIntegerField(default=1000)
def __str__(self):
return self.nickname or self.username
class InviteCode(models.Model):
code = models.CharField(max_length=32, unique=True)
group = models.CharField(max_length=80, blank=True)
max_uses = models.PositiveIntegerField(default=1)
used_count = models.PositiveIntegerField(default=0)
expires_at = models.DateTimeField(null=True, blank=True)
is_active = models.BooleanField(default=True)
created_by = models.ForeignKey(
User, null=True, blank=True, on_delete=models.SET_NULL, related_name="invite_codes"
)
created_at = models.DateTimeField(auto_now_add=True)
@property
def is_available(self):
return (
self.is_active
and self.used_count < self.max_uses
and (self.expires_at is None or self.expires_at > timezone.now())
)
def __str__(self):
return self.code
class InviteCodeUsage(models.Model):
invite_code = models.ForeignKey(InviteCode, on_delete=models.PROTECT, related_name="usages")
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="invite_usage")
used_at = models.DateTimeField(auto_now_add=True)
ip_address = models.GenericIPAddressField(null=True, blank=True)
class UserSession(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="login_sessions")
session_key = models.CharField(max_length=40, unique=True)
user_agent = models.CharField(max_length=300, blank=True)
ip_address = models.GenericIPAddressField(null=True, blank=True)
last_seen_at = models.DateTimeField(auto_now=True)
revoked_at = models.DateTimeField(null=True, blank=True)
class VisitorMigration(models.Model):
class Status(models.TextChoices):
PENDING = "pending", "待确认"
APPLIED = "applied", "已迁移"
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="visitor_migration")
payload = models.JSONField(default=dict)
summary = models.JSONField(default=dict)
status = models.CharField(max_length=16, choices=Status.choices, default=Status.PENDING)
created_at = models.DateTimeField(auto_now_add=True)
applied_at = models.DateTimeField(null=True, blank=True)
class AuditLog(models.Model):
actor = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
action = models.CharField(max_length=80)
target_type = models.CharField(max_length=80)
target_id = models.CharField(max_length=80, blank=True)
reason = models.CharField(max_length=300, blank=True)
metadata = models.JSONField(default=dict, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ["-created_at"]