Files
tehran_tala/docs/GO-LIVE-PLAN.md
T

358 lines
30 KiB
Markdown
Raw Normal View History

2026-10-04 20:11:09 +03:30
# پلن رفتن به تولید — ارتقای پشته، جداسازی انتشار، جابه‌جایی از henza
سند برنامه‌ریزی، نوشته‌شده در ۲۰۲۶-۱۰-۰۴. **هیچ قدمی هنوز اجرا نشده.**
> این سند طوری نوشته شده که **روی سیستم دیگری، بدون گفتگوی قبلی، قابل ادامه باشد.** از §۰ شروع کنید.
>
> نسخه‌ها و تاریخ‌های این سند مال روز نوشتن‌اند. قبل از اجرای هر قدم، با
> `node scripts/check-majors.mjs` دوباره بسنجید.
---
## خلاصه
- **هدف:** سیستم زنده‌ی henza که الان زیر بار است، روی پشته‌ی جدید برود — **با همان ظاهر برای کاربر**، سریع‌تر و امن‌تر.
- **تصمیم مخزن:** پروژه‌ی جدید ساخته نمی‌شود. همین مونوریپو «نسخه‌ی بعدی تولید» است و امنیت تولید با **جداسازی انتشار** تأمین می‌شود (§۱).
- **سرعت از بک‌اند می‌آید، نه از بازنویسی ظاهر.** اپ کاربر قدیم همین حالا Vue 3 است (§۲).
- **بزرگ‌ترین ریسک داده است، نه کد.** ابزار انتقال داده از دیتابیس henza به Postgres هنوز وجود ندارد (§۷).
### ترتیب اجرا
| # | قدم | بخش | وابسته به |
|---|---|---|---|
| 1 | راه‌اندازی روی هر سیستم + commit و push اسناد | §۰ | — |
| 2 | اندازه‌گیری مبنا روی سیستم زنده | §۳ | — |
| 3 | ارتقای پشته | §۵ | — |
| 4 | جداسازی انتشار | §۶ | — |
| 5 | پورت اپ کلاسیک | `docs/PWA-PORT-PLAN.md` | تصمیم‌های مالک در آن سند |
| 6 | ابزار مهاجرت داده و تطبیق | §۷ | — (موازی با ۵ قابل انجام است) |
| 7 | اجرای موازی روی staging | §۸ | ۵ و ۶ |
| 8 | بستن مسدودکننده‌ها | §۹ | — |
| 9 | روز جابه‌جایی | §۱۰ | همه |
---
## §۰ — ادامه روی سیستم دیگر
### چه چیزی با git منتقل می‌شود و چه چیزی نه
| چیز | در git مونوریپو؟ | روی سیستم جدید |
|---|---|---|
| کد مونوریپو و `docs/` | بله، **بعد از commit و push** | clone از `http://get.feep.ir/s.esmaeili/gold-bank.git` |
| همین سند، `docs/PWA-PORT-PLAN.md` و `scripts/check-majors.mjs` | **امروز نه** — هنوز commit نشده‌اند | قبل از ترک این سیستم، commit و push |
| `henza_pwa`، `henza_server`، `henza_admin` | **نه** — هر کدام مخزن git جدای خودش است و مونوریپو آن‌ها را track نمی‌کند | جدا clone و روی commit پین‌شده checkout (جدول بعد) |
| `.env` | نه — ignore شده، و باید همین‌طور بماند | از `.env.example` بسازید؛ secret ها را از جای امن بیاورید |
| `CLAUDE.md` | نه — در ریشه‌ی workspace قبلی (`E:\gold bank`) بوده و اسناد parity به §2، §3، §4 و §6 آن ارجاع می‌دهند | اگر نسخه‌اش را دارید به مخزن اضافه کنید؛ بازسازی قواعدش در `docs/PWA-PORT-PLAN.md` آمده |
| skill `reference-parity-audit` | نه — روی ماشین قبلی | اگر دارید منتقل کنید؛ وگرنه D12 و همین اسناد روش را می‌گویند |
| حافظه‌ی Claude | نه — محلی است | همین اسناد جایش را می‌گیرند |
### مخزن‌های مرجع — commit های پین‌شده
| مخزن | remote | commit | نکته |
|---|---|---|---|
| `henza_pwa` | `http://get.feep.ir/henzagold/henza_pwa.git` | `b171fff` (2026-09-02) | مرجع پورت کلاسیک |
| `henza_server` | `http://get.feep.ir/henzagold/henza_server.git` | `37c7d7a` (2026-09-17) | روی سیستم فعلی سه فایل حذف‌شده‌ی commit‌نشده دارد (`resources/js/app.js`، `resources/js/bootstrap.js`، `webpack.mix.js`). این‌ها اسکلت Laravel Mix‌اند و به API ربطی ندارند؛ clone تازه آن‌ها را دارد و در parity فرقی ایجاد نمی‌کند |
| `henza_admin` | `http://get.feep.ir/henzagold/henza_admin.git` | `1b67448` (2026-08-11) | — |
```bash
git clone http://get.feep.ir/s.esmaeili/gold-bank.git
cd gold-bank
git checkout release/delivery # یا هر شاخه‌ای که این اسناد رویش commit شده‌اند
git clone http://get.feep.ir/henzagold/henza_pwa.git && git -C henza_pwa checkout b171fff
git clone http://get.feep.ir/henzagold/henza_server.git && git -C henza_server checkout 37c7d7a
git clone http://get.feep.ir/henzagold/henza_admin.git && git -C henza_admin checkout 1b67448
cp .env.example .env # بعد secret ها را پر کنید
pnpm install --frozen-lockfile
```
### دو تله
1. **پوشه‌های `henza_*` در `.gitignore` نیستند.** یک `git add .` یا `git add -A` آن‌ها را به‌صورت «embedded repository» اضافه می‌کند، و clone روی سیستم دیگر به‌جایشان پوشه‌ی خالی می‌گیرد. یا این سه نام به `.gitignore` اضافه شوند، یا همیشه فایل‌ها با نام add شوند.
2. **remote ها `http://` هستند، نه `https://`.** نام کاربری و رمز git بدون رمزنگاری منتقل می‌شوند.
### ابزار روی سیستم جدید
- **Node 24.** سیستم فعلی 24.21 دارد؛ `engines` در `package.json` می‌گوید `>=20`؛ ایمیج Docker API روی 20 است (§۵.۱).
- **pnpm 9** (`packageManager: pnpm@9.0.0`).
- **build روی خود ماشین انجام می‌شود، نه داخل Docker** (DEPLOY-03). Dockerfile ها فقط خروجی ساخته‌شده (`release/` و `dist/`) را کپی می‌کنند، پس Node و سیستم‌عامل ماشین build روی خروجی اثر دارند. قبل از `docker compose build` همیشه `pnpm build:release` اجرا شود.
- **ترتیب خواندن:** همین سند ← `docs/parity/PROGRESS.md` ← `docs/parity/DECISIONS.md` ← `docs/PWA-PORT-PLAN.md`.
---
## §۱ — تصمیم مخزن: پروژه‌ی جدید نه
**وضعیت:** پیشنهاد (۲۰۲۶-۱۰-۰۴)؛ مالک خواست نوشته شود.
1. **اپ‌ها و API با هم تکامل پیدا می‌کنند.** اپ کلاسیک برای ۱۸ endpoint تازه به `apps/api` وابسته است، و ادمین و اپ‌ها یک دیتابیس و یک schema دارند. جدا کردن اپ و API در دو مخزن یعنی هر تغییر دو PR، و ناهمخوانی نسخه‌ی کلاینت و سرور.
2. **امنیت تولید از جداسازی انتشار می‌آید، نه جداسازی مخزن.** هر اپ همین حالا Dockerfile و سرویس جدا دارد. فقط `build:release` همه را با هم می‌سازد، پس خرابی یک اپ آزمایشی انتشار تولید را هم می‌خواباند. راه‌حل در §۶.
3. **تاریخچه و قواعد اینجاست.** تاریخچه‌ی git و کل `docs/parity` (تصمیم‌ها، PROTECTED، ISSUES) در همین مخزن است؛ مخزن تازه یا گمشان می‌کند یا دو نسخه می‌سازد.
**چه وقت پروژه‌ی جدید درست است:** اگر `user-app` و `user-app-v2` قرار است محصول، برند یا تیم دیگری شوند. اگر قرار است هرگز منتشر نشوند، بایگانی‌شان کنید (یک tag، و خروج از انتشار) — باز هم بدون مخزن جدید. حذفشان طبق D18 و `PROTECTED.md` تأیید صریح مالک می‌خواهد.
---
## §۲ — واقعیت درباره‌ی «سرعت»
| بخش | در تولید امروز | وضعیت | جایگزین در مونوریپو |
|---|---|---|---|
| سرور | Laravel 8 + PHP `^7.3` (`henza_server/composer.json`) | پشتیبانی امنیتی Laravel 8 در ژانویه‌ی ۲۰۲۳ و PHP 7.x در ۲۰۲۲ تمام شده | `apps/api` — NestJS + Prisma + Postgres |
| ادمین | Vue 2.6 + Vuex 3 + Vuesax + Tailwind 1 + vue-cli 3 (`henza_admin/package.json`) | Vue 2 پایان ۲۰۲۳ تمام شد | `apps/admin-panel` — Vue 3 |
| اپ کاربر | Vue 3 + Vite + Tailwind 3 — lockfile آن Vue 3.5.40 دارد و هر ۲۳ route آن lazy است | **پشته به‌روز است** | `apps/user-app-classic` (`docs/PWA-PORT-PLAN.md`) |
پس بردن اپ کاربر روی «Vue 3، Tailwind، Pinia» به‌خودی‌خود سریعش نمی‌کند. اهرم‌های واقعی سرعت در آن:
1. **کل CSS ant-design** با `import 'ant-design-vue/dist/antd.less'` (`henza_pwa/src/main.ts:7`) → فقط استایل کامپوننت‌های استفاده‌شده.
2. **کل CSS vant، دو بار:** `vant/lib/index.css` هم در `main.ts:10` و هم در `main.ts:11`.
3. **درخواست‌های تکراری:** `/price` از دو مسیر جدا (`store/store.ts:114` و `Dashboard.vue:634`)؛ `/front` دو بار (`home/index.vue:184` و `Mixins/getProducts.ts:46`).
4. **بدون صفحه‌بندی:** `/get-orders` و `/get-my-findoc` کل داده را برمی‌گردانند.
5. **گزارش حساب** از یک سیستم حسابداری بیرونی proxy می‌شد (D24)؛ در هدف از دفتر داخلی خوانده می‌شود.
همه‌ی این‌ها زیر کاپوت‌اند و ظاهر را عوض نمی‌کنند، پس با قرارداد «دقیقاً مثل مرجع» در `docs/PWA-PORT-PLAN.md` سازگارند.
---
## §۳ — اندازه‌گیری مبنا (قدم ۲)
بدون عدد «قبل»، معلوم نیست کندی کجاست و بعداً هم نمی‌شود نشان داد سریع‌تر شده. **روی تولید آزمون بار (load test) اجرا نشود.**
| لایه | ابزار | چه چیزی ثبت شود |
|---|---|---|
| اپ — مهمان | `npx lighthouse https://app.henzagold.com/home --only-categories=performance --output=json --output-path=baseline-home.json` (پیش‌فرض Lighthouse شبیه‌سازی موبایل است)؛ همین برای `/search` و یک `/product/<id>` | LCP، TBT، حجم JS و CSS منتقل‌شده، تعداد درخواست‌ها |
| اپ — واردشده | Chrome DevTools ← Performance و Network، روی یک گوشی متوسط واقعی | زمان تا نمایش میز کار؛ تعداد و زمان درخواست‌های شروع |
| سرور | لاگ nginx با `$request_time` یا لاگ Laravel، در یک روز کاری | p50 و p95 برای `/front`، `/get-products`، `/me-data`، `/get-orders`، `/get-my-findoc`، `/price`، `/fin_tahesab/*`، `/set-order` |
| دیتابیس | slow query log (مثلاً بالای 0.5 ثانیه)، یک روز | ده کوئری کندتر |
| سنجه | مبنا (henza) | بعد از جابه‌جایی |
|---|---|---|
| LCP خانه (موبایل) | | |
| حجم JS + CSS خانه | | |
| تعداد درخواست تا میز کار | | |
| p95 `/front` | | |
| p95 `/get-products` | | |
| p95 `/set-order` | | |
---
## §۴ — استاندارد نسخه‌ها
«دو نسخه عقب‌تر» به‌عنوان قاعده‌ی ثابت جواب نمی‌دهد: دو نسخه قبل از vue-router فعلی، router مخصوص Vue 2 است که مرده؛ و دو نسخه قبل از NestJS فعلی، همان نسخه‌ای است که الان دارید و از ژانویه patch نگرفته.
**قاعده:** جدیدترین major که حدود ۶ ماه از انتشارش گذشته، هنوز patch می‌گیرد و وابستگی‌ها پشتیبانی‌اش می‌کنند. **دو استثنا:**
- **ابزارهای زمان build** (TypeScript، vue-tsc، Vite) روی سرور اجرا نمی‌شوند؛ patch نگرفتنشان ریسک امنیتی زمان اجرا ندارد.
- **کتابخانه‌هایی که ظاهر را می‌سازند** مستقل از سنشان ثابت می‌مانند (جدول دوم).
داده‌ی npm در ۲۰۲۶-۱۰-۰۴ — با `node scripts/check-majors.mjs` تازه کنید:
| بسته | جدیدترین | پیشنهاد | الان در مونوریپو | خط فعلی آخرین بار کی patch شد | نوع |
|---|---|---|---|---|---|
| Node | 26 (هنوز LTS نیست) | **24 LTS** | Docker API: **20** | Node 20 از ۲۰۲۶-۰۴-۳۰ پشتیبانی ندارد | زمان اجرا |
| NestJS | 12.1.2 (یک‌ماهه) | **11.x** — فعال | `^10.4.0` | 10.4.22 در ۲۰۲۶-۰۱-۱۰ | زمان اجرا |
| Prisma | 8 (RC) / 7.10.0 | **6.19.x** حالا؛ 7 بعد از تولید | `^5.19.0` | 5.22.0 در ۲۰۲۴-۱۱-۰۵ | زمان اجرا |
| ایمیج nginx | `latest` | **tag ثابت** | `latest` | — | زمان اجرا |
| Vue | 3.5.43 | **3.5.x** | `^3.5.0` | فعال | کلاینت |
| vue-router | 5.3.1 | **4.6.4** (آخرین 4.x) | `^4.4.0` | 4.6.4 در ۲۰۲۵-۱۲-۱۱ | کلاینت |
| Pinia | 4.0.3 (سه‌ماهه) | **2.3.1** (آخرین 2.x) | `^2.2.0` | 2.3.1 در ۲۰۲۵-۰۱-۲۰ | کلاینت |
| Vite | 8.3.2 | **7.3.x** | `^5.4.0` | 5.4.21 در ۲۰۲۵-۱۰-۲۰ | build |
| @vitejs/plugin-vue | 6.0.9 | **6.0.x** | `^5.1.0` | 5.2.4 در ۲۰۲۵-۰۵-۰۹ | build |
| vite-plugin-pwa | 2.0.0 (یک‌روزه) | **1.3.x** | `^0.20.5` | 0.21.2 در ۲۰۲۵-۰۳-۲۱ | build |
| TypeScript | 7.0.2 (سه‌ماهه، کامپایلر تازه) | **5.9.3** | `^5.5.0` | 5.9.3 در ۲۰۲۵-۰۹-۳۰ | build |
| vue-tsc | 3.3.12 | **3.x** | `^2.1.0` | 2.2.12 در ۲۰۲۵-۰۷-۰۲ | build |
**چرا Pinia 3 نه:** خط 3 هم ۳۳۳ روز است release نداشته، و برای پروژه‌ی Vue 3 چیزی بیشتر از 2 نمی‌دهد. تنها خط فعال 4 است که هنوز سه ماه بیشتر عمر ندارد — بعداً.
**چرا Prisma 6 با اینکه ۱۸۶ روز release نداشته:** پله‌ی کوچک‌تر و کم‌ریسک‌تر از 5 است (§۵.۷)؛ مقصد بلندمدت 7 است که بعد از تولید می‌آید.
سازگاری این ترکیب بررسی شد: `@vitejs/plugin-vue@6` با Vite 5 تا 8، `vite-plugin-pwa@1.3` با Vite 3 تا 8، `vue-tsc@3` با TypeScript ≥ 5.0، Vite 7 با Node ≥ 20.19، و NestJS 11 با Node ≥ 20 جور است.
### کتابخانه‌هایی که به‌خاطر ظاهر ثابت می‌مانند
| بسته | نسخه | چرا |
|---|---|---|
| `ant-design-vue` | 3.2.20 برای اپ‌های کاربر؛ 4.2.6 برای ادمین | v4 استایل را به CSS-in-JS برده. **هیچ‌کدام از دو خط از نوامبر ۲۰۲۴ release نداشته‌اند** — ریسک بلندمدتی که به‌خاطر ظاهر پذیرفته می‌شود |
| `tailwindcss` | 3.0.18 | Tailwind 4 بازنویسی است و پیش‌فرض‌هایش ظاهر را عوض می‌کند |
| `vant` | 4.10.x (اپ کلاسیک دقیقاً 4.10.0) | — |
| `swiper` | 7.4.1 | — |
| `@vueuse/core` | 9.13.0 | مرجع با همین نسخه است؛ ارتقا رفتار `useTransition` و `useInfiniteScroll` را عوض می‌کند و تست جدا می‌خواهد |
**پین:** نسخه‌ها بدون `^` و `~`، lockfile commit‌شده، و `pnpm install --frozen-lockfile` روی ماشین build.
---
## §۵ — قدم‌های ارتقا (قدم ۳)
**قواعد همه‌ی زیرقدم‌ها:**
- هر زیرقدم یک commit یا PR جدا روی شاخه‌ی خودش (`chore/upgrade-*`)، با تست کامل، **بدون کار فیچر قاطی**.
- `admin-panel`، `user-app` و `user-app-v2` در `PROTECTED.md` هستند: ارتقای ابزارشان **تأیید صریح مالک** و اسکرین‌شات قبل و بعد از صفحه‌های اصلی می‌خواهد. هیچ تغییر ظاهری یا رفتاری مجاز نیست.
- برای دیتابیس، روند D14 و D15 برقرار است: هرگز `migrate reset` و هرگز دست زدن به `_prisma_migrations`.
ترتیب از کم‌ریسک به پرریسک است، به‌جز Node که اول می‌آید چون Vite 7 به Node ≥ 20.19 نیاز دارد و Node 20 تمام شده.
### ۵.۱ — Node 24
- **تغییر:** `apps/api/Dockerfile` از `node:20-bookworm` به `node:24-bookworm`؛ `engines.node` به `">=24 <25"`؛ فایل `.nvmrc` با مقدار `24`.
- **ممکن است بشکند:** ریسک پایین — `apps/api/package.json` هیچ وابستگی native ندارد (بررسی شد).
- **سنجش:** `pnpm build:release`؛ بالا آمدن API در Docker؛ `apps/api/scripts/auth-smoke.sh` (۴۶ assertion)؛ `pnpm test` در api.
### ۵.۲ — پین ایمیج nginx
- **تغییر:** نسخه‌ی واقعی `latest` فعلی را پیدا کنید (`docker run --rm docker.arvancloud.ir/nginx:latest nginx -v`) و در سه Dockerfile همان tag را بنویسید.
- **رفتار:** بی‌تغییر؛ فقط از این به بعد هر build همان nginx را می‌گیرد.
### ۵.۳ — پین دقیق و patch های داخل همان خط
- **تغییر:** حذف `^` و `~` از وابستگی‌های همه‌ی `package.json` ها؛ بالا بردن به آخرین patch در همان major: Vue 3.5.43، vue-router 4.6.4، Pinia 2.3.1، TypeScript 5.9.3؛ `pnpm install --frozen-lockfile` روی ماشین build و در CI.
- **سنجش:** روی یک clone تازه، `pnpm install --frozen-lockfile` بدون تغییر lockfile تمام شود؛ typecheck و build همه‌ی اپ‌ها.
### ۵.۴ — vue-tsc 3
- **ممکن است بشکند:** vue-tsc 3 خطاهای تایپی تازه در template ها پیدا می‌کند. همه درست شوند، نه خاموش.
- **سنجش:** typecheck تمیز در `admin-panel`، `user-app` و `user-app-v2`.
### ۵.۵ — Vite 7، plugin-vue 6، vite-plugin-pwa 1.3
- **مهم‌ترین تله:** طبق راهنمای مهاجرت Vite 7، پیش‌فرض `build.target` از `'modules'` به `'baseline-widely-available'` عوض شده — یعنی مرورگرهای جدیدتر (حدود Chrome 107 و Safari 16). **هیچ‌کدام از `vite.config.ts` ها `build.target` ندارند** (بررسی شد)، پس گوشی‌های قدیمی‌تر کاربران بی‌صدا از کار می‌افتند. ← `build.target` صریحاً روی مقدار قبلی، `['es2020', 'edge88', 'firefox78', 'chrome87', 'safari14']`، گذاشته شود، مگر مالک بر اساس آمار گوشی‌های کاربران تصمیم دیگری بگیرد. قبل از اجرا با راهنمای رسمی تطبیق داده شود.
- `vite-plugin-pwa@1.3` به `workbox-build` و `workbox-window` ^7.4.1 نیاز دارد.
- **سنجش:** build هر سه اپ؛ سناریوی دود (ورود، کاتالوگ، سبد)؛ نصب PWA و جریان به‌روزرسانی؛ اسکرین‌شات قبل و بعد.
### ۵.۶ — NestJS 11 (همه‌ی بسته‌های `@nestjs/*` با هم)
- **ممکن است بشکند:** NestJS 11 روی Express 5 است. route با wildcard یا `forRoutes('*')` در کد نیست (بررسی شد) — رایج‌ترین شکست صدق نمی‌کند. ولی parser پیش‌فرض query در Express 5 «simple» شده؛ هر endpoint که query تودرتو می‌خواند باید چک شود. با راهنمای مهاجرت رسمی تطبیق داده شود.
- **سنجش:** jest کامل؛ `auth-smoke.sh`؛ تست‌های توکن متقابل (T1).
### ۵.۷ — Prisma 6.19
- **ممکن است بشکند:** مهم‌ترین شکست‌های Prisma 6 اینجا صدق نمی‌کنند (بررسی شد): در migration ها رابطه‌ی many-to-many ضمنی نیست، و فیلد `Bytes` و `NotFoundError` استفاده نشده‌اند.
- **سنجش:** بعد از ارتقا، `prisma migrate diff --from-schema-datasource --to-schema-datamodel --script` باید «empty migration» بدهد؛ `prisma migrate status`؛ jest؛ دود.
### ۵.۸ — بعداً، نه قبل از تولید
Prisma 7، Pinia 4، vue-router 5، Vite 8، TypeScript 6 و 7، NestJS 12 — هر کدام وقتی پخته شد، جدا و با تست. **Tailwind 4 و ant-design-vue 4 هرگز برای اپ‌های کاربر**، چون ظاهر عوض می‌شود.
---
## §۶ — جداسازی انتشار (قدم ۴)
- **مجموعه‌ی تولید:** `apps/api`، `apps/admin-panel`، و `apps/user-app-classic` وقتی آماده شد. **آزمایشی:** `apps/user-app`، `apps/user-app-v2`.
- **اسکریپت تازه‌ی `build:prod`** فقط برای مجموعه‌ی تولید. `build:release` فعلی دست نمی‌خورد، چون `DELIVERY.md` به آن تکیه دارد.
- **`docker-compose.yml`:** سرویس‌های آزمایشی زیر `profiles: ["experimental"]` بروند، تا `docker compose up` فقط تولید را بالا بیاورد و آزمایشی‌ها با `--profile experimental` بیایند. `DELIVERY.md` هم‌زمان به‌روز شود.
- **شاخه‌ها:** `main` همیشه آماده‌ی تولید؛ کار روی شاخه‌های `chore/upgrade-*` و `feature/classic-*`؛ شاخه‌ی انتشار از `main` بریده شود. امروز همه‌چیز روی `release/delivery` است.
- **DEPLOY-03 — build داخل Docker با multi-stage:** حالا که کار ممکن است بین ماشین‌ها جابه‌جا شود، اولویتش بالاتر است. خروجی نباید به Node و سیستم‌عامل ماشین build وابسته باشد.
---
## §۷ — مهاجرت داده و تطبیق (قدم ۶) — بزرگ‌ترین شکاف
**واقعیت:** ابزار انتقال داده از دیتابیس henza به Postgres نه در مخزن هست و نه در اسناد (جست‌وجوی ۲۰۲۶-۱۰-۰۴). موتور دیتابیس henza هم از مخزن معلوم نیست — `config/database.php` پیش‌فرض `'localhost'` دارد و `.env.example` تنظیم DB ندارد. احتمالاً MySQL است؛ از سرور تولید تأیید شود.
**ویژگی‌های اجباری ابزار:**
- تکرارپذیر و idempotent؛ همیشه روی **کپی** داده‌ی تولید.
- برای هر موجودیت یک جدول نگاشت `legacyId ← id جدید` نگه دارد. لازم است برای:
- تطبیق؛
- ترتیب ۹ دسته‌ی خانه که با id عددی هاردکد شده (Q5 در `docs/PWA-PORT-PLAN.md`)؛
- لینک‌های قدیمی محصول (`/product/123`) که کاربران فرستاده‌اند — بدون نگاشت، به خانه redirect می‌شوند؛
- سبدهای باز `appCart` روی گوشی‌ها.
- یک گزارش تطبیق بسازد (جدول دوم).
| henza | مقصد | نکته |
|---|---|---|
| مشتری (`customers`) | `Customer` | نام ← `firstName`/`lastName` (D0)؛ `type` ← `CustomerKind`؛ `percent` ← `makingChargeOffsetPercent` (D9)؛ `gram_level` ← `goldCreditLimitGrams`؛ `remain` ← `goldDebtGrams`؛ `rial_remain` ← `fiatDebtRial` **با بررسی واحد** (نقص #19 در `docs/PWA-PORT-PLAN.md`) |
| کارشناس فروش (`customers.type = 1`) | `AdminUser` با نقش `SALES_EXPERT` + `SalesExpertProfile` | D17 و D21 |
| مشتری تک (`SingleCustomer`/`login`) | `Customer` با `kind = RETAIL` | D13 — داده حفظ شود، حتی اگر خارج از کار است |
| کاربران ادمین (`users`) و `permission` | `AdminUser` + `UserType` + `Permission` | — |
| محصول، تنوع، تصویر، دسته، برند، کالکشن | مدل‌های هم‌نام | **فایل‌های تصویر هم کپی شوند** (`UPLOAD_DIR`) |
| سفارش‌ها — عمده و تک، دو جدول جدا در henza | `Order` + `OrderItem` + `OrderStatusEvent` | `sts` عددی ← `OrderStatus` |
| سفارش ساخت | `PreOrder` | — |
| اسناد مالی (`cash`) | `LedgerEntry` | فقط تأییدشده‌ها در موجودی حساب می‌شوند (D24) |
| کیف پول و cashback | مدل‌های موجود | D13 — حفظ، بدون کار |
| علاقه‌مندی، تخفیف و استثنا، اعلان، شهر، تنظیمات | مدل‌های موجود یا تازه | علاقه‌مندی جدول تازه می‌خواهد (`docs/PWA-PORT-PLAN.md`) |
| بررسی تطبیق | آستانه |
|---|---|
| تعداد مشتری، کارشناس و کاربر ادمین | دقیقاً برابر |
| بدهی طلای هر مشتری (`remain` ↔ `goldDebtGrams`) | **صفر اختلاف** |
| بدهی ریالی هر مشتری | **صفر اختلاف**، بعد از تبدیل واحد |
| سقف اعتبار و آفست اجرت هر مشتری | صفر اختلاف |
| تعداد سفارش به تفکیک وضعیت؛ جمع وزن سفارش‌های باز | برابر |
| تعداد و جمع اسناد تأییدشده و تأییدنشده | برابر |
| تعداد محصول، تنوع و تصویر؛ فایل تصویر گم‌شده | برابر؛ صفر |
**شماره‌ی تکراری بین مشتری و کارشناس** قبل از مهاجرت فهرست شود. یکتایی شماره بین `Customer` و `AdminUser` امروز اجرا نمی‌شود — هر چهار بررسی تداخل فقط `adminUser` را کوئری می‌کنند — و در `ISSUES.md` هم ثبت نشده. قاعده‌ی مالک در D21: «کارشناس برنده است».
---
## §۸ — اجرای موازی روی staging (قدم ۷)
- یک دامنه‌ی staging؛ هر شب داده‌ی تولید با ابزار §۷ منتقل و تطبیق شود.
- **پیامک در staging خاموش، یا فقط به شماره‌ی تست.** داده‌ی واقعی یعنی شماره‌ی واقعی مشتری‌ها.
- کاربران داخلی جریان‌های واقعی را روی staging امتحان کنند.
- **معیار آمادگی:** مثلاً سه شب پیاپی تطبیق تمیز، و هیچ باگ مسدودکننده‌ی باز.
---
## §۹ — مسدودکننده‌های قبل از تولید (قدم ۸)
| مورد | منبع |
|---|---|
| مقدار جدا برای `JWT_CUSTOMER_SECRET` (REL-03) | `DELIVERY.md` §A1 |
| حذف `OTP_MASTER_CODE` (REL-01) | §A2 |
| `JWT_SECRET` واقعی | §A3 |
| اعتبارنامه‌ی SMS (REL-02) | §A4 |
| چرخاندن اعتبارنامه‌های نشت‌کرده: پنل SMS (SEC-12)، توکن‌های PayPing و Tahesab و pepper کیف پول (SEC-06). **هم‌زمان با سیستم قدیم**، چون همان حساب‌های زنده‌اند؛ چرخاندن یک‌طرفه پیامک سیستم قدیم را قطع می‌کند | §B |
| MON-08 — سرور اجرت و تخفیف را از کلاینت نپذیرد. وضعیت فعلی چک شود | `docs/parity/ISSUES.md` |
| MON-05 — اجرای `canPlaceOrders` و `verificationStep` | `docs/parity/ISSUES.md` |
| یکتایی شماره بین `Customer` و `AdminUser` | §۷ |
---
## §۱۰ — روز جابه‌جایی (قدم ۹)
### شرط‌های go/no-go
- همه‌ی موارد §۹ بسته‌اند.
- معیار آمادگی §۸ برقرار است.
- برگشت (rollback) یک بار تمرین شده.
- به کاربران اطلاع داده شده: زمان قطعی؛ یک بار ورود دوباره با OTP، چون توکن‌های قدیمی باطل می‌شوند؛ خالی شدن سبدهای باز، مگر با نگاشت §۷ منتقل شوند.
### گام‌ها
1. سیستم قدیم در حالت نگهداری؛ نوشتن قفل.
2. مهاجرت نهایی و گزارش تطبیق ← **تصمیم go/no-go.**
3. سوئیچ مسیرها روی **همان دامنه‌ها**:
- service worker جدید در همان مسیر `/sw.js`، تا جای قبلی را بگیرد.
- نسخه‌ی اپ کلاسیک بالاتر از `1.0.7` باشد، تا دیالوگ مرجع «نسخه جدید هنزا منتشر شد!» یک بار برای همه بیاید و اپ تازه شود (`henza_pwa/src/App.vue:56-74`).
4. اولین باز شدن بعد از سوئیچ ممکن است پوسته‌ی کش‌شده‌ی قدیمی را نشان دهد که به API قدیم (`henzagold.com/oapi/api/`) درخواست می‌فرستد. آن مسیر تا مدتی پاسخ تمیز (مثلاً 503) بدهد، نه timeout.
5. آزمون دود: ورود با OTP؛ موجودی میز کار برابر گزارش تطبیق؛ ثبت سفارش روی یک مشتری تست؛ ورود ادمین و دیدن همان سفارش؛ رسیدن پیامک.
6. باز کردن برای همه.
### برگشت
- سیستم قدیم چند روز (مثلاً ۱۴ روز) فقط‌خواندنی بماند؛ پشتیبان کامل از هر دو دیتابیس.
- **مهلت تصمیم برگشت از قبل تعیین شود** (مثلاً ۲ ساعت بعد از باز کردن). بعد از آن، سفارش‌ها و اسناد تازه فقط در سیستم جدیدند و برگشت یعنی انتقال دستی آن‌ها به دیتابیس قدیم.
- **برگشت سمت گوشی‌ها فوری نیست:** service worker جدید روی گوشی‌ها کش شده. برای برگرداندن، نسخه‌ی قدیم باید با شماره‌ی نسخه‌ی بالاتر دوباره منتشر شود.
---
## §۱۱ — بعد از جابه‌جایی
- مقایسه با مبنای §۳ و ثبت نتیجه در جدول همان بخش.
- پایش خطا و زمان پاسخ در هفته‌ی اول.
- بعد: ارتقاهای §۵.۸؛ وصل کردن `user-app` و `user-app-v2` به قابلیت‌های تازه‌ی بک‌اند (تنش ۱ در `docs/PWA-PORT-PLAN.md`).
---
# وضعیت اجرا
| # | قدم | وضعیت |
|---|---|---|
| 1 | راه‌اندازی روی هر سیستم + commit و push اسناد | ⬜ |
| 2 | اندازه‌گیری مبنا | ⬜ |
| 3 | ارتقای پشته (۵.۱ تا ۵.۷) | ⬜ |
| 4 | جداسازی انتشار | ⬜ |
| 5 | پورت اپ کلاسیک | ⬜ منتظر تصمیم‌های مالک در `docs/PWA-PORT-PLAN.md` |
| 6 | ابزار مهاجرت داده و تطبیق | ⬜ |
| 7 | اجرای موازی روی staging | ⬜ |
| 8 | بستن مسدودکننده‌ها | ⬜ |
| 9 | روز جابه‌جایی | ⬜ |