معماری و یکپارچهسازی جامع موتور MedusaJS 2.0
موتور تجارت الکترونیک MedusaJS 2.0 (v2.15.5) تمامی عملیات مربوط به کاتالوگ محصولات، قیمتگذاری بر اساس منطقه (Regions)، سبد خرید، فرایند تسویهحساب (Checkout)، ماژولهای لینک بینماژولی (Module Links) و حساب کاربری مشتریان را مدیریت میکند.
نکته بسیار مهم در تنظیمات Publishable API Key
پس از ساخت کلید در پنل Medusa Admin، حتماً باید کلید ساخته شده را به **Sales Channel اصلی** متصل کنید؛ در غیر این صورت محصولات روی فرانتاند نمایش داده نخواهند شد.
معماری لینکهای بینماژولی Medusa v2 (Module Links)
در نسخه Medusa v2، ماژولها کاملاً ایزوله هستند و ارتباط دیتابیسی بین مدلهای ماژولهای مستقل (مانند نظرات، سوالات، علاقهمندیها و Strapi) از طریق defineLink در پوشه store-admin/src/links/ برقرار میشود:
product-strapi.tsنگاشت بین ID محصول مدوسا و شناسههای محتوایی Strapi CMS.
review-product.tsارتباط بین نظرات خریداران و محصول متناظر مدوسا.
wishlist-customer.tsارتباط بین لیست علاقهمندیها و پروفایل کاربری مشتری.
question-product.tsاتصال پرسش و پاسخهای ثبتشده به محصولات کاتالوگ.
store-admin/src/links/review-product.ts
typescriptimport { defineLink } from "@medusajs/framework/modules-sdk"import ProductModule from "@medusajs/medusa/product"import ProductReviewModule from "../modules/product-review" export default defineLink( ProductModule.linkable.product, ProductReviewModule.linkable.productReview)اسکریپتهای کاربردی مدیریت دیتابیس و سرور (Server Scripts)
در پوشه store-admin/src/scripts/ مجموعه اسکریپتهای آمادهای جهت Seed و مدیریت محصولات تعبیه شده است:
اسکریپت Seed اولیه دیتابیس (منطقهها، ارسال، محصولات)
bashnpm run seedتزریق ۳۰ محصول نمونه کامل با متغیرها و قیمتها
bashnpx medusa exec ./src/scripts/seed-30-products.tsهمگامسازی دستی و اجباری شاخص محصولات در Meilisearch
bashnpx medusa exec ./src/scripts/sync-meilisearch.tsپاکسازی کامل محصولات برای شروع از صفر
bashnpx medusa exec ./src/scripts/delete-all-products.tsلایه فراخوانی محصولات در فرانتاند (products.ts)
src/lib/data/products.ts
typescriptimport { sdk } from "@lib/config"import { HttpTypes } from "@medusajs/types"import { getAuthHeaders, getCacheOptions } from "./cookies"import { getLocaleHeader } from "@lib/util/get-locale-header"import { getRegion, retrieveRegion } from "./regions"import { isNetworkFetchError, warnMedusaUnreachable } from "@lib/util/medusa-fetch" export const listProducts = async ({ pageParam = 1, queryParams, countryCode, regionId, disableAuth = false,}: { pageParam?: number queryParams?: HttpTypes.FindParams & HttpTypes.StoreProductListParams countryCode?: string regionId?: string disableAuth?: boolean}): Promise<{ response: { products: HttpTypes.StoreProduct[]; count: number } nextPage: number | null queryParams?: HttpTypes.FindParams & HttpTypes.StoreProductListParams}> => { if (!countryCode && !regionId) { throw new Error("Country code or region ID is required") } const limit = queryParams?.limit || 12 const _pageParam = Math.max(pageParam, 1) const offset = _pageParam === 1 ? 0 : (_pageParam - 1) * limit let region: HttpTypes.StoreRegion | undefined | null if (countryCode) { region = await getRegion(countryCode, disableAuth) } else { region = await retrieveRegion(regionId!, disableAuth) } if (!region) { return { response: { products: [], count: 0 }, nextPage: null } } const headers = { ...(disableAuth ? {} : await getAuthHeaders()), ...(await getLocaleHeader(disableAuth)), } as Record<string, string> const next = { ...(disableAuth ? { tags: ["store-products"] } : await getCacheOptions("store-products")), revalidate: 3600, } try { return await sdk.client .fetch<{ products: HttpTypes.StoreProduct[]; count: number }>( `/store/products`, { method: "GET", query: { limit, offset, region_id: region?.id, fields: "*variants.calculated_price,+variants.inventory_quantity,*variants.images,*options,*options.values,*variants.options,+metadata,+tags", ...queryParams, }, headers, next, cache: "force-cache", } ) .then(({ products, count }) => { const nextPage = count > offset + limit ? pageParam + 1 : null return { response: { products, count }, nextPage, queryParams, } }) } catch (error) { if (isNetworkFetchError(error)) { warnMedusaUnreachable("listProducts") return { response: { products: [], count: 0 }, nextPage: null, queryParams, } } throw error }}سرویس ارسال ایمیل و اعلانها (Nodemailer SMTP & SendPulse)
سیستم ارسال ایمیلهای اطلاعرسانی، فرم تماس و ثبت سفارش با استفاده از Nodemailer (SMTP) و SendPulse و قالبهای واکنشگرا پیادهسازی شده است:
تنظیمات سرویس ایمیل در .env
envSMTP_HOST=smtp.gmail.comSMTP_PORT=587SMTP_USER=your-email@gmail.comSMTP_PASS=your-app-passwordSMTP_FROM_ADDRESS=noreply@yourdomain.comCONTACT_FORM_RECIPIENT=info@yourdomain.comSENDPULSE_API_ID=your_sendpulse_idSENDPULSE_API_SECRET=your_sendpulse_secret