feat: add software subscription orders

This commit is contained in:
QiuSW
2026-07-21 11:52:49 +08:00
parent 341864dc70
commit bc9d1aa0f7
25 changed files with 1179 additions and 47 deletions
+105
View File
@@ -219,6 +219,110 @@ class SoftwareEntitlement(models.Model):
return self.status == self.Status.ACTIVE and self.grace_expires_at > now
class SoftwareOrder(models.Model):
class PayMethod(models.TextChoices):
WEIXIN = "weixin", "微信"
class Status(models.TextChoices):
PENDING = "pending", "待支付"
PAID = "paid", "已支付"
FAILED = "failed", "下单失败"
EXPIRED = "expired", "已过期"
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
verbose_name="用户",
on_delete=models.PROTECT,
related_name="software_orders",
)
source_plan = models.ForeignKey(
SoftwarePlan,
verbose_name="来源套餐",
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name="software_orders",
)
entitlement = models.ForeignKey(
SoftwareEntitlement,
verbose_name="发放权益",
null=True,
blank=True,
on_delete=models.PROTECT,
related_name="software_orders",
)
fulfillment_event = models.OneToOneField(
"LicenseEvent",
verbose_name="权益发放事件",
null=True,
blank=True,
on_delete=models.PROTECT,
related_name="software_order",
)
order_no = models.CharField("软件订单号", max_length=64, unique=True, editable=False)
product_code = models.CharField(
"产品代码",
max_length=32,
choices=ClientDevice.ProductCode.choices,
)
plan_name = models.CharField("套餐名称快照", max_length=120)
plan_duration_days = models.PositiveIntegerField("套餐有效天数快照")
plan_price = models.DecimalField("套餐价格快照", max_digits=12, decimal_places=2)
plan_device_limit = models.PositiveSmallIntegerField("设备数量快照")
plan_grace_days = models.PositiveSmallIntegerField("宽限天数快照", default=0)
amount_money = models.DecimalField("支付金额", max_digits=12, decimal_places=2)
currency = models.CharField("币种", max_length=8, default="CNY")
pay_method = models.CharField("支付方式", max_length=20, choices=PayMethod.choices)
status = models.CharField(
"订单状态",
max_length=20,
choices=Status.choices,
default=Status.PENDING,
)
code_url = models.TextField("二维码票据", blank=True)
expires_at = models.DateTimeField("支付票据过期时间", null=True, blank=True)
payment_txn_no = models.CharField("支付交易号", max_length=128, null=True, blank=True)
paid_at = models.DateTimeField("支付时间", null=True, blank=True)
fulfilled_at = models.DateTimeField("权益发放时间", null=True, blank=True)
created_at = models.DateTimeField("创建时间", auto_now_add=True)
updated_at = models.DateTimeField("更新时间", auto_now=True)
class Meta:
db_table = "software_order"
verbose_name = "软件套餐订单"
verbose_name_plural = "软件套餐订单"
ordering = ("-created_at", "-id")
constraints = [
models.UniqueConstraint(
fields=("pay_method", "payment_txn_no"),
name="software_order_pay_method_txn_unique",
),
models.CheckConstraint(
condition=Q(plan_duration_days__gt=0),
name="software_order_duration_positive",
),
models.CheckConstraint(
condition=Q(plan_price__gt=0),
name="software_order_plan_price_positive",
),
models.CheckConstraint(
condition=Q(plan_device_limit__gt=0),
name="software_order_device_limit_positive",
),
models.CheckConstraint(
condition=Q(amount_money__gt=0),
name="software_order_amount_positive",
),
]
indexes = [
models.Index(fields=("user", "product_code", "status")),
models.Index(fields=("status", "expires_at")),
]
def __str__(self) -> str:
return f"{self.order_no} {self.user} {self.plan_name}"
class LicenseSeat(models.Model):
entitlement = models.ForeignKey(
SoftwareEntitlement,
@@ -273,6 +377,7 @@ class LicenseEvent(models.Model):
MIGRATION_GRANTED = "migration_granted", "迁移资格授予"
CREDENTIAL_ISSUED = "credential_issued", "设备凭证签发"
CREDENTIAL_REVOKED = "credential_revoked", "设备凭证吊销"
ORDER_FULFILLED = "order_fulfilled", "套餐订单权益发放"
entitlement = models.ForeignKey(
SoftwareEntitlement,