from django.db import models from django.utils import timezone import secrets # Status choices for tasks STATUS_CHOICES = [ ('pending', '待分配'), ('assigned', '已分配'), ('running', '执行中'), ('success', '成功'), ('failed', '失败'), ('retrying', '重试中'), ('timeout', '超时,关闭'), ] class Client(models.Model): name = models.CharField(max_length=100, unique=True, verbose_name='客户端标识') token = models.CharField(max_length=128, verbose_name='API Token') last_seen = models.DateTimeField(auto_now=True, verbose_name='最后活跃时间') created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') def __str__(self): return self.name def save(self, *args, **kwargs): # Generate a unique token if this is a new client if not self.pk and not self.token: # Use secrets module to generate a secure random token self.token = secrets.token_urlsafe(64) # 64 bytes -> ~86 characters super().save(*args, **kwargs) class Meta: verbose_name = '客户端' verbose_name_plural = '客户端' class Task(models.Model): name = models.CharField(max_length=200, verbose_name='任务名称') client_name = models.CharField(max_length=100, null=True, blank=True, verbose_name='指定执行客户端') script = models.TextField(null=True, blank=True, verbose_name='执行脚本') status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending', verbose_name='任务状态') timeout_seconds = models.IntegerField(default=259200, verbose_name='超时时间(秒)') # 默认3天 created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间') assigned_to = models.CharField(max_length=100, null=True, blank=True, verbose_name='实际执行客户端') started_at = models.DateTimeField(null=True, blank=True, verbose_name='开始执行时间') completed_at = models.DateTimeField(null=True, blank=True, verbose_name='完成时间') def __str__(self): return self.name class Meta: verbose_name = '任务' verbose_name_plural = '任务' class TaskResult(models.Model): task = models.ForeignKey(Task, on_delete=models.CASCADE, related_name='results', verbose_name='关联任务') client = models.ForeignKey(Client, on_delete=models.CASCADE, verbose_name='执行客户端') result_file = models.FileField(upload_to='task_results/', null=True, blank=True, verbose_name='结果文件') status = models.CharField(max_length=20, choices=STATUS_CHOICES, verbose_name='执行状态') message = models.TextField(null=True, blank=True, verbose_name='执行消息') created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') def __str__(self): return f"{self.task.name} - {self.client.name} - {self.status}" class Meta: verbose_name = '任务结果' verbose_name_plural = '任务结果'