93 lines
4.6 KiB
Markdown
93 lines
4.6 KiB
Markdown
# پایگاه داده — تسک ۱۱: پنل اشتراکی (Subscription Tiers)
|
||
|
||
## جدول: subscription_plans
|
||
|
||
| ستون | نوع | توضیح |
|
||
|------|-----|-------|
|
||
| id | INT UNSIGNED AUTO_INCREMENT PK | |
|
||
| uuid | CHAR(36) UNIQUE NOT NULL | |
|
||
| name | VARCHAR(30) NOT NULL | `'free'` \| `'basic'` \| `'professional'` |
|
||
| level | TINYINT NOT NULL | 0, 1, یا 2 |
|
||
| max_secretaries | TINYINT NOT NULL | 1, 2، یا 5 |
|
||
| features | JSON NOT NULL | `{"patient_records": bool, "services": bool, "sms_panel": bool}` |
|
||
| active | TINYINT(1) NOT NULL DEFAULT 1 | |
|
||
| created_at | INT NOT NULL | Unix timestamp |
|
||
| updated_at | INT NOT NULL | Unix timestamp |
|
||
|
||
## جدول: subscription_periods
|
||
|
||
| ستون | نوع | توضیح |
|
||
|------|-----|-------|
|
||
| id | INT UNSIGNED AUTO_INCREMENT PK | |
|
||
| uuid | CHAR(36) UNIQUE NOT NULL | |
|
||
| plan_id | INT NOT NULL FK→subscription_plans.id ON DELETE CASCADE | |
|
||
| label | VARCHAR(50) NOT NULL | مثال: `'۶ ماهه'`, `'تریال ۱ ماهه'` |
|
||
| duration_months | TINYINT NOT NULL | مدت به ماه |
|
||
| price_rials | INT NOT NULL | `0` برای تریال |
|
||
| is_trial | TINYINT(1) NOT NULL DEFAULT 0 | |
|
||
| active | TINYINT(1) NOT NULL DEFAULT 1 | |
|
||
| sort_order | TINYINT NOT NULL DEFAULT 0 | ترتیب نمایش |
|
||
| created_at | INT NOT NULL | |
|
||
| updated_at | INT NOT NULL | |
|
||
|
||
## جدول: clinic_subscriptions
|
||
|
||
| ستون | نوع | توضیح |
|
||
|------|-----|-------|
|
||
| id | INT UNSIGNED AUTO_INCREMENT PK | |
|
||
| uuid | CHAR(36) UNIQUE NOT NULL | |
|
||
| entity_type | VARCHAR(10) NOT NULL | `'doctor'` \| `'clinic'` |
|
||
| entity_id | INT NOT NULL | id دکتر یا کلینیک |
|
||
| plan_id | INT NOT NULL FK→subscription_plans.id | |
|
||
| period_id | INT NOT NULL FK→subscription_periods.id | |
|
||
| payment_id | INT NULL FK→payments.id ON DELETE SET NULL | `NULL` برای تریال |
|
||
| is_trial | TINYINT(1) NOT NULL DEFAULT 0 | |
|
||
| starts_at | INT NOT NULL | Unix timestamp |
|
||
| expires_at | INT NULL | `NULL` = بینهایت (Free) |
|
||
| created_at | INT NOT NULL | |
|
||
|
||
## ایندکسها
|
||
|
||
```sql
|
||
-- جستجوی سریع اشتراک فعال
|
||
CREATE INDEX idx_clinic_subscriptions_entity
|
||
ON clinic_subscriptions(entity_type, entity_id, expires_at);
|
||
|
||
-- جلوگیری از استفاده مکرر از تریال
|
||
-- توجه: این constraint روی is_trial=1 کار میکند چون is_trial=0 میتواند تکراری باشد
|
||
-- بنابراین در application check میکنیم نه UNIQUE index
|
||
CREATE INDEX idx_clinic_subscriptions_trial
|
||
ON clinic_subscriptions(entity_type, entity_id, is_trial);
|
||
```
|
||
|
||
## Seed Data (Migration اولیه)
|
||
|
||
```sql
|
||
-- سه پنل پایه — باید در migration ایجاد شوند
|
||
INSERT INTO subscription_plans (uuid, name, level, max_secretaries, features, active, created_at, updated_at) VALUES
|
||
(UUID(), 'free', 0, 1, '{"patient_records":false,"services":false,"sms_panel":false}', 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
|
||
(UUID(), 'basic', 1, 2, '{"patient_records":true,"services":true,"sms_panel":false}', 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
|
||
(UUID(), 'professional', 2, 5, '{"patient_records":true,"services":true,"sms_panel":true}', 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP());
|
||
|
||
-- دورههای نمونه برای basic (ادمین بعداً قیمتها را ویرایش میکند)
|
||
-- plan_id=2 = basic
|
||
INSERT INTO subscription_periods (uuid, plan_id, label, duration_months, price_rials, is_trial, active, sort_order, created_at, updated_at) VALUES
|
||
(UUID(), 2, 'تریال ۱ ماهه', 1, 0, 1, 1, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
|
||
(UUID(), 2, '۱ ماهه', 1, 250000, 0, 1, 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
|
||
(UUID(), 2, '۳ ماهه', 3, 690000, 0, 1, 2, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
|
||
(UUID(), 2, '۶ ماهه', 6, 1200000, 0, 1, 3, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
|
||
(UUID(), 2, '۱۲ ماهه', 12, 2000000, 0, 1, 4, UNIX_TIMESTAMP(), UNIX_TIMESTAMP());
|
||
```
|
||
|
||
## SiteConfig key های جدید
|
||
|
||
| کلید | نوع | مقدار پیشفرض | توضیح |
|
||
|------|-----|--------------|-------|
|
||
| `trial_enabled` | string `'1'`\|`'0'` | `'1'` | ادمین میتواند تریال را غیرفعال کند |
|
||
|
||
## نکات مهم
|
||
|
||
- `expires_at = NULL` فقط برای رکوردهای Free plan است — اینها بینهایت معتبرند
|
||
- query اشتراک فعال: `WHERE entity_type=? AND entity_id=? AND (expires_at IS NULL OR expires_at > UNIX_TIMESTAMP()) ORDER BY id DESC LIMIT 1`
|
||
- تریال check در application: `SELECT COUNT(*) FROM clinic_subscriptions WHERE entity_type=? AND entity_id=? AND is_trial=1`
|