from django.db import models from django.utils import timezone from datetime import timedelta from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin import uuid class UserManager(BaseUserManager): def create_user(self, email, password=None, **extra_fields): if not email: raise ValueError('邮箱地址不能为空') email = self.normalize_email(email) user = self.model(email=email, **extra_fields) if password: user.set_password(password) user.save(using=self._db) return user def create_superuser(self, email, password=None, **extra_fields): extra_fields.setdefault('is_staff', True) extra_fields.setdefault('is_superuser', True) extra_fields.setdefault('role', 'admin') if extra_fields.get('is_staff') is not True: raise ValueError('超级用户必须设置is_staff=True') if extra_fields.get('is_superuser') is not True: raise ValueError('超级用户必须设置is_superuser=True') return self.create_user(email, password, **extra_fields) class User(AbstractBaseUser, PermissionsMixin): """用户模型,用于登录和账户管理""" ROLE_CHOICES = ( ('user', '普通用户'), ('annotator', '标注员'), ('admin', '管理员'), ) email = models.EmailField(max_length=255, unique=True, verbose_name="电子邮箱") password = models.CharField(max_length=255, verbose_name="密码") username = models.CharField(max_length=255, unique=True, null=True, blank=True, verbose_name="用户名") company = models.CharField(max_length=255, blank=True, null=True, verbose_name="公司名称") name = models.CharField(max_length=255, blank=True, null=True, verbose_name="用户姓名") full_name = models.CharField(max_length=255, null=True, blank=True) role = models.CharField(max_length=50, choices=ROLE_CHOICES, default='annotator') profile_image = models.CharField(max_length=255, null=True, blank=True) is_first_login = models.BooleanField(default=True, verbose_name="是否首次登录") last_login = models.DateTimeField(blank=True, null=True, verbose_name="最近登录时间") is_active = models.BooleanField(default=True) is_staff = models.BooleanField(default=False) is_superuser = models.BooleanField(default=False, verbose_name="超级用户状态") # 时间戳 created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") updated_at = models.DateTimeField(auto_now=True, verbose_name="更新时间") objects = UserManager() USERNAME_FIELD = 'email' EMAIL_FIELD = 'email' REQUIRED_FIELDS = [] class Meta: verbose_name = "用户" verbose_name_plural = verbose_name db_table = "users" indexes = [ models.Index(fields=['username']), models.Index(fields=['email']), ] def __str__(self): return self.email @property def is_authenticated(self): return True def has_perm(self, perm, obj=None): """用户是否有特定权限""" return self.is_superuser def has_module_perms(self, app_label): """用户是否有访问特定app的权限""" return self.is_superuser class UserToken(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='tokens') token = models.CharField(max_length=40, unique=True) created_at = models.DateTimeField(auto_now_add=True) expired_at = models.DateTimeField() last_accessed = models.DateTimeField(default=timezone.now) ip_address = models.CharField(max_length=100, null=True, blank=True) user_agent = models.CharField(max_length=255, null=True, blank=True) def save(self, *args, **kwargs): if not self.expired_at: # 设置token有效期为30天 self.expired_at = timezone.now() + timedelta(days=30) super().save(*args, **kwargs) def is_expired(self): return timezone.now() > self.expired_at class Meta: db_table = 'user_token' indexes = [ models.Index(fields=['token']), models.Index(fields=['user']), ] class UserActivityLog(models.Model): id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4, editable=False) user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='activity_logs') action_type = models.CharField(max_length=50) target_type = models.CharField(max_length=50, blank=True, null=True) target_id = models.CharField(max_length=36, blank=True, null=True) details = models.TextField(blank=True, null=True) ip_address = models.CharField(max_length=45, blank=True, null=True) user_agent = models.TextField(blank=True, null=True) created_at = models.DateTimeField(default=timezone.now) class Meta: verbose_name = '用户活动日志' verbose_name_plural = '用户活动日志' class AnnotationStats(models.Model): id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4, editable=False) user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='annotation_stats') date = models.DateField() total_annotations = models.IntegerField(default=0) positive_annotations = models.IntegerField(default=0) negative_annotations = models.IntegerField(default=0) conversations_count = models.IntegerField(default=0) messages_count = models.IntegerField(default=0) created_at = models.DateTimeField(default=timezone.now) updated_at = models.DateTimeField(default=timezone.now) class Meta: verbose_name = '标注统计' verbose_name_plural = '标注统计' unique_together = ('user', 'date') class AnnotationQuality(models.Model): REVIEW_STATUS_CHOICES = ( ('pending', '待审核'), ('approved', '已通过'), ('rejected', '已拒绝'), ) id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4, editable=False) user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='annotation_quality') message_id = models.CharField(max_length=36) quality_score = models.FloatField(default=0.0) consistency_score = models.FloatField(default=0.0) review_status = models.CharField(max_length=20, choices=REVIEW_STATUS_CHOICES, default='pending') reviewer = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='reviews') review_notes = models.TextField(blank=True, null=True) created_at = models.DateTimeField(default=timezone.now) reviewed_at = models.DateTimeField(null=True, blank=True) class Meta: verbose_name = '标注质量' verbose_name_plural = '标注质量'