import uuid from django.db import models class TaskStatus(models.TextChoices): DRAFT = 'DRAFT', '草稿' OPEN = 'OPEN', '已发布' IN_PROGRESS = 'IN_PROGRESS', '进行中' IN_REVIEW = 'IN_REVIEW', '待验收' COMPLETED = 'COMPLETED', '已完成' CANCELLED = 'CANCELLED', '已取消' class ApplyStatus(models.TextChoices): PENDING = 'PENDING', '待审核' APPROVED = 'APPROVED', '已录用' REJECTED = 'REJECTED', '已拒绝' WITHDRAWN = 'WITHDRAWN', '已撤回' DELIVERED = 'DELIVERED', '交付待验收' COMPLETED = 'COMPLETED', '已完成' class Task(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) publisher = models.ForeignKey('users.User', on_delete=models.CASCADE, related_name='published_tasks') enterprise = models.ForeignKey('users.Enterprise', on_delete=models.SET_NULL, null=True, blank=True, related_name='tasks') title = models.CharField(max_length=256) description = models.TextField() skill_tags = models.JSONField(default=list) budget_min = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True) budget_max = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True) currency = models.CharField(max_length=8, default='CNY') deadline = models.DateField(null=True, blank=True) task_type = models.CharField(max_length=64, null=True, blank=True) attachments = models.JSONField(default=list) contact_user = models.ForeignKey('users.User', on_delete=models.SET_NULL, null=True, blank=True, related_name='+') contact_name = models.CharField(max_length=64, null=True, blank=True) contact_phone = models.CharField(max_length=32, null=True, blank=True) contact_email = models.EmailField(null=True, blank=True) contact_wechat = models.CharField(max_length=64, null=True, blank=True) status = models.CharField(max_length=16, choices=TaskStatus.choices, default=TaskStatus.DRAFT) assignee = models.ForeignKey('users.User', on_delete=models.SET_NULL, null=True, blank=True, related_name='assigned_tasks') assigned_at = models.DateTimeField(null=True, blank=True) completed_at = models.DateTimeField(null=True, blank=True) completion_note = models.TextField(null=True, blank=True) deliverables = models.JSONField(default=list) cancelled_at = models.DateTimeField(null=True, blank=True) cancel_reason = models.TextField(null=True, blank=True) is_deleted = models.BooleanField(default=False) is_recommended = models.BooleanField(default=False, help_text='管理员推荐') recommend_priority = models.IntegerField(default=0, help_text='推荐优先级, 越大越靠前') created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = 'tasks' ordering = ['-is_recommended', '-recommend_priority', '-created_at'] class TaskApplication(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) task = models.ForeignKey(Task, on_delete=models.CASCADE, related_name='applications') applicant = models.ForeignKey('users.User', on_delete=models.CASCADE, related_name='task_applications') cover_letter = models.TextField(null=True, blank=True) expected_days = models.IntegerField(null=True, blank=True) expected_price = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True) attachments = models.JSONField(default=list) negotiation_history = models.JSONField(default=list) status = models.CharField(max_length=16, choices=ApplyStatus.choices, default=ApplyStatus.PENDING) reject_reason = models.TextField(null=True, blank=True) reviewed_by = models.ForeignKey('users.User', on_delete=models.SET_NULL, null=True, blank=True, related_name='reviewed_applications') reviewed_at = models.DateTimeField(null=True, blank=True) deliverables = models.JSONField(default=list) completion_note = models.TextField(null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = 'task_applications' unique_together = ('task', 'applicant') ordering = ['-created_at'] class InvitationStatus(models.TextChoices): PENDING = 'PENDING', '待处理' ACCEPTED = 'ACCEPTED', '已接受' REJECTED = 'REJECTED', '已拒绝' DISCUSSING = 'DISCUSSING', '洽谈中' class TaskInvitation(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) task = models.ForeignKey(Task, on_delete=models.CASCADE, related_name='invitations') expert = models.ForeignKey('users.User', on_delete=models.CASCADE, related_name='task_invitations') message = models.TextField(null=True, blank=True) messages = models.JSONField(default=list) status = models.CharField(max_length=16, choices=InvitationStatus.choices, default=InvitationStatus.PENDING) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = 'task_invitations' unique_together = ('task', 'expert') ordering = ['-created_at'] class DeliveryStatus(models.TextChoices): PENDING = 'PENDING', '待验收' APPROVED = 'APPROVED', '已验收' REJECTED = 'REJECTED', '已驳回' class DeliveryRecord(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) application = models.ForeignKey(TaskApplication, on_delete=models.CASCADE, related_name='delivery_records') note = models.TextField() files = models.JSONField(default=list) status = models.CharField(max_length=16, choices=DeliveryStatus.choices, default=DeliveryStatus.PENDING) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = 'delivery_records' ordering = ['-created_at'] class TaskReview(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) task = models.OneToOneField(Task, on_delete=models.CASCADE, related_name='review') reviewer = models.ForeignKey('users.User', on_delete=models.CASCADE, related_name='given_reviews') reviewee = models.ForeignKey('users.User', on_delete=models.CASCADE, related_name='received_reviews') score = models.IntegerField(default=5) # 1 to 5 comment = models.TextField(null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True) class Meta: db_table = 'task_reviews' ordering = ['-created_at']