Quickstarters
Как создать бэкэнд для электронной коммерции?
88 мин
в этом практическом руководстве вы создадите надежное полнофункциональное приложение электронной коммерции с нуля, используя бэкенд как услугу по пути вы освоите безопасная аутентификация пользователей, обеспечивающая доступ только авторизованным покупателям и администраторам интуитивно понятные модели данных, разработанные для поддержки каталогов продуктов, профилей клиентов и истории заказов restful и реалтайм api, чтобы ваш фронтенд всегда оставался синхронизированным с инвентарем, корзинами и статусом оформления заказа автоматизированные конвейеры развертывания, позволяющие вам уверенно вносить обновления и откатываться, если что то пойдет не так к концу вы получите не только готовый к производству интернет магазин, но и архитектурные знания, чтобы расширять, масштабировать и защищать любое веб приложение изучите готовое приложение электронной коммерции в действии по адресу веб приложение электронной коммерции давайте начнем! основные выводы быстрая настройка бэкенда запустите и настройте полную модель данных электронной коммерции за считанные минуты с помощью платформы back4app с низким кодом безопасные пользовательские потоки реализуйте надежную регистрацию, вход и выход с использованием cloud code и токенов сеанса разработка с приоритетом на api получите доступ к вашим данным через автоматически сгенерированные rest/graphql конечные точки и sdk parse для любого клиента бизнес логика в cloud code централизуйте основные операции — crud продуктов, управление корзиной и оформление заказа — при этом соблюдая разрешения контейнеризированное развертывание фронтенда упакуйте и разверните ваш магазин next js в back4app web deployment для последовательного и масштабируемого хостинга предварительные требования и настройка окружения чтобы следовать этому руководству, убедитесь, что у вас есть следующее аккаунт back4app вы можете зарегистрироваться бесплатно на сайте back4app https //www back4app com/signup установленный node js, позволяющий управлять зависимостями с помощью npm или yarn знание javascript для написания пользовательского облачного кода и использования sdk back4app базовое понимание концепций электронной коммерции, таких как аутентификация пользователей, управление каталогом продуктов и обработка заказов git для контроля версий, чтобы вы могли отслеживать изменения, откатываться при необходимости и бесшовно сотрудничать обеспечение наличия вышеуказанных элементов поможет вам максимально использовать возможности back4app и реализовать ключевые функции электронной коммерции, не застревая в процессе создание и настройка вашей учетной записи back4app чтобы начать настройку вашего бэкенда электронной коммерции на back4app, посетите back4app https //www back4app com и нажмите зарегистрироваться введите свой адрес электронной почты, выберите надежный пароль и укажите все необходимые данные после подтверждения вашей электронной почты вы получите доступ к панели управления back4app страница регистрации back4app внутри панели управления выберите создать новое приложение или создать новое приложение и дайте вашему проекту имя — что то описательное, чтобы вы могли легко его найти позже подтвердите свои выборы, затем дождитесь завершения процесса создания приложения панель управления проектом отобразит классы, аналитику и настройки создание приложения back4app с этими шагами вы создали платформу электронной коммерции в следующем разделе вы разработаете свою схему данных и подготовитесь к управлению продуктами, обработке заказов и аутентификации пользователей проектирование вашей схемы данных электронной коммерции чтобы реализовать масштабируемую и поддерживаемую серверную часть для вашей платформы электронной коммерции, вы организуете свои данные вокруг семи основных таблиц пользователь , покупатель , продавец , продукт , корзина , заказ , и детализаказа каждая таблица выполняет определенную роль в поддержке общей функциональности системы, и вместе они формируют нормализованную и эффективную схему начните с таблицы user , которая служит основой для всех пользовательских идентичностей каждый человек на вашей платформе — независимо от его роли — будет аутентифицироваться с использованием этой таблицы включите такие поля, как username , password , и email , чтобы обрабатывать базовый вход добавьте emailverified , чтобы отслеживать статус проверки, и authdata , если вы планируете поддерживать сторонние входы или потоки oauth чтобы различать покупателей и продавцов, определите поле usertype это поле позволяет вам контролировать доступ, управлять представлениями и разделять функциональность в зависимости от ролей пользователей, при этом сохраняя единую, согласованную систему входа вместо того чтобы смешивать все типы пользователей в одной таблице, создайте отдельные таблицы buyer и seller , которые ссылаются на user через поле userid это позволяет вам хранить данные, специфичные для роли, не перегружая основную таблицу пользователей таблица buyer может содержать поля, связанные с покупками, такие как paymentinfo и shippingaddress , которые необходимы для процессов оформления заказа и доставки в отличие от этого, таблица seller отслеживает атрибуты, относящиеся к продавцам, такие как storename , bio , и phonenumber с этой разделением вы можете легко управлять и запрашивать покупателей и продавцов независимо, при этом все еще связывая их с их общей идентичностью в user ваш продукт таблица — это место, где хранятся товары на продажу каждый продукт должен включать основную описательную информацию, такую как название, подробное описание и одно или несколько изображений для управления ценами и запасами вам также нужно будет включить поля для цены, статуса доступности через булев isactive , и количества запасов под quantityavailable крайне важно, чтобы каждый продукт был связан с продавцом, который его разместил чтобы установить эту связь, добавьте sellerid внешний ключ, который ссылается на seller таблицу эта структура гарантирует, что каждый товар в вашем каталоге связан с проверенным продавцом и упрощает фильтрацию по конкретному продавцу чтобы обрабатывать поведение до покупки, введите cart таблицу каждая корзина принадлежит пользователю, поэтому включите поле userid которое связывает с user храните массив items корзины, каждый из которых представляет продукт, который пользователь намеревается купить, вместе с любыми метаданными, которые вам нужны, такими как количество или выбранные опции отслеживание totalprice корзины позволяет вам быстро отображать сводки оформления заказа без перерасчета значений при каждом запросе эта настройка поддерживает базовую функциональность корзины и упрощает сохранение сессий если вы считаете, что структура массива ограничивает вас — особенно если вам нужно записывать более сложные данные на уровне товара — вы всегда можете позже переработать товары в отдельную таблицу когда покупатель размещает заказ, вы будете записывать транзакцию в order таблице каждый заказ связан с buyerid , ссылаясь на buyer таблицу, чтобы убедиться, что покупатель действителен и авторизован в дополнение к этой связи, храните orderdate , текущий orderstatus , и общую денежную сумму под total полем это дает вам четкое резюме покупки и поддерживает рабочие процессы, такие как отслеживание и выполнение заказов поскольку каждый заказ может содержать несколько продуктов, вам понадобится orderdetails таблица для управления отдельными строками товаров эта таблица связывает каждый товар с его родительским заказом через orderid , и с купленным продуктом, используя productid чтобы точно зафиксировать состояние покупки, укажите количество quantity купленных товаров и unitprice на момент продажи разделение деталей заказа таким образом позволяет вам создавать отчеты, генерировать счета и обрабатывать возвраты или корректировки с точностью эта схема, с ее четким разделением обязанностей и реляционной целостностью, формирует прочную основу для вашей электронной коммерции теперь, когда модель данных согласована с реальными рабочими процессами вашей платформы, вы готовы начать ее внедрение в своем бэкенде начните с настройки таблиц user , buyer , и seller и стройте оттуда настройка классов в панели управления следуйте приведенным ниже шагам, чтобы создать и связать ваши сущности в back4app вы определите классы для user , buyer , seller , cart , product , order , и orderdetails , а затем установите указатели для представления отношений, таких как наследование, один ко многим и многие ко многим доступ к браузеру базы данных откройте панель управления вашего приложения и создайте новый класс с именем user нажмите на database в меню слева back4app dashboard создание класса “user” back4app автоматически создает класс user класс user включает в себя следующие столбцы имя пользователя пароль электронная почта и т д чтобы добавить столбец usertype выберите добавить новый столбец добавить новый столбец укажите имя столбца, тип данных, значение по умолчанию и т д добавить тип пользователя столбец примечание в parse каждый класс включает поле по умолчанию objectid , которое функционирует как первичный ключ это означает, что objectid будет фактически служить как userid в вашей реляционной модели создание класса “покупатель” чтобы создать класс покупателя, вернитесь в браузер базы данных и выберите добавить класс создать новый класс выберите пользовательский и назовите его покупатель добавить класс покупателя добавьте указатель столбец с именем user , указывающий на класс user создать класс userid это имитирует “наследование”, ссылаясь на objectid , базового пользователя 3\ добавьте следующие столбцы адресдоставки (строка) информацияоплате (строка) когда вы создаете новый объект покупателя, вы свяжете его с существующей записью пользователя, установив buyer user на objectid этого пользователя создание класса “продавец” повторите тот же процесс, чтобы создать класс продавец добавьте колонку указатель с именем user ссылающуюся на класс user добавьте эти дополнительные колонки имямагазина (строка) биография (строка) номертелефона (число) создание класса “продукт” выберите создать класс снова, назвав его продукт добавьте столбец указатель с именем продавец указывающий на класс продавец (это устанавливает отношение один ко многим, где один продавец может иметь много продуктов) добавьте следующие столбцы имя (строка) описание (строка) цена (число) количестводоступно (число) активен (булевый) изображения (массив) хранит объекты, представляющие изображения продукта создание класса “корзина” нажмите создать класс , назовите класс корзина добавьте столбец указатель с именем пользователь указывающий на класс пользователь (это связывает каждую корзину с конкретным пользователем) затем добавьте следующие столбцы для представления товаров в корзине товары (массив) хранит объекты, представляющие каждый товар в корзине каждый товар может быть объектом, таким как { продукт указатель на продукт, количество число } общаяцена (число) создание класса “заказ” создайте новый класс с именем order добавьте столбец pointer с именем buyer который указывает на класс buyer (указывает на то, что один покупатель может иметь много заказов) включите эти столбцы orderdate (дата или строка, в зависимости от вашего предпочтения) orderstatus (строка) создание класса “orderdetails” наконец, создайте класс с именем orderdetails добавьте два столбца pointer order → ссылается на order product → ссылается на product затем добавьте оставшиеся столбцы quantity (число) unitprice (число) эта настройка позволяет установить отношение многие ко многим между order и product через orderdetails , поскольку каждый заказ может включать несколько продуктов, и каждый продукт может появляться в нескольких заказах визуализация отношений вот упрощенная диаграмма сущностей и отношений (erd), отражающая классы и их указатели dbschema2 после того как эти классы будут созданы, вы сможете обрабатывать регистрации и входы через класс user , создавать специализированные записи покупателя или продавца для различных типов пользователей и хранить информацию о продуктах или заказах с четкими ссылками на внешние ключи структурируя вашу базу данных таким образом, вы получаете чистый, поддерживаемый дизайн для любого приложения электронной коммерции с вашей схемой на месте вы готовы настроить аутентификацию и авторизацию, чтобы ограничить, кто может размещать товары, делать заказы или выполнять административные задачи следующий шаг покажет вам, как настроить безопасные процессы регистрации и входа, которые основываются на ролях и классах, которые вы только что определили реализация аутентификации с back4app безопасная аутентификация является центральной для любой платформы электронной коммерции, помогая вам защищать учетные записи пользователей, платежные данные и личную информацию back4app упрощает это с помощью нескольких методов — от классических комбинаций логина/пароля до социальных логинов или потоков на основе токенов — обеспечивая вам возможность предложить вашим покупателям простой и безопасный опыт чтобы настроить аутентификацию в вашем приложении электронной коммерции, вы будете использовать облачный код облачный код расширяет возможности вашего бэкенда, позволяя вам писать пользовательскую бизнес логику без необходимости размещать собственные серверы это означает, что вы можете проверять заказы, рассчитывать сборы или инициировать уведомления по конкретным событиям — полностью в инфраструктуре back4app это идеальный способ обработки чувствительных транзакций или процессов данных, которые требуют строгого контроля и быстрой реализации чтобы включить облачный код на back4app, откройте панель управления вашего приложения и найдите раздел облачный код cloud code чтобы реализовать процесс аутентификации в back4app, вам нужно будет развернуть несколько функций в вашем облачном коде для регистрации пользователей, входа пользователей и выхода пользователей зарегистрировать нового пользователя (по умолчанию покупатель) чтобы зарегистрировать нового пользователя, используйте метод регистрации parse установите стандартные поля, такие как username , password , и email , и добавьте пользовательское поле, чтобы указать роль либо buyer или seller по умолчанию новым пользователям назначается роль buyer до тех пор, пока они не решат изменить её для начала создайте файл auth js в каталоге облачного кода и добавьте следующий блок кода parse cloud define('signupuser', async (request) => { const { name, password, confirmpassword, email, shippingaddress, paymentinfo } = request params; // validate password match if (password !== confirmpassword) { throw new error('passwords do not match'); } const user = new parse user(); user set('username', name); user set('password', password); user set('email', email); user set('usertype', "buyer"); try { // sign up the user const signedupuser = await user signup(); console log('user registered ', signedupuser id); // create a buyer record linked to the new user const buyer = parse object extend('buyer'); const buyer = new buyer(); buyer set('user', signedupuser); buyer set('shippingaddress', shippingaddress); buyer set('paymentinfo', paymentinfo); const savedbuyer = await buyer save(); console log('buyer created ', savedbuyer id); return { success true, message 'user registered and buyer created', userid signedupuser id, buyerid savedbuyer id }; } catch (error) { console error('sign up failed ', error); throw new error('sign up failed ' + error message); } }); эта облачная функция обрабатывает регистрацию пользователей для покупателей она проверяет ввод, создает нового пользователя parse, назначает роль ( buyer ) и сохраняет данные, специфичные для покупателей, в отдельном классе buyer вход пользователей чтобы аутентифицировать пользователей, вызовите встроенный метод parse parse user login и верните токен сессии клиенту добавьте следующий код в ваш auth js файл, чтобы реализовать эту логику в вашем облачном коде parse cloud define("loginuser", async (request) => { const { email, password } = request params; try { const user = await parse user login(email, password); // example block users with disabled flag if (user get("isdisabled")) { throw new parse error(403, "account is disabled"); } return { sessiontoken user getsessiontoken(), userid user id, }; } catch (error) { throw new parse error(101, "invalid credentials"); } }); эта функция принимает электронную почту и пароль от клиента, пытается аутентифицировать пользователя и возвращает токен сессии в случае успеха если учетная запись помечена как отключенная, она блокирует доступ и выбрасывает четкую ошибку выйти чтобы реализовать функцию выхода для вашего бэкенда, добавьте следующий блок кода в ваш auth js файл parse cloud define('logoutuser', async (request) => { const sessiontoken = request headers\['x parse session token']; if (!sessiontoken) { throw new error('session token required'); } const sessionquery = new parse query(' session'); sessionquery equalto('sessiontoken', sessiontoken); const session = await sessionquery first({ usemasterkey true }); if (session) { await session destroy({ usemasterkey true }); } return { success true, message 'session invalidated' }; }); это надежно завершает сессию на бэкенде, предотвращая дальнейшее использование токена с этими функциями cloud code у вас теперь есть безопасная система аутентификации, которая регистрирует пользователей с логикой на основе ролей аутентифицирует и авторизует доступ с использованием токенов сессии связывает учетные записи пользователей с записями, специфичными для покупателей поддерживает чистые потоки входа и выхода использует встроенные инструменты проверки сессий и электронной почты back4app создание бизнес логики для вашего приложения электронной коммерции чтобы управлять пользователями, продуктами, корзинами и транзакциями на вашей платформе электронной коммерции, вы определите ряд функций cloud code в back4app эти функции позволяют вам создавать, читать, обновлять и удалять (crud) ресурсы, такие как продавцы, продукты и заказы, при этом обеспечивая безопасный контроль доступа, связанный с ролями пользователей ниже приведен набор облачных функций, который демонстрирует, как работать с ключевыми сущностями продавец , продукт , корзина , заказ , и детализаказа каждая функция обрабатывает конкретную операцию на бэкенде и обеспечивает соблюдение разрешений через проверку пользователей и проверку ролей продавец создайте профиль продавца для пользователя, который хочет предлагать товары на платформе создание продавца эта функция обновляет роль текущего пользователя на продавца и создает запись продавец с информацией о магазине чтобы реализовать это в вашем облачном коде, сначала создайте файл seller js и добавьте в него следующий блок кода parse cloud define('createseller', async (request) => { const currentuser = request user; // get the current logged in user if (!currentuser) { throw new error('user is not logged in'); } try { // check and update the user type const usertype = currentuser get('usertype'); if (usertype === 'buyer') { currentuser set('usertype', 'seller'); await currentuser save(); } // create the seller object const { businessname, bio, phone } = request params; const seller = parse object extend('seller'); const seller = new seller(); seller set('user', currentuser); seller set('storename', businessname); seller set('bio', bio); seller set('phonenumber', number(phone)); const newseller = await seller save(); console log('seller created ', newseller id); return { success true, message 'seller account created', sellerid newseller id }; } catch (error) { console error('error creating seller ', error); throw new error('error creating seller ' + error message); } }); это гарантирует, что только аутентифицированные пользователи могут стать продавцами, и связывает запись продавец с соответствующим пользователем через указатель продукт продукты размещаются продавцами и могут просматриваться, запрашиваться и покупаться покупателями эти функции позволяют продавцам создавать, извлекать, обновлять и удалять свои продукты, а также предоставлять доступ к продуктам покупателям создание продукта эта функция добавляет новый продукт для вошедшего в систему продавца и прикрепляет до трех изображений продукта чтобы реализовать это в вашем облачном коде, создайте файл product js и добавьте в него следующий код parse cloud define("addproduct", async (request) => { // destructure parameters sent from the client const { name, description, price, quantityavailable, isactive, imagefiles } = request params; // optional check if user is logged in (request user is available when an active session exists) const currentuser = request user; if (!currentuser) { throw new error("unauthorized you must be logged in to add a product "); } // ensure the user is indeed a seller by checking their usertype const usertype = currentuser get("usertype"); if (usertype != "seller") { throw new error("this user is not a seller "); } const seller = parse object extend("seller"); const query = new parse query(seller); query equalto("userid", currentuser); // match the pointer field in seller const sellerobj = await query first({ usemasterkey true }); if (!sellerobj) { throw new error("no matching 'seller' object found for this user "); } if(!array isarray(imagefiles) || imagefiles length > 3) { throw new error("a maximum of 3 images are provided"); } // create the new product object const product = parse object extend("product"); const product = new product(); product set("seller", sellerobj); // pointer to the seller product set("name", name); product set("description", description); product set("price", number(price)); product set("quantityavailable", number(quantityavailable)); product set("isactive", isactive); product set("images", imagefiles); // save the product to the database try { const savedproduct = await product save(null, { usemasterkey true }); return savedproduct; } catch (error) { throw new error(`could not create product ${error message}`); } }); эта функция проверяет, что пользователь вошел в систему и имеет роль продавца, что существует соответствующий продавец объект и что не подано более трех изображений после проверки она сохраняет продукт и связывает его с продавцом запрос всех продуктов эта функция извлекает все продукты в системе, которые могут быть использованы для создания публичного магазина чтобы реализовать это в вашем облачном коде, добавьте следующий блок кода в ваш products js файл parse cloud define("fetchallproducts", async (request) => { try { const product = parse object extend("product"); const query = new parse query(product); const products = await query find({ usemasterkey true }); // return products in a json friendly format return products map((product) => ({ id product id, sellerid product get("seller")? id, name product get("name"), description product get("description"), price product get("price"), quantityavailable product get("quantityavailable"), isactive product get("isactive"), createdat product createdat, updatedat product updatedat, })); } catch (error) { throw new error(`error fetching products ${error message}`); } }); эта функция извлекает продукты с флагом usemasterkey и форматирует их для использования на фронтенде запрос одного продукта используйте эту функцию при отображении подробной информации о продукте для покупателей чтобы реализовать это в вашем облачном коде, добавьте следующий блок кода в ваш products js файл parse cloud define("getsingleproduct", async (request) => { const { productid } = request params; const currentuser = request user; // ensure user is logged in if (!currentuser) { throw new error("you must be logged in to retrieve product information "); } if (!productid) { throw new error("missing required parameter productid "); } // query the product class const product = parse object extend("product"); const query = new parse query(product); query equalto("objectid", productid); try { const product = await query first({ usemasterkey true }); if (!product) { throw new error("product not found "); } // return a json friendly object return { objectid product id, name product get("name"), description product get("description"), price product get("price"), quantityavailable product get("quantityavailable"), isactive product get("isactive"), // optionally return more fields like images, category, etc }; } catch (error) { throw new error(`error fetching product ${error message}`); } }); эта функция возвращает полные детали продукта для одного товара она проверяет, что передан действительный productid и гарантирует, что запрашивающий вошел в систему запрос продуктов продавца эта функция позволяет продавцу получить все свои продукты чтобы реализовать это в вашем облачном коде, добавьте следующий блок кода в ваш products js файл parse cloud define("getsellerproducts", async (request) => { const currentuser = request user; // ensure the user is logged in if (!currentuser) { throw new error("you must be logged in to fetch seller products "); } // if your schema depends on verifying the user is truly a seller, you can check usertype const usertype = currentuser get("usertype"); if (usertype !== "seller") { throw new error("only sellers can view their own products "); } // if you want to retrieve products for the currently logged in seller // fetch the seller record that points to currentuser const seller = parse object extend("seller"); const sellerquery = new parse query(seller); sellerquery equalto("user", currentuser); const sellerrecord = await sellerquery first({ usemasterkey true }); if (!sellerrecord) { throw new error("no matching seller record found for the current user "); } // finally, fetch all products pointing to this seller const product = parse object extend("product"); const productquery = new parse query(product); productquery equalto("seller", sellerrecord); productquery limit(1000); // adjust or paginate as needed try { const products = await productquery find({ usemasterkey true }); // return a more friendly json structure return products map((prod) => ({ objectid prod id, name prod get("name"), description prod get("description"), price prod get("price"), quantityavailable prod get("quantityavailable"), isactive prod get("isactive"), createdat prod createdat, updatedat prod updatedat, })); } catch (error) { throw new error(`error fetching seller products ${error message}`); } }); эта функция подтверждает, что пользователь является продавцом, находит связанный продавец запись и запрашивает продукт класс, используя указатель на продавца продавцы используют это, чтобы получить все свои продукты удаление продукта эта функция гарантирует, что только продавец, создавший продукт, может его удалить чтобы реализовать это в вашем облачном коде, добавьте следующий блок кода в ваш products js файл parse cloud define("deleteproduct", async (request) => { const { productid } = request params; const currentuser = request user; if (!currentuser) { throw new error("you must be logged in to delete a product "); } const usertype = currentuser get("usertype"); if (usertype !== "seller") { throw new error("only sellers can delete products "); } if (!productid) { throw new error("missing required parameter productid "); } // 1 find the seller record for the current user const seller = parse object extend("seller"); const sellerquery = new parse query(seller); sellerquery equalto("user", currentuser); const sellerrecord = await sellerquery first({ usemasterkey true }); if (!sellerrecord) { throw new error("no matching seller record found for the current user "); } // 2 fetch the product with a query that ensures the user owns it const product = parse object extend("product"); const productquery = new parse query(product); productquery equalto("objectid", productid); productquery equalto("seller", sellerrecord); // must match the correct seller const product = await productquery first({ usemasterkey true }); if (!product) { throw new error("product not found or you do not have permission to delete it "); } // 3 destroy the product try { await product destroy({ usemasterkey true }); return { message "product deleted successfully ", productid }; } catch (error) { throw new error(`error deleting product ${error message}`); } }); эта функция подтверждает право собственности пользователя и затем удаляет продукт из базы данных обновление продукта позвольте продавцам изменять детали продукта с помощью этой функции чтобы реализовать это в вашем облачном коде, добавьте следующий блок кода в ваш products js файл parse cloud define("updateproduct", async (request) => { const { productid, name, description, price, quantityavailable, isactive } = request params; const currentuser = request user; if (!currentuser) { throw new error("you must be logged in to update a product "); } const usertype = currentuser get("usertype"); if (usertype !== "seller") { throw new error("only sellers can update products "); } if (!productid) { throw new error("missing required parameter productid "); } // 1 find the seller record for the current user const seller = parse object extend("seller"); const sellerquery = new parse query(seller); sellerquery equalto("user", currentuser); const sellerrecord = await sellerquery first({ usemasterkey true }); if (!sellerrecord) { throw new error("no matching seller record found for the current user "); } // 2 fetch the product const product = parse object extend("product"); const query = new parse query(product); query equalto("objectid", productid); query equalto("seller", sellerrecord); // must match the seller to ensure ownership const product = await query first({ usemasterkey true }); if (!product) { throw new error("product not found or you do not have permission to modify it "); } // 3 update product fields if (name !== undefined) product set("name", name); if (description !== undefined) product set("description", description); if (price !== undefined) product set("price", price); if (quantityavailable !== undefined) product set("quantityavailable", quantityavailable); if (isactive !== undefined) product set("isactive", isactive); // 4 save changes try { const updatedproduct = await product save(null, { usemasterkey true }); return { objectid updatedproduct id, name updatedproduct get("name"), description updatedproduct get("description"), price updatedproduct get("price"), quantityavailable updatedproduct get("quantityavailable"), isactive updatedproduct get("isactive"), }; } catch (error) { throw new error(`error updating product ${error message}`); } }); это гарантирует, что пользователь является продавцом, подтверждает право собственности на продукт и обновляет только те поля, которые указаны в запросе корзина функции корзина позволяют покупателям управлять товарами, которые они собираются купить эти функции включают добавление в корзину, обновление количеств, просмотр содержимого и удаление товаров добавление в корзину эта функция добавляет продукт в корзину покупателя или обновляет количество, если он уже существует для реализации этого создайте файл cart js и добавьте в него следующий код parse cloud define("addtocart", async (request) => { const { productid, quantity } = request params; // ensure there is a currently logged in user const currentuser = request user; if (!currentuser) { throw new error("you must be logged in to modify the cart "); } if (!productid || !quantity) { throw new error("missing required parameters productid and quantity "); } // 1 fetch or create the user's cart const cart = parse object extend("cart"); const cartquery = new parse query(cart); cartquery equalto("user", currentuser); let cart = await cartquery first({ usemasterkey true }); if (!cart) { cart = new cart(); cart set("user", currentuser); cart set("items", \[]); // initialize empty array cart set("totalprice", 0); // initialize price } // 2 fetch the product for price details (or any other attributes you need) const product = parse object extend("product"); const productquery = new parse query(product); productquery equalto("objectid", productid); const product = await productquery first({ usemasterkey true }); if (!product) { throw new error("product not found "); } const productprice = product get("price") || 0; // 3 insert or update the item in the cart const cartitems = cart get("items") || \[]; // check if this product is already in the cart const existingitemindex = cartitems findindex( (item) => item product objectid === productid ); if (existingitemindex >= 0) { // if product is already in cart, update the quantity cartitems\[existingitemindex] quantity += quantity; } else { // otherwise, add a new entry cartitems push({ product { type "pointer", classname "product", objectid productid, }, quantity quantity }); } // 4 recalculate total price // e g , summation of productprice quantity for each cart item let total = 0; for (const item of cartitems) { if (item product objectid === productid) { // use productprice from the newly fetched product total += productprice item quantity; } else { // this is a simplified approach ideally, you'd also store a price attribute on each item // or re fetch each product's price // for demonstration, we'll just skip them } } cart set("items", cartitems); cart set("totalprice", total); // 5 save the cart object try { const savedcart = await cart save(null, { usemasterkey true }); return { message "cart updated successfully", cartid savedcart id, items savedcart get("items"), totalprice savedcart get("totalprice"), }; } catch (error) { throw new error(`error saving cart ${error message}`); } }); это гарантирует, что пользователь аутентифицирован, проверяет продукт, обновляет список элементов и пересчитывает общую цену корзины запрос корзины получите содержимое корзины текущего пользователя, включая детали продуктов и промежуточные итоги чтобы реализовать это, добавьте следующий код в ваш cart js файл parse cloud define("getcart", async (request) => { if (!request user) throw "user must be logged in "; const user = request user; const cart = parse object extend("cart"); const cartquery = new parse query(cart); cartquery equalto("user", user); cartquery include("items product"); // deep include const cart = await cartquery first(); if (!cart) return { items \[], totalprice 0 }; const items = cart get("items") || \[]; const parseditems = \[]; for (const item of items) { const product = item product; parseditems push({ productid product id, name product get("name"), price product get("price"), image product get("image")? url() || null, quantity item quantity, subtotal product get("price") item quantity, }); } return { cartid cart id, items parseditems, totalprice cart get("totalprice") || 0, }; }); эта функция использует include("items product") для получения вложенных данных о продукте за один запрос редактирование корзины обновите количество товара в корзине или удалите его полностью, если новое количество равно нулю для реализации этого добавьте следующий код в ваш cart js файл parse cloud define("updatecart", async (request) => { const { productid, newquantity } = request params; const currentuser = request user; if (!currentuser) { throw new error("you must be logged in to update the cart "); } if (!productid || newquantity === undefined) { throw new error("missing required parameters productid and newquantity "); } // fetch or create the user's cart const cart = parse object extend("cart"); const cartquery = new parse query(cart); cartquery equalto("user", currentuser); let cart = await cartquery first({ usemasterkey true }); if (!cart) { // if there's no existing cart, create one cart = new cart(); cart set("user", currentuser); cart set("items", \[]); cart set("totalprice", 0); } let cartitems = cart get("items") || \[]; // find the item matching the productid const itemindex = cartitems findindex( (item) => item product id === productid ); if (itemindex === 1) { throw new error("product not found in cart please add it first "); } // if newquantity <= 0, remove the item from the cart if (newquantity <= 0) { cartitems splice(itemindex, 1); } else { // otherwise, update the quantity cartitems\[itemindex] quantity = newquantity; } // recalculate total by fetching current prices from each product let total = 0; if (cartitems length > 0) { const product = parse object extend("product"); // for each item in the cart, fetch the product to get its price for (const item of cartitems) { const productquery = new parse query(product); productquery equalto("objectid", item product objectid); const product = await productquery first({ usemasterkey true }); if (product) { const productprice = product get("price") || 0; total += productprice item quantity; } } } // update and save the cart cart set("items", cartitems); cart set("totalprice", total); try { const updatedcart = await cart save(null, { usemasterkey true }); return { message "cart updated successfully", cartid updatedcart id, items updatedcart get("items"), totalprice updatedcart get("totalprice"), }; } catch (error) { throw new error(`error updating cart ${error message}`); } }); он повторно проверяет все цены на продукты во время обновления, чтобы обеспечить полную точность удаление из корзины эта функция удаляет продукт из корзины пользователя чтобы реализовать это, добавьте следующий блок кода в ваш cart js файл parse cloud define("removefromcart", async (request) => { const { productid } = request params; const currentuser = request user; if (!currentuser) { throw new error("you must be logged in to remove items from the cart "); } if (!productid) { throw new error("missing required parameter productid "); } // fetch the user's cart const cart = parse object extend("cart"); const cartquery = new parse query(cart); cartquery equalto("user", currentuser); const cart = await cartquery first({ usemasterkey true }); if (!cart) { throw new error("no existing cart found for this user "); } let cartitems = cart get("items") || \[]; // filter out the item with the specified productid const filtereditems = cartitems filter( (item) => item product id !== productid ); // if nothing changed, the product wasn't in the cart if (filtereditems length === cartitems length) { throw new error("product not found in cart "); } // recalculate the cart total let total = 0; if (filtereditems length > 0) { const product = parse object extend("product"); for (const item of filtereditems) { const productquery = new parse query(product); productquery equalto("objectid", item product objectid); const product = await productquery first({ usemasterkey true }); if (product) { const productprice = product get("price") || 0; total += productprice item quantity; } } } // update and save the cart cart set("items", filtereditems); cart set("totalprice", total); try { const updatedcart = await cart save(null, { usemasterkey true }); return { message "item removed from cart", cartid updatedcart id, items updatedcart get("items"), totalprice updatedcart get("totalprice"), }; } catch (error) { throw new error(`error removing item from cart ${error message}`); } }); он фильтрует элемент из массива корзины и пересчитывает общую сумму перед сохранением заказ эти функции управляют процессом заказа — от создания заказов до получения истории заказов продавца создание заказа эта функция преобразует корзину покупателя в официальный заказ для реализации этого создайте файл order js и добавьте в него следующий код parse cloud define("createorder", async (request) => { const currentuser = request user; if (!currentuser) { throw new error("you must be logged in to place an order "); } // ensure the user is a buyer (assumes buyer class with user pointer) const buyer = parse object extend("buyer"); const buyerquery = new parse query(buyer); buyerquery equalto("user", currentuser); const buyer = await buyerquery first({ usemasterkey true }); if (!buyer) { throw new error("only buyers can place orders "); } // retrieve the cart for the user const cart = parse object extend("cart"); const cartquery = new parse query(cart); cartquery equalto("user", buyer get("user")); // assuming the user pointer is stored in buyer const cart = await cartquery first({ usemasterkey true }); if (!cart || !cart get("items") || cart get("items") length === 0) { throw new error("cart is empty"); } const items = cart get("items"); let totalamount = 0; // adjust stock and calculate total order price const orderdetails = \[]; for (const item of items) { const productid = item product id; const quantity = item quantity; const product = parse object extend("product"); const productquery = new parse query(product); productquery equalto("objectid", productid); const product = await productquery first({ usemasterkey true }); if (!product) { throw new error(`product with id ${productid} not found`); } const availablequantity = product get("quantityavailable"); if (availablequantity < quantity) { throw new error(`not enough stock for product ${product get("name")}`); } // reduce the product quantity product set("quantityavailable", availablequantity quantity); await product save(null, { usemasterkey true }); const unitprice = product get("price"); totalamount += unitprice quantity; // prepare order detail entry orderdetails push({ product, quantity, unitprice }); } // create the new order object const order = parse object extend("order"); const order = new order(); order set("buyer", buyer); // set the buyer pointer order set("total", totalamount); // set the total amount order set("orderstatus", "pending"); // set the order status order set("orderdate", new date()); // set the order date // save the order to the database try { const savedorder = await order save(null, { usemasterkey true }); // create orderdetails for each item const orderdetails = parse object extend("orderdetails"); for (const detail of orderdetails) { const orderdetail = new orderdetails(); orderdetail set("order", savedorder); orderdetail set("product", detail product); orderdetail set("quantity", detail quantity); orderdetail set("unitprice", detail unitprice); await orderdetail save(null, { usemasterkey true }); } // optionally, clear the cart after saving the order cart set("items", \[]); await cart save(null, { usemasterkey true }); return savedorder; } catch (error) { throw new error(`could not create order ${error message}`); } }); он проверяет покупателя, проверяет наличие товара на складе, уменьшает запасы продукта, сохраняет как заказ так и детализаказа , и очищает корзину после успешного заказа получение заказов продавца эта функция извлекает все заказы, которые включают продукты, проданные текущим продавцом чтобы реализовать это, добавьте следующий блок кода в ваш order js файл parse cloud define("getsellerorders", async (request) => { const currentuser = request user; if (!currentuser) { throw new error("you must be logged in to view your orders "); } if (currentuser get("usertype") !== "seller") { throw new error("only sellers can access this endpoint "); } try { const seller = parse object extend("seller"); const seller = await new parse query(seller) equalto("userid", currentuser) first({ usemasterkey true }); if (!seller) { throw new error("seller profile not found "); } // find products by this seller const product = parse object extend("product"); const productquery = new parse query(product); productquery equalto("seller", seller); const products = await productquery find({ usemasterkey true }); if (!products || products length === 0) { return \[]; } // get product ids const productids = products map(product => product id); // find order details containing these products const orderdetails = parse object extend("orderdetails"); const orderdetailsquery = new parse query(orderdetails); orderdetailsquery containedin("product", products); const orderdetails = await orderdetailsquery find({ usemasterkey true }); if (!orderdetails || orderdetails length === 0) { return \[]; } const orderids = \[ new set(orderdetails map(detail => detail get("order") id))]; // query orders for these ids const order = parse object extend("order"); const orderquery = new parse query(order); orderquery containedin("objectid", orderids); orderquery include("buyer"); const orders = await orderquery find({ usemasterkey true }); // return the orders return orders map(order => ({ id order id, total order get("total"), status order get("orderstatus"), date order get("orderdate"), buyer order get("buyer") })); } catch (error) { throw new error(`could not fetch orders ${error message}`); } }); он находит продукты, принадлежащие продавцу, запрашивает orderdetails для этих продуктов, извлекает соответствующие заказы и возвращает упрощенную структуру, включая информацию о покупателе обновление статуса заказа эта функция переключает статус заказа между «ожидается» и «завершен» чтобы реализовать это, добавьте следующий код в ваш order js файл parse cloud define("updateorder", async (request) => { const { orderid } = request params; const currentuser = request user; if (!currentuser) { throw new error("you must be logged in to view your orders "); } if (currentuser get("usertype") !== "seller") { throw new error("only sellers can access this endpoint "); } // validate the input parameter if (!orderid) { throw new error("missing required field orderid"); } try { // find the order with the specified orderid const order = parse object extend("order"); const query = new parse query(order); query equalto("objectid", orderid); const order = await query first({ usemasterkey true }); if (!order) { throw new error("order not found"); } // check current status // update the order status if (order get("orderstatus") === "completed") { order set("orderstatus", "pending"); } else { order set("orderstatus", "completed"); } // save the updated order await order save(null, { usemasterkey true }); return { success true, message "order status updated to completed" }; } catch (error) { throw new error(`could not update order status ${error message}`); } }); это гарантирует, что только продавцы могут выполнять это действие и что заказ существует перед обновлением статуса эти функции бэкенда предоставляют вашему приложению электронной коммерции возможность безопасно управлять всеми основными операциями — списками продуктов, корзинами покупателей, заказами и запасами продавцов — непосредственно в back4app с использованием cloud code чтобы обеспечить доступ к этим облачным функциям извне, вам нужно зарегистрировать все ваши файлы облачного кода в вашем main js файле, вы можете сделать это, добавив следующий блок кода в ваш main js файл //main js require(" /auth js") require(" /cart js") require(" /order js") require(" /product js") require(" /seller js") создание фронтенда чтобы визуально создать ваш интернет магазин, вы будете использовать v0 dev для этого начните с посещения v0 dev http //v0 dev создайте учетную запись, если у вас ее еще нет как только ваша учетная запись будет настроена, вы сможете начать создавать свой фронтенд чтобы создать ваш фронтенд, введите подсказку ниже create an e commerce web application with authentication features and a buyer and seller feature запрос создаст приложение электронной коммерции на next js с запрашиваемыми функциями create ecommerce app используя функцию предварительного просмотра, вы можете просмотреть приложение, чтобы подтвердить, что все работает как ожидалось если возникнут какие либо сложности или вы захотите добавить больше функций, вы можете передать другой запрос, и v0 изменит приложение например, передайте запрос the seller can create, add, delete and update products этот запрос изменит приложение, создавая страницы, на которых продавец сможет выполнять операции crud modify ecommerce app наконец, передайте запрос do not sort products by categories and remove the categories page and input fields, remove the second and third step from the process of becoming a seller and add a bio input field to the first step, users should only sign in with email, and enhance the seller functionality to fully support product management (crud operations) этот запрос внесет последние правки в приложение чтобы получить предварительный просмотр измененного приложения, нажмите на кнопку просмотр в выделенном разделе на изображении выше как только вы убедитесь, что приложение соответствует вашим требованиям, следующим шагом будет получение проекта на ваше локальное устройство для этого нажмите на кнопку загрузки, которую v0 генерирует с ответом на запрос кнопка загрузки нажатие на кнопку откроет выпадающее меню с ссылкой и кнопкой скачать zip выпадающее меню далее нажмите на кнопку скачать zip как только вы закончите загрузку zip, откройте терминал и создайте новую папку с именем ecommerce app в вашем предпочтительном каталоге mkdir ecommerce app теперь извлеките содержимое zip папки в папку ecommerce app перейдите в каталог ecommerce app в вашем терминале и установите необходимые зависимости для этого выполните следующую команду cd ecommerce app npm install после установки зависимостей выполните команду npm run dev в вашем терминале, чтобы просмотреть проект на вашем локальном сервере интеграция вашего фронтенда с бэкендом чтобы подключить ваш фронтенд к функциям cloud code в вашем бэкенде back4app, вы будете использовать parse javascript sdk sdk позволяет вам аутентифицировать пользователей, вызывать функции бэкенда и безопасно и эффективно взаимодействовать с моделями данных вашего приложения чтобы настроить sdk, выполните следующую команду в каталоге вашего проекта в терминале npm install parse затем создайте файл lib/parse js для настройки соединения в этом файле введите следующий блок кода import parse from "parse/dist/parse min js"; parse initialize("your app id", "your javascript key"); parse serverurl = "https //parseapi back4app com"; export default parse; замените your app id и your javascript key на учетные данные, найденные в вашей панели управления back4app в разделе app settings эта базовая конфигурация гарантирует, что sdk знает, как подключиться к вашему конкретному проекту теперь, когда вы подключили свой фронтенд к бэкенду, вы можете начать писать функции для вызова облачных функций кода, которые вы определили в своем приложении back4app аутентификация на клиенте этот раздел показывает, как обрабатывать регистрацию пользователей, вход в систему и выход из системы на вашем фронтенде с использованием parse sdk каждая функция соответствует функции облачного кода или методу sdk, который вы реализовали в своем бэкенде back4app в вашем приложении каталог app/auth/register содержит логику для регистрации пользователей в файле page tsx определите состояние formdata это состояние будет хранить учетные данные, необходимые для регистрации пользователя состояние formdata должно выглядеть следующим образом const \[formdata, setformdata] = usestate({ name "", email "", password "", confirmpassword "", shippingaddress "", paymentinfo "", }); с учетными данными, переданными в formdata , вы можете вызвать signupuser облачную функцию, которую вы определили в своем приложении back4app убедитесь, что вы импортировали parse из lib/parse js import parse from "@/lib/parse"; const handlesubmit = async(e react formevent) => { e preventdefault(); try { const response = await parse cloud run("signupuser", { name formdata name, email formdata email, password formdata password, confirmpassword formdata confirmpassword, shippingaddress formdata shippingaddress, paymentinfo formdata paymentinfo, }); console log("signup successful ", response); } catch (error any) { console error("signup failed ", error); } } эта функция будет выполняться, когда вы отправите форму регистрации она вызывает signupuser облачную функцию, определенную в cloud code функция передает учетные данные пользователя и данные, специфичные для покупателя (такие как информация о доставке и оплате) наконец, она выводит ответ, если операция успешна, или печатает ошибку, если она не удалась вход пользователей чтобы войти в систему, используйте функцию loginuser облачного кода в вашем бэкенде перейдите в директорию app/auth/login в вашем приложении в файле page tsx v0 создаст состояние, содержащее учетные данные пользователя, которые вам нужны для входа пользователя, все, что вам нужно сделать, это передать эти учетные данные в функцию loginuser например import parse from "@/lib/parse"; const handlesubmit = async (e react formevent) => { e preventdefault(); setisloading(true); try { const result = await parse cloud run("loginuser", { email, password }); // log in using returned session token await parse user become(result sessiontoken); console log("user logged in with session ", result sessiontoken); setisloading(false); router push("/"); // proceed to app } catch (error) { console error("login failed ", error); } }; эта функция вызывает облачную функцию loginuser , отправляя учетные данные для входа пользователя на сервер после получения действительного токена сессии в ответ она использует parse user become для аутентификации клиента и установления сессии как принадлежащей вошедшему пользователю выход пользователей чтобы безопасно выйти из системы, вы вызовете как parse user logout() так и вашу пользовательскую logoutuser функцию облачного кода это гарантирует, что токен сессии будет очищен как на клиенте, так и аннулирован на сервере вы найдете кнопку выхода в вашем компоненте заголовка этот компонент будет в вашей components папке в корневом каталоге в компоненте определите эту функцию const handlelogout = async () => { const sessiontoken = parse user current()? get("sessiontoken"); if (sessiontoken) { await parse user become(sessiontoken); // ensures proper context await parse cloud run("logoutuser"); // delete the session on server } await parse user logout(); // clear the local session }; эта функция выйдет из системы пользователя и очистит сессию как на клиенте, так и на сервере в части jsx компонента свяжите функцию с кнопкой выхода, используя onclick событие например \<dropdownmenuitem onclick={handlelogout}> logout \</dropdownmenuitem> онбординг продавца этот раздел показывает функцию, позволяющую покупателям обновить свою учетную запись и зарегистрироваться как продавцы в вашем приложении перейдите в директорию app/seller/register в этой директории вы найдете ваш page tsx файл, где вы определите функцию, которая вызывает createseller облачную функцию эта функция отвечает за создание профиля продавца для текущего пользователя в файле page tsx измените состояние formdata, чтобы оно выглядело так const \[formdata, setformdata] = usestate({ phone "", bio "", businessname "", }); замените логику в функции handlesubmit , созданной v0, на логику ниже const handlesubmit = async (e react formevent) => { e preventdefault(); try { const result = await parse cloud run("createseller", { businessname formdata businessname, bio formdata bio, phone formdata phone, }); console log("seller created ", result sellerid); } catch (error) { console error("error creating seller ", error); } }; вызовите эту функцию при отправке формы, чтобы создать профиль продавца для ваших пользователей управление продуктами используйте эти вызовы функций на вашем фронтенде для управления операциями с продуктами в качестве продавца создание продукта чтобы вызвать функцию облачного кода, которая добавляет продукт в базу данных, найдите директорию app/seller/products/new в этой директории находится ваш page tsx файл, в этом файле есть форма эта форма принимает информацию о продукте, включая изображения измените состояние product так, чтобы оно выглядело так const \[product, setproduct] = usestate({ name "", price "", description "", stock "", status "active", }); также создайте состояние для хранения ваших изображений const \[images, setimages] = usestate\<file\[]>(\[]); теперь напишите функцию для добавления изображений в базу данных эта функция должна выглядеть так const handleimageupload = async (event) => { event preventdefault(); try { let name = "image jpg"; const file = new parse file(name, event target files\[0]); const photo = await file save(); setimages((prev) => \[ prev, photo]); console log("file saved ", file); alert(`image uploaded successfully`); } catch (error) { console error("error saving file ", error); } }; привяжите эту функцию к полю ввода, ответственному за загрузку файла, используя событие onchange далее определите функцию handlesubmit эта функция вызывает addproduct облачный код, передавая необходимую информацию вот так const handlesubmit = async (e react formevent) => { e preventdefault(); try { interface addproductparams { name string; description string; price number; quantityavailable number; isactive boolean; } interface addproductresponse { id string; } parse cloud run\<addproductresponse, addproductparams>("addproduct", { name product name, description product description, price product price, quantityavailable product stock, isactive product status === "active", imagefiles images, }) then((response) => { console log("product created ", response id); }); } catch (error) { console error("error creating product ", error); } }; этот вызов отправляет данные о продукте, включая изображения, на сервер для создания нового объявления получить все продукты этот вызов извлекает весь каталог для публичных списков продуктов или ленты главной страницы чтобы создать функцию, которая вызывает fetchallproducts облачную функцию, перейдите в директорию app/products в файле page tsx , добавьте этот код interface product { id string; name string; description string; price number; image? string; } const \[products, setproducts] = usestate\<product\[]>(\[]); async function getallproducts() { try { const response = await parse cloud run("fetchallproducts"); console log("products fetched ", response); return response; } catch (error) { console error("error fetching products ", error); throw error; } } этот блок кода выше определяет интерфейс для товаров и состояние products функция getallproducts вызывает облачную функцию fetchallproducts для получения всех товаров из базы данных обратите внимание, что вам нужно вызвать функцию getallproducts выше внутри хука useeffect() , чтобы получить и отобразить товары при загрузке страницы вот так useeffect(() => { getallproducts() then((products) => { setproducts(products); }) catch((error) => { console error("error fetching products ", error); }); }, \[]); получить один продукт этот вызов получает полные данные о конкретном продукте перейдите в папку \[id] в вашем app/products каталоге эта папка содержит файл page tsx который будет содержать логику для получения данных о единственном продукте в файле page tsx напишите этот код interface product { id string; name string; description string; price number; images? string\[]; quantityavailable number; } const \[product, setproduct] = usestate\<product>({ id "", name "", description "", price 0, images \[], quantityavailable 0, }); async function getsingleproduct() { try { const response = await parse cloud run("getsingleproduct", {productid params id}); console log("product fetched ", response); return response; } catch (error) { console error("error fetching products ", error); throw error; } } useeffect(() => { getsingleproduct() then((product) => { setproduct(product); }) catch((error) => { console error("error fetching products ", error); }); }, \[]); этот блок кода определяет интерфейс для объекта product, настраивает переменную состояния product с значениями по умолчанию функция getsingleproduct вызывает вашу функцию облачного кода getsingleproduct с использованием productid из параметров маршрута этот useeffect хук выполняется один раз при монтировании компонента обновить продукт когда продавец редактирует продукт, вызовите эту функцию с обновленными полями чтобы обновить продукты, вам нужна форма с полями ввода для сбора новых данных, вы можете найти эту форму в файле page tsx расположенном в директории app/seller/products/\[id]/edit перед обновлением продукта вам нужно получить продукт, и для этого добавьте этот код в ваш page tsx файл const \[product, setproduct] = usestate({ id "", name "", price "", description "", stock "", images \["", "", ""], status "active", }); useeffect(() => { async function fetchproduct() { try { const response = await parse cloud run("getsingleproduct", { productid params id, }); setproduct({ id response objectid, name response name || "", price string(response price ?? "0"), description response description || "", stock string(response quantityavailable ?? "0"), // fallback to placeholders if actual image data is not provided images \[ response image1 || `/placeholder svg?height=500\&width=500\&text=product`, response image2 || `/placeholder svg?height=500\&width=500\&text=productview+2`, response image3 || `/placeholder svg?height=500\&width=500\&text=productview+3`, ], status response isactive ? "active" "out of stock", }); } catch (error) { console error("error fetching product ", error); } } fetchproduct(); }, \[]); этот код получает детали одного продукта из вашего бэкенда back4app с помощью функции облачного кода getsingleproduct и сохраняет детали в состоянии компонента product для отображения в качестве значения по умолчанию в вашей форме перед тем, как вы его измените после получения деталей продукта вы можете изменить детали, используя форму после заполнения формы отправка формы вызовет функцию handlesubmit , которая будет содержать логику для обновления вашего продукта ваша handlesubmit функция должна выглядеть так const handlesubmit = async (e react formevent) => { e preventdefault(); try { await parse cloud run("updateproduct", { productid product id, name product name, description product description, price parsefloat(product price), quantityavailable parseint(product stock, 10), isactive product status === "active", }); } catch (error) { console error("error updating product ", error); } }; эта функция вызывает вашу updateproduct функцию облачного кода и отправляет объект с обновленными данными продукта удалить продукт используйте эту функцию для удаления продукта после подтверждения от продавца чтобы удалить продукт, вам нужно добавить handledelete функцию, которая содержит логику, необходимую для удаления продукта вы определите handledelete функцию в файле page tsx в директории app/seller/products/\[id]/edit const handledelete = async () => { const confirmdelete = confirm( "are you sure you want to delete this product? this action cannot be undone " ); if (!confirmdelete) return; try { await parse cloud run("deleteproduct", { productid product id, }); } catch (error) { console error("error deleting product ", error); } }; функция handledelete сначала подтверждает намерение пользователя, прежде чем вызывать функцию облачного кода deleteproduct , определенную в вашем бэкенде привяжите функцию handledelete к кнопке удаления в jsx части компонента например \<button type="button" variant="destructive" onclick={handledelete} \> \<trash2 classname="h 4 w 4 mr 2" /> delete product \</button> получить продукты продавца используйте это для получения всех продуктов, принадлежащих текущему вошедшему в систему продавцу вы определите функцию для обработки получения продуктов продавца в файле page tsx в директории app/seller/dashboard в файле напишите этот код const \[products, setproducts] = usestate< { objectid string; name string; price number; quantityavailable number; sales number; isactive boolean; }\[] \>(\[]); useeffect(() => { async function fetchproducts() { try { const result = await parse cloud run("getsellerproducts"); setproducts(result); } catch (error) { console error("error fetching products ", error); } }; fetchproducts(); }, \[]); этот блок кода определяет состояние products и использует хук useeffect для вызова функции fetchproducts один раз при загрузке функция fetchproducts вызывает облачную функцию getsellerproducts и обновляет состояние products результатом вызова управление корзиной эти вызовы позволяют покупателям управлять своей корзиной перед оформлением заказа добавить в корзину этот вызов добавляет выбранный продукт в корзину пользователя или увеличивает его количество найдите файл page tsx в директории app/products и добавьте следующие строки кода async function handleaddtocart(productid string, quantity number = 1) { try { const response = await parse cloud run("addtocart", { productid, quantity, }); console log("add to cart success ", response); } catch (error) { console error("failed to add to cart ", error); } } функция handleaddtocart принимает productid и необязательное количество в качестве аргументов и вызывает addtocart облачный код на вашем сервере эта функция должна выполняться, когда ваши пользователи нажимают кнопку "добавить в корзину" в карточке продукта например \<button classname="w full" size="sm" onclick={() => handleaddtocart(product id)}> \<shoppingcart classname="h 4 w 4 mr 2" /> add to cart \</button> в файле напишите этот код посмотреть корзину используйте это, чтобы получить содержимое корзины и отобразить живое резюме корзины для этого перейдите к файлу page tsx в директории app/cart и добавьте блок кода ниже interface cartitem { id string; // product's objectid name string; price number; quantity number; image string; } const \[cartitems, setcartitems] = usestate\<cartitem\[]>(\[]); useeffect(() => { async function fetchcart() { try { const response = await parse cloud run("getcart"); const parsed = response items map((item) => ({ id item productid, name item name, price item price, quantity item quantity, image item image, })); setcartitems(parsed); } catch (error) { console error("failed to fetch cart ", error); } } fetchcart(); }, \[]); блок кода выше определяет структуру данных корзины на фронтенде с помощью интерфейса cartitem , определяет состояние cartitems , которое хранит элементы корзины он также использует useeffect для вызова функции fetchcart один раз при загрузке страницы функция fetchcart вызывает облачную функцию getcart и получает корзину из вашего бэкенда она использует метод map для преобразования формата бэкенда в формат фронтенда перед сохранением обработанных данных корзины в состоянии cartitems обновить элемент корзины этот вызов изменяет количество конкретного продукта в корзине в файле page tsx в директории app/cart вы найдете функцию updatequantity замените эту функцию на приведенную ниже const updatequantity = async (productid string, newquantity number) => { if (newquantity < 1) return; try { await parse cloud run("updatecart", { productid, newquantity, }); console log("updated the cart"); } catch (error) { console error("failed to update cart item ", error); } }; эта функция принимает два параметра, productid и обновленное количество, которое пользователь хочет установить newquantity она вызывает вашу updatecart облачную функцию и отправляет как productid так и newquantity в качестве параметров вы связываете эту функцию с кнопками +/ с помощью события onclick вот так \<div classname="flex items center gap 2"> \<button variant="outline" size="icon" classname="h 8 w 8" onclick={() => updatequantity(item id, item quantity 1)} \> \<minus classname="h 3 w 3" /> \<span classname="sr only">decrease quantity\</span> \</button> \<span classname="w 8 text center">{item quantity}\</span> \<button variant="outline" size="icon" classname="h 8 w 8" onclick={() => updatequantity(item id, item quantity + 1)} \> \<plus classname="h 3 w 3" /> \<span classname="sr only">increase quantity\</span> \</button> \</div> удалить из корзины полностью удаляет элемент из корзины для этого добавьте блок кода ниже в файл page tsx в директории app/cart в этом файле const removeitem = async (productid string) => { try { await parse cloud run("removefromcart", { productid, }); setcartitems((items) => items filter((item) => item id !== productid)); } catch (error) { console error("failed to remove item ", error); } }; эта функция, removeitem , удаляет конкретный продукт из корзины пользователя, вызывая облачную функцию removefromcart на сервере функция также обновляет локальное состояние корзины на фронтенде, чтобы отразить изменения привяжите эту функцию к кнопке "удалить элемент", как показано ниже \<button variant="ghost" size="icon" classname="h 8 w 8 text muted foreground" onclick={() => removeitem(item id)} \> \<trash2 classname="h 4 w 4" /> \<span classname="sr only">remove item\</span> \</button> управление заказами эти функции обрабатывают создание и отслеживание заказов для покупателей и продавцов создать заказ это создает заказ из текущего содержимого корзины пользователя вы также определяете эту функцию в файле page tsx в директории app/cart функция, содержащая логику для создания заказа, должна выглядеть так const handleorder = async () => { try { const user = parse user current(); if (!user) { throw new error("you must be logged in to place an order "); } const result = await parse cloud run("createorder"); console log("order created successfully ", result); } catch (error any) { console error("failed to create order ", error); } }; эта функция, handleorder , обрабатывает процесс оформления заказа, размещая заказ через ваш бэкенд back4app она проверяет, вошел ли пользователь в систему, а затем вызывает функцию createorder облака, чтобы преобразовать корзину текущего пользователя в официальный заказ чтобы вызвать это при оформлении заказа, привяжите функцию к кнопке "checkout" с помощью события onclick например \<button classname="w full" onclick={handleorder}>proceed to checkout\</button> просмотр заказов продавца продавцы могут использовать этот вызов, чтобы просмотреть заказы, которые включают их продукты определите эту функцию в файле page tsx в директории app/seller/dashboard добавьте следующие строки кода в файл page tsx const \[ordersdata, setordersdata] = usestate<{ id string; total number; buyer string; date date; status string; }\[] \>(\[]); async function fetchsellerorders() { try { const orders = await parse cloud run("getsellerorders"); console log("seller orders ", orders); setordersdata(orders); } catch (err) { console error("error fetching seller orders ", err); } }; этот код получает список заказов, которые включают продукты продавца, и сохраняет их в локальном состоянии ordersdata для отображения на панели управления продавца или в представлении управления заказами убедитесь, что функция вызывается в хуке useeffect , чтобы функция выполнялась один раз при загрузке страницы обновить заказ это переключает статус заказа между "ожидается" и "завершен" определите эту функцию в файле page tsx в директории app/seller/dashboard функция должна выглядеть так const handleupdate = async (id string) => { const confirmupdate = confirm( "are you sure you want to update this order?" ); if (!confirmupdate) return; try { await parse cloud run("completeorder", { orderid id, }); console log("updated order"); } catch (error) { console error("error updating order ", error); } }; эта функция, handleupdate , обновляет статус существующего заказа, вызывая облачную функцию с именем completeorder , на вашем бэкенде back4app привяжите эту функцию к кнопке "редактировать" в части jsx компонента вот так \<button variant="ghost" size="icon" onclick={() => {handleupdate(order id)}}> \<pencil classname="h 4 w 4" /> \<span classname="sr only">view\</span> \</button> развертывание фронтенда на контейнерах back4app контейнеры back4app предлагают упрощенный способ упаковки и развертывания вашего фронтенд приложения вместо того чтобы управлять отдельными хостинг сервисами, вы можете хранить все зависимости в образе docker, обеспечивая стабильную производительность и упрощая обслуживание этот контейнерный подход особенно полезен для приложений next js, где вы можете оптимизировать сборки для скорости и надежности чтобы контейнеризовать ваше приложение, создайте dockerfile , чтобы определить, как ваше приложение собирается и обслуживается ниже приведен пример dockerfile для приложения next js # stage 1 build the next js app from node 20 alpine as builder workdir /app copy package json package lock json / run npm install copy run npm run build \# stage 2 run the next js app from node 20 alpine workdir /app copy from=builder /app / expose 3000 cmd \["npm", "start"] после создания вашего dockerfile создайте dockerignore файл и добавьте эти команды # node modules (reinstalled in docker) node modules \# next js build output next out \# logs npm debug log yarn debug log yarn error log pnpm debug log \# env files (optional — only if you handle secrets another way) env env local env development env production env test \# os / ide / editor junk ds store thumbs db vscode idea \# git git gitignore теперь вы можете собирать и тестировать ваше приложение локально для этого выполните следующую команду в вашем терминале docker build t ecommerce app docker run p 3000 3000 ecommerce app откройте http //localhost 3000 чтобы убедиться, что ваш сайт работает правильно как только вы убедитесь, что все работает так, как ожидалось, пришло время отправить ваш код на github и развернуть его через back4app убедитесь, что ваш код зафиксирован и отправлен в репозиторий github это позволит back4app получить доступ к вашему проекту для развертывания в вашей учетной записи back4app перейдите на панель управления и нажмите на панель управления в выпадающем меню в верхней части экрана dashboard menu в выпадающем меню выберите платформа веб развертывания web deployment platform это перенесет вас на страницу развертывания веб приложения deploy web application page нажмите кнопку развернуть веб приложение чтобы начать процесс развертывания link github repo затем нажмите на импортировать репозиторий github и выберите репозиторий, который содержит ваш dockerfile назначьте имя вашему проекту (например, ecommerce back4app) следуйте пошаговой настройке, чтобы создать и развернуть ваше приложение после завершения развертывания back4app отобразит страницу подтверждения, подобную этой успешное развертывание выделенная область на изображении выше — это url, по которому ваше приложение доступно вы можете использовать его для просмотра и тестирования вашего приложения непосредственно в веб браузере после развертывания используйте панель управления back4app для мониторинга журналов сборки, отслеживания состояния контейнеров и отката к предыдущим версиям, если это необходимо панель управления отображает обновления в реальном времени о состоянии контейнеров, что упрощает обнаружение ошибок и поддержание стабильной среды для ваших пользователей с контейнерами вы получаете гибкое, портативное решение для фронтенда, которое без проблем сочетается с вашим бэкендом back4app, прокладывая путь для последовательных, безпроблемных релизов вы можете посетить веб сайт электронной коммерции, созданный в этом учебнике здесь https //ecommerceback4app mcphit37 b4a run/ заключение поздравляем — вы теперь собрали каждый слой проверенного временем, готового к производству стека электронной коммерции настройка бэкенда отprovisioning вашего аккаунта back4app до моделирования пользователей, продуктов и заказов безопасность и api реализация надежной аутентификации и предоставление ваших данных через rest и graphql конечные точки бизнес логика и обновления в реальном времени автоматизация рабочих процессов с помощью cloud code и потоковая передача изменений через livequery развертывание фронтенда подключение javascript sdk и развертывание магазина next js в контейнерах back4app что дальше? погрузитесь в документацию back4app https //www back4app com/docs и руководство по parse platform https //docs parseplatform org для углубленной справки или присоединяйтесь к форуму сообщества, чтобы получить продвинутые советы и примеры приложений от кураторов и других разработчиков этот фундамент — только начало расширьте его, интегрируя вебхуки и запланированные задания, подключая платежные шлюзы или создавая мобильное приложение компаньон продолжайте итерации, рефакторинг и развитие вашей кодовой базы — и наблюдайте, как ваш магазин растет вместе с вашими амбициями