52 lines
2.8 KiB
Python
52 lines
2.8 KiB
Python
from django.db import models
|
||
|
||
|
||
class Plan(models.Model):
|
||
"""
|
||
⚠️ اصلاح نسبت به سند اولیه: نمونهی پاسخ GET /api/subscription/plans در
|
||
سند Endpointهای API یک فیلد boolean به اسم `popular` داره («ماهانه» →
|
||
false، «سالانه» → true) که در دیاگرام مدلها (سند معماری داده) برای
|
||
Plan اصلاً ذکر نشده. چون بدون این فیلد نمیشه دقیقاً همون response
|
||
schema رو پیادهسازی کرد، اینجا اضافه شده — دقیقاً مثل فیکس is_correct
|
||
روی content.Choice در فاز ۱.
|
||
"""
|
||
title = models.CharField(max_length=50)
|
||
price = models.PositiveIntegerField()
|
||
currency = models.CharField(max_length=10, default='IRT')
|
||
discount_percent = models.PositiveSmallIntegerField(default=0)
|
||
popular = models.BooleanField(default=False)
|
||
# منبعِ صریحِ مدتِ اشتراک؛ بدونش activate_subscription مجبور بود مدت رو
|
||
# از رویِ متنِ title (مثلاً کلمهی «سالانه») حدس بزنه.
|
||
duration_days = models.PositiveIntegerField(default=30)
|
||
|
||
def __str__(self):
|
||
return self.title
|
||
|
||
|
||
class Subscription(models.Model):
|
||
"""وضعیتِ pending برای فاصلهی بینِ ساختنِ Subscription (در purchase) و
|
||
تاییدِ پرداخت (webhook)؛ فعالسازیِ واقعی توسطِ
|
||
subscription.services.activate_subscription انجام میشه."""
|
||
STATUS_CHOICES = [
|
||
('pending', 'در انتظار پرداخت'),
|
||
('active', 'فعال'),
|
||
('expired', 'منقضی'),
|
||
('canceled', 'لغوشده'),
|
||
]
|
||
|
||
user_id = models.IntegerField(unique=True, db_index=True) # ارجاع سبک به accounts.User
|
||
plan = models.ForeignKey(Plan, on_delete=models.SET_NULL, null=True, related_name='subscriptions')
|
||
status = models.CharField(max_length=15, choices=STATUS_CHOICES, default='pending')
|
||
started_at = models.DateTimeField(auto_now_add=True)
|
||
expires_at = models.DateTimeField(null=True, blank=True)
|
||
order_id = models.IntegerField(null=True, blank=True) # ارجاع سبک به payment.Order
|
||
# چون Subscription فقط یک ردیف در ازای هر user_id داره، دو خریدِ همزمانِ
|
||
# همون کاربر میتونن به وضعیتِ میانیِ کاملاً یکسانی برسن (مقایسهی
|
||
# فیلدها کافی نیست برای CAS). version با UPDATEِ اتمیکِ
|
||
# version=F('version')+1 (بدونِ select_for_update) هر نوشتنِ واقعی رو
|
||
# از نوشتنِ همزمانِ دیگری تمیز میده.
|
||
version = models.PositiveIntegerField(default=0)
|
||
|
||
def __str__(self):
|
||
return f"sub-user{self.user_id}-{self.status}"
|