import uuid from django.conf import settings from django.db import models class Organization(models.Model): name = models.CharField(max_length=160) class Membership(models.Model): user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) organization = models.ForeignKey(Organization, on_delete=models.PROTECT) role = models.CharField( max_length=16, choices=[("admin", "管理员"), ("analyst", "分析师"), ("viewer", "查看者")] ) departments = models.JSONField(default=list) class Dataset(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) organization = models.ForeignKey(Organization, on_delete=models.PROTECT) created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT) name = models.CharField(max_length=200) file = models.FileField(upload_to="sources/%Y/%m") sha256 = models.CharField(max_length=64) status = models.CharField(max_length=16, default="mapping") profile = models.JSONField(default=dict) mapping = models.JSONField(default=dict) quality = models.JSONField(default=dict) row_count = models.PositiveIntegerField(default=0) periods = models.JSONField(default=list) error = models.TextField(blank=True) created_at = models.DateTimeField(auto_now_add=True) started_at = models.DateTimeField(null=True) finished_at = models.DateTimeField(null=True) class Meta: constraints = [models.UniqueConstraint(fields=["organization", "sha256"], name="unique_org_source")] class Fact(models.Model): dataset = models.ForeignKey(Dataset, on_delete=models.CASCADE, related_name="facts") period = models.CharField(max_length=7) dimensions = models.JSONField(default=dict) customer = models.CharField(max_length=256, null=True) revenue = models.DecimalField(max_digits=44, decimal_places=24) cost = models.DecimalField(max_digits=44, decimal_places=24, null=True) gross = models.DecimalField(max_digits=44, decimal_places=24, null=True) quantity = models.DecimalField(max_digits=44, decimal_places=24, null=True) list_amount = models.DecimalField(max_digits=44, decimal_places=24, null=True) discount = models.DecimalField(max_digits=44, decimal_places=24, null=True) source_row = models.PositiveIntegerField() raw = models.JSONField(default=list) class Meta: indexes = [models.Index(fields=["dataset", "period"])] constraints = [models.UniqueConstraint(fields=["dataset", "source_row"], name="unique_source_row")] class Preference(models.Model): dataset = models.ForeignKey(Dataset, on_delete=models.CASCADE) user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) value = models.JSONField(default=dict) revision = models.PositiveIntegerField(default=1) class Meta: constraints = [models.UniqueConstraint(fields=["dataset", "user"], name="unique_preference")] class Investigation(models.Model): dataset = models.ForeignKey(Dataset, on_delete=models.PROTECT) user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT) key = models.CharField(max_length=64) context = models.JSONField(default=dict) status = models.CharField(max_length=16, default="open") note = models.TextField(blank=True) updated_at = models.DateTimeField(auto_now=True) class Meta: constraints = [ models.UniqueConstraint(fields=["dataset", "user", "key"], name="unique_investigation") ] class Audit(models.Model): organization = models.ForeignKey(Organization, on_delete=models.PROTECT) user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT) action = models.CharField(max_length=64) resource = models.CharField(max_length=100, blank=True) details = models.JSONField(default=dict) created_at = models.DateTimeField(auto_now_add=True) class Throttle(models.Model): key = models.CharField(max_length=64, primary_key=True) count = models.PositiveIntegerField(default=0) expires_at = models.DateTimeField() class FormulaExport(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) dataset = models.ForeignKey(Dataset, on_delete=models.CASCADE, related_name="formula_exports") user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) context = models.JSONField(default=dict) departments = models.JSONField(default=list) status = models.CharField(max_length=16, default="queued") path = models.CharField(max_length=512, blank=True) error = models.CharField(max_length=500, blank=True) created_at = models.DateTimeField(auto_now_add=True) finished_at = models.DateTimeField(null=True) expires_at = models.DateTimeField(null=True) class Meta: indexes = [models.Index(fields=["status", "created_at"], name="finance_for_status_fa1c07_idx")] class FormulaDefinition(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) dataset = models.ForeignKey(Dataset, on_delete=models.CASCADE, related_name="formula_definitions") user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) normalized_name = models.CharField(max_length=320) definition = models.JSONField() created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: constraints = [ models.UniqueConstraint( fields=["dataset", "user", "normalized_name"], name="unique_formula_definition_name" ) ]