feat: add billing pricing rules
This commit is contained in:
@@ -6,6 +6,10 @@ from django.db.models import Q
|
||||
from apps.users.models import ApiKey
|
||||
|
||||
|
||||
def normalize_resolution(value: str | None) -> str:
|
||||
return "" if value is None else str(value).strip().upper()
|
||||
|
||||
|
||||
class CallRecord(models.Model):
|
||||
class OperationType(models.TextChoices):
|
||||
TITLE = "title", "Generate title"
|
||||
@@ -66,6 +70,90 @@ class CallRecord(models.Model):
|
||||
return f"{self.operation_type}:{self.alias or '<default>'} {self.status}"
|
||||
|
||||
|
||||
class PricingRule(models.Model):
|
||||
operation_type = models.CharField(max_length=32, choices=CallRecord.OperationType.choices)
|
||||
alias = models.CharField(max_length=64)
|
||||
resolution = models.CharField(
|
||||
max_length=32,
|
||||
blank=True,
|
||||
default="",
|
||||
help_text="Leave blank to apply to every resolution for this alias.",
|
||||
)
|
||||
points_cost = models.BigIntegerField()
|
||||
is_active = models.BooleanField(default=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
db_table = "pricing_rule"
|
||||
ordering = ("operation_type", "alias", "resolution")
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=("operation_type", "alias", "resolution"),
|
||||
name="unique_pricing_rule_per_alias_resolution",
|
||||
),
|
||||
models.CheckConstraint(
|
||||
condition=Q(points_cost__gt=0),
|
||||
name="pricing_rule_points_cost_positive",
|
||||
),
|
||||
]
|
||||
indexes = [
|
||||
models.Index(fields=("operation_type", "alias", "is_active")),
|
||||
models.Index(fields=("operation_type", "alias", "resolution", "is_active")),
|
||||
]
|
||||
|
||||
def __str__(self) -> str:
|
||||
resolution = self.resolution or "*"
|
||||
return f"{self.operation_type}:{self.alias}:{resolution} = {self.points_cost}"
|
||||
|
||||
def clean(self) -> None:
|
||||
super().clean()
|
||||
self.alias = str(self.alias or "").strip()
|
||||
self.resolution = normalize_resolution(self.resolution)
|
||||
if not self.alias:
|
||||
raise ValidationError({"alias": "Alias is required."})
|
||||
|
||||
def save(self, *args, **kwargs) -> None:
|
||||
self.full_clean()
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class ExchangeRate(models.Model):
|
||||
currency = models.CharField(max_length=10, default="CNY")
|
||||
points_per_unit = models.DecimalField(max_digits=12, decimal_places=4)
|
||||
is_active = models.BooleanField(default=True)
|
||||
effective_from = models.DateTimeField()
|
||||
note = models.CharField(max_length=255, blank=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
db_table = "exchange_rate"
|
||||
ordering = ("-effective_from", "-id")
|
||||
constraints = [
|
||||
models.CheckConstraint(
|
||||
condition=Q(points_per_unit__gt=0),
|
||||
name="exchange_rate_points_per_unit_positive",
|
||||
),
|
||||
]
|
||||
indexes = [
|
||||
models.Index(fields=("currency", "is_active", "effective_from")),
|
||||
]
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.currency} 1 = {self.points_per_unit} points"
|
||||
|
||||
def clean(self) -> None:
|
||||
super().clean()
|
||||
self.currency = str(self.currency or "").strip().upper()
|
||||
if not self.currency:
|
||||
raise ValidationError({"currency": "Currency is required."})
|
||||
|
||||
def save(self, *args, **kwargs) -> None:
|
||||
self.full_clean()
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class PointsLedger(models.Model):
|
||||
class ChangeType(models.TextChoices):
|
||||
RECHARGE = "recharge", "Recharge"
|
||||
|
||||
Reference in New Issue
Block a user