156 lines
12 KiB
Markdown
156 lines
12 KiB
Markdown
# ماژول محصول (Product) — ادمین پنل
|
|||
|
|
|
||
|
|
مستند تعریف محصول، تنوع (Variant) و ثبت اطلاعات در ادمین پنل، منطبق بر پیادهسازی فعلی.
|
||
|
|
|
||
|
|
مسیرهای مرتبط:
|
||
|
|
- فرم ثبت/ویرایش: [AddProduct.vue](../apps/admin-panel/src/views/apps/product/AddProduct.vue) (هم برای ایجاد هم ویرایش استفاده میشود؛ [EditProduct.vue](../apps/admin-panel/src/views/apps/product/EditProduct.vue) صرفاً یک wrapper است)
|
||
|
|
- لیست محصولات: [ProductList.vue](../apps/admin-panel/src/views/apps/product/ProductList.vue) و مدل جدول [productListModel.ts](../apps/admin-panel/src/views/apps/product/productListModel.ts)
|
||
|
|
- بکاند: [admin-products.controller.ts](../apps/api/src/modules/admin-products/admin-products.controller.ts) و [admin-products.service.ts](../apps/api/src/modules/admin-products/admin-products.service.ts)
|
||
|
|
- اسکیمای دیتابیس: [schema.prisma](../apps/api/prisma/schema.prisma) (مدلهای `Product`, `ProductVariant`, `ProductImage`)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## ۱. تعریف محصول (Product)
|
||
|
|
|
||
|
|
هر محصول یک رکورد پایه دارد که در کارت «اطلاعات پایه» و «تصاویر محصول» فرم پر میشود.
|
||
|
|
|
||
|
|
### ۱.۱ اطلاعات پایه
|
||
|
|
|
||
|
|
| فیلد فرم (فارسی) | نام در فرم/پیلود | ستون در دیتابیس | نوع | الزامی | توضیح |
|
||
|
|
|---|---|---|---|---|---|
|
||
|
|
| کد محصول | `product_code` | `Product.productCode` | string، یکتا (`@unique`) | خیر | SKU نمایشی؛ در فرم اعتبارسنجی نمیشود ولی در دیتابیس یکتا است |
|
||
|
|
| نام محصول | `title` | `Product.title` | string | بله | تنها فیلد با اعتبارسنجی سمت فرم (`err.title`) |
|
||
|
|
| نوع نمایش | `show_type` | `Product.showType` | int، پیشفرض `3` | خیر | مقادیر: `1` سایت، `2` گوشی، `3` سایت و گوشی |
|
||
|
|
| دستهبندی | `category_id` | `Product.categoryId` (FK به `Category`) | string، اختیاری | خیر | از `GET category` پر میشود؛ عنوانها با نوع فروشگاه (`type_str`) پسوند میخورند |
|
||
|
|
| برند | `brand_id` | `Product.brandId` (FK به `Brand`) | string، اختیاری | خیر | از `GET brand` |
|
||
|
|
| سینیها (گروهبندی) | `ciny_ids` | جدول واسط `CollectionProduct` → مدل `Collection` | آرایه از id | خیر | چندانتخابی؛ معادل مدرن فیچر قدیمی «Ciny»، صرفاً یک تگگذاری/گروهبندی محصول است، نه چیزی مرتبط با قیمت لحظهای طلا |
|
||
|
|
| شرح محصول (سایت) | `body_html` | `Product.description` | text | خیر | |
|
||
|
|
| نمایش محصول | `is_active` | `Product.isActive` | boolean، پیشفرض `true` | خیر | فعال/غیرفعال بودن محصول در فروشگاه |
|
||
|
|
|
||
|
|
فیلدهای سیستمی که در فرم دیده نمیشوند: `id` (cuid)، `createdAt`.
|
||
|
|
|
||
|
|
> فیلدهای نسخه قدیمی که عمداً حذف شدهاند (چون ستون معادل در اسکیمای جدید ندارند): `delivery_type`, `message_long_delivery`, `offer_day`, `has_warranty`, `is_exist`, `tags`، و فیلدهای رنگ/اجرت ریالی در تنوع. طبق کامنت بالای فایل فرم، این تصمیم آگاهانه بوده — داده جمعآوری و بیصدا دور ریخته نمیشود.
|
||
|
|
|
||
|
|
### ۱.۲ تصاویر محصول
|
||
|
|
|
||
|
|
محصول به ازای هر تصویر یک رکورد `ProductImage` دارد که با فیلد `role` مشخص میشود کدام یک از ۴ اسلات قدیمی است:
|
||
|
|
|
||
|
|
| بخش فرم (فارسی) | `ProductImageRole` | تعداد | فیلد پیلود |
|
||
|
|
|---|---|---|---|
|
||
|
|
| تصویر شاخص | `FEATURED` | تک | `indicator_id` |
|
||
|
|
| تصویر شاخص موبایل | `FEATURED_MOBILE` | تک | `mobile_pic` |
|
||
|
|
| گالری تصاویر سایت | `GALLERY` (پیشفرض) | چندتایی | `images` |
|
||
|
|
| گالری تصاویر موبایل | `GALLERY_MOBILE` | چندتایی | `images_mobile` |
|
||
|
|
|
||
|
|
آپلود از طریق کامپوننت `ImageUploadInput` و اندپوینت عمومی `file/create` انجام میشود (همان اندپوینتی که برای آپلود دستهبندی/اسلایدر/آواتار هم استفاده میشود). فیلد `imageBody` (کپشن هر تصویر گالری در نسخه قدیمی) معادلی در دیتابیس ندارد و حذف شده.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## ۲. کارکرد واریانت (تنوع محصول)
|
||
|
|
|
||
|
|
هر محصول میتواند چند «تنوع» (`ProductVariant`) داشته باشد — مثلاً وزنهای مختلف یک مدل طلا. مدیریت واریانتها **درون همان فرم محصول** و بهصورت یک جدول اینلاین با مودال افزودن/ویرایش انجام میشود؛ واریانتها تا زمان «ثبت اطلاعات» فقط در حافظه (state) نگه داشته میشوند و همراه با کل محصول به سرور ارسال میگردند.
|
||
|
|
|
||
|
|
### ۲.۱ فیلدهای واریانت
|
||
|
|
|
||
|
|
| عنوان در مودال | نام فیلد | ستون دیتابیس | نوع | توضیح |
|
||
|
|
|---|---|---|---|---|
|
||
|
|
| عنوان | `title` | `ProductVariant.title` | string، الزامی | تنها فیلد با اعتبارسنجی (`عنوان تنوع الزامی است`) |
|
||
|
|
| وزن | `grams` | `ProductVariant.rashWeightGrams` | Decimal(14,4) | وزن پایه/خام پیش از احتساب اجرت («rash weight») |
|
||
|
|
| اجرت درصدی | `percentage_pay` | `ProductVariant.defaultWagePercent` | Decimal(6,3) | درصد اجرت پیشفرض روی وزن پایه |
|
||
|
|
| تعداد | `qty` | `ProductVariant.qty` | int، پیشفرض `0` | موجودی انبار |
|
||
|
|
| فعال | `is_active` | `ProductVariant.isActive` | boolean، پیشفرض `true` | |
|
||
|
|
| پیش سفارش | `is_pre_order` | `ProductVariant.isPreOrder` | boolean، پیشفرض `false` | اگر `true` باشد، حتی با موجودی صفر قابل سفارش است |
|
||
|
|
|
||
|
|
فیلدهای دیگر مدل `ProductVariant` که در فرم نیستند: `sku` (یکتا)، `id`. فیلد رنگ و اجرت ریالی نسخه قدیمی حذف شدهاند (بدون ستون معادل).
|
||
|
|
|
||
|
|
> قاعدهی سبد خرید: اگر `qty = 0` و `isPreOrder = false` باشد، واریانت قابل افزودن به سبد نیست (منطق در `apps/admin-panel/src/stores/cart.ts`).
|
||
|
|
|
||
|
|
### ۲.۲ رفتار UI جدول تنوعها
|
||
|
|
|
||
|
|
- ستونها: عنوان، وزن، اجرت درصدی، تعداد، فعال (تگ سبز/قرمز)، پیش سفارش (تگ آبی/خالی)، عملیات.
|
||
|
|
- **افزودن تنوع**: دکمه بالای جدول → باز کردن مودال خالی با کلید موقت `__key` (برای ردیابی ردیف پیش از ذخیره در سرور).
|
||
|
|
- **ویرایش تنوع**: آیکون ویرایش هر ردیف → پر کردن مودال با مقادیر همان ردیف.
|
||
|
|
- **حذف تنوع**: آیکون حذف → صرفاً از آرایهی حافظه حذف میشود؛ حذف واقعی از دیتابیس زمانی رخ میدهد که فرم اصلی «ثبت اطلاعات» شود.
|
||
|
|
- کل بخش تنوعها پشت پرمیشن است (زیر بخش «ثبت اطلاعات» دقیقتر شرح داده شده).
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## ۳. ثبت اطلاعات
|
||
|
|
|
||
|
|
### ۳.۱ فرم ایجاد/ویرایش
|
||
|
|
|
||
|
|
- یک فرم واحد (`AddProduct.vue`) هم برای ایجاد و هم ویرایش به کار میرود؛ تشخیص حالت از روی وجود `route.params.id`.
|
||
|
|
- **اعتبارسنجی سمت کلاینت**: فقط «نام محصول» (`title`) الزامی است؛ در سطح واریانت فقط «عنوان» الزامی است. سایر فیلدها اختیاریاند.
|
||
|
|
- **دکمه ارسال**: «ثبت اطلاعات» → `handleSubmit`.
|
||
|
|
|
||
|
|
### ۳.۲ اندپوینتها
|
||
|
|
|
||
|
|
| حالت | متد | مسیر |
|
||
|
|
|---|---|---|
|
||
|
|
| ایجاد محصول جدید | `POST` | `product/create` |
|
||
|
|
| ویرایش محصول موجود | `PUT` | `product/update/:id` |
|
||
|
|
| نمایش یک محصول (پر کردن فرم ویرایش) | `GET` | `product/show/:id` |
|
||
|
|
| لیست برندها | `GET` | `brand` |
|
||
|
|
| لیست دستهبندیها | `POST` | `category` |
|
||
|
|
| لیست سینیها/کالکشنها | `GET` | `admin/products/collections` |
|
||
|
|
|
||
|
|
### ۳.۳ ساختار پیلود ارسالی
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"title": "...",
|
||
|
|
"product_code": "...",
|
||
|
|
"show_type": 3,
|
||
|
|
"category_id": "...",
|
||
|
|
"brand_id": "...",
|
||
|
|
"ciny_ids": ["..."],
|
||
|
|
"body_html": "...",
|
||
|
|
"is_active": true,
|
||
|
|
"indicator_id": "url|null",
|
||
|
|
"mobile_pic": "url|null",
|
||
|
|
"cats": ["category_id"],
|
||
|
|
"images": ["url", "..."],
|
||
|
|
"images_mobile": ["url", "..."],
|
||
|
|
"variants": [
|
||
|
|
{
|
||
|
|
"id": "فقط در ویرایش واریانت موجود",
|
||
|
|
"title": "...",
|
||
|
|
"grams": 0,
|
||
|
|
"percentage_pay": 0,
|
||
|
|
"qty": 0,
|
||
|
|
"is_active": true,
|
||
|
|
"is_pre_order": false
|
||
|
|
}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
نکات مهم:
|
||
|
|
- `cats` مشتق از `category_id` است (آرایهای تکعضوی یا خالی) — یک artifact از انتقال ساختار قدیمی چند-دستهای.
|
||
|
|
- `images` معادل گالری سایت و `images_mobile` معادل گالری موبایل است؛ تصاویر شاخص جدا (`indicator_id`, `mobile_pic`) ارسال میشوند.
|
||
|
|
- کل آرایه `variants` هر بار با کل درخواست ارسال میشود (نه اندپوینت جداگانه)؛ بکاند مسئول تطبیق/ایجاد/بهروزرسانی ردیفها بر اساس وجود یا نبود `id` است.
|
||
|
|
|
||
|
|
### ۳.۴ بازخورد به کاربر
|
||
|
|
|
||
|
|
- موفق: پیام `اطلاعات با موفقیت به ثبت رسید.` و ریدایرکت به `/apps/product/list`.
|
||
|
|
- خطا: پیام خطای برگشتی از سرور (`e.response.data.message`) یا پیام پیشفرض `خطا در ثبت اطلاعات.`
|
||
|
|
- در حین ارسال، کل فرم پشت `a-spin` (لودینگ) قفل میشود.
|
||
|
|
|
||
|
|
### ۳.۵ پرمیشنهای مرتبط با ثبت/مدیریت واریانت
|
||
|
|
|
||
|
|
| کلید پرمیشن | کنترل میکند |
|
||
|
|
|---|---|
|
||
|
|
| `product.list.variants` | نمایش کل بخش «تنوعها» در فرم محصول |
|
||
|
|
| `product.list.variants.view` | مشاهده (تعریفشده در `permissionConst.ts`؛ در UI فعلی فرم جداگانه چک نشده) |
|
||
|
|
| `product.list.variants.add` | نمایش دکمه «افزودن تنوع» |
|
||
|
|
| `product.list.variants.edit` | نمایش آیکون ویرایش هر ردیف تنوع |
|
||
|
|
| `product.list.variants.remove` | نمایش آیکون حذف هر ردیف تنوع |
|
||
|
|
|
||
|
|
ثبت خودِ محصول (فرم اصلی) پرمیشن جداگانهای در این فایل چک نمیکند — دسترسی به صفحه از طریق روتینگ/منو کنترل میشود.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## ۴. لیست محصولات (خلاصه، برای زمینه)
|
||
|
|
|
||
|
|
جدول لیست (`admin/products/list`) این ستونها را نشان میدهد: کد، تصویر محصول، نام محصول، برند، دستهبندی، کد محصول، نوع نمایش (با فیلتر انتخابی روی مقادیر ۱/۲/۳)، عملیات. تعریف کامل در [productListModel.ts](../apps/admin-panel/src/views/apps/product/productListModel.ts).
|