Files
clinicpro/docs/phase2_taskes/task-11-subscription/database.md
T

93 lines
4.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# پایگاه داده — تسک ۱۱: پنل اشتراکی (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`