Files

79 lines
5.6 KiB
Markdown
Raw Permalink Normal View History

# جدا شدن جدول ادمین‌ها از کارشناسان فروش
## چه چیزی عوض شد
| | قبل | بعد |
|---|---|---|
| پرسنل پنل (مدیر ارشد، تأییدکننده، سرپرست کارگاه، حسابدار) | جدول `AdminUser` | جدول جدید **`Admin`** |
| کارشناس فروش | جدول `AdminUser` | جدول `AdminUser` (**بدون هیچ تغییری**) |
| یکتایی شماره موبایل | یک شماره = فقط یک نقش | یکتا **در هر جدول جدا**؛ یک شماره می‌تواند هم ادمین باشد هم کارشناس |
ادمین فقط برای پنل است. کارشناس فقط برای اپ‌ها. توکن اپ کارشناس هیچ‌وقت به پنل دسترسی نمی‌دهد
(`JwtStrategy` فقط در `Admin` می‌گردد).
## مایگریشن — `20261026120000_split_admin_table`
فقط **افزودنی** است. به `AdminUser` (کارشناس‌ها) چیزی نمی‌نویسد، حذف نمی‌کند و قفل نمی‌کند.
1. جدول `Admin` را می‌سازد.
2. ردیف‌های غیرکارشناس `AdminUser` را با **همان `id`** در `Admin` کپی می‌کند
(توکن‌های صادرشده و همهٔ ستون‌های «چه کسی انجام داد» معتبر می‌مانند).
3. کلید خارجی‌های «ثبت‌کننده» را از `AdminUser` به `Admin` منتقل می‌کند. **نام ستون‌ها و دیتا عوض نمی‌شود**،
فقط مقصد constraint:
`LedgerEntry.postedByAdminUserId`، `OrderStatusEvent` و `PreOrderStatusEvent.actorAdminUserId`،
`BranchAllocation.allocatorAdminUserId`، `SmsMessage` و `Notification.sentByAdminUserId`،
`UploadedFile.uploadedByAdminUserId`، `Workshop.supervisorId`، `AdminSession.adminUserId`.
4. به‌صورت `NOT VALID` اضافه و بعد validate می‌شوند؛ اگر ردیف قدیمی‌ای به id غیرادمین اشاره کند، deploy شکست
نمی‌خورد و فقط یک `NOTICE` چاپ می‌شود.
بدون تغییر ماندند (چون دربارهٔ کارشناس‌اند): `Order.salesAgentId`، `Customer.referredByAgentId`،
`SalesExpertProfile.adminUserId`.
قابل اجرای مجدد است (idempotent).
### آزمایش‌شده روی
کپی دیتابیس با ۱۲۰۰۰ کارشناس: چک‌سام (md5) تمام ردیف‌های `SALES_EXPERT` قبل و بعد از مایگریشن **یکسان** بود.
زنجیرهٔ کامل مایگریشن‌ها روی دیتابیس خالی اجرا شد و `prisma migrate diff` هیچ اختلافی بین `schema.prisma`
و دیتابیس نشان نداد.
## قبل از دیپلوی روی سرور اصلی
1. از دیتابیس **بکاپ** بگیرید.
2. (اختیاری، فقط‌خواندنی) ببینید ردیفی هست که به id غیرادمین اشاره کند؛ برای هر جدول:
```sql
-- نمونه برای دفتر حساب؛ برای بقیهٔ جدول‌ها/ستون‌ها همین الگو
SELECT count(*) FROM "LedgerEntry" l
WHERE l."postedByAdminUserId" IS NOT NULL
AND NOT EXISTS (SELECT 1 FROM "Admin" a WHERE a."id" = l."postedByAdminUserId");
```
قبل از اجرای مایگریشن `Admin` هنوز وجود ندارد؛ این کوئری را بعد از دیپلوی بزنید. جواب `0` یعنی همه چیز سالم است.
3. ابتدا مایگریشن (با بوت شدن API جدید خودکار اجرا می‌شود)، سپس همان کد.
چون additive است، کد قدیمی در فاصله هم کار می‌کند.
## یک محدودیت تا «فاز ۲»
ردیف‌های قدیمی ادمین‌ها در `AdminUser` **باقی می‌مانند** (غیرفعال، بدون استفاده در کد). شمارهٔ آن‌ها هنوز در
`AdminUser` قفل است، پس برای **ادمین‌های موجود** ساختن کارشناس با همان شماره خطای «قبلاً ثبت شده» می‌دهد.
(ادمین‌های تازه‌ساخته و شماره‌های جدید مشکلی ندارند.)
برای آزاد کردن آن شماره‌ها، بعد از اطمینان از درستی کار:
`apps/api/prisma/manual/20261026_remove_dormant_admin_rows.sql`
— دستی است، عمداً داخل `migrations/` نیست، فقط ردیف‌های **غیرکارشناس** را که در `Admin` کپی دارند حذف می‌کند،
و اگر تعداد کارشناس‌ها یک عدد هم تغییر کند خودش rollback می‌کند.
## برگشت (Rollback)
کد قبلی را دوباره deploy کنید. چیزی حذف نشده، پس کد قدیمی همهٔ ادمین‌های پیشین را در `AdminUser` پیدا می‌کند.
ادمین‌هایی که **بعد** از جدا شدن ساخته شده‌اند فقط در `Admin` هستند و باید دوباره ساخته شوند.
## تغییر رفتار پنل
- جدول «کاربران» فقط ادمین‌ها را نشان می‌دهد؛ کارشناس‌ها از بخش کارشناسان.
- نقش «کارشناس فروش» از لیست نقش‌های فرم ادمین حذف شد؛ فرم کارشناس همان `POST admin/users` را با
`role: SALES_EXPERT` می‌زند و در `AdminUser` ساخته می‌شود.
- نقش یک ادمین به کارشناس (و برعکس) قابل تغییر نیست، چون دو جدول جدا هستند.
- کارشناس دیگر نمی‌تواند با رمز خودش وارد پنل شود (قبلاً چون در همان جدول بود، می‌توانست).