Quickstarters
如何构建电子商务后台?
75 分
在这个实践教程中,您将从头开始创建一个坚如磐石的全栈电子商务应用程序,使用后端即服务。在此过程中,您将掌握 安全的用户身份验证,确保只有授权的购物者和管理员可以访问 直观的数据模型,旨在支持产品目录、客户档案和订单历史 restful 和实时 api,使您的前端始终与库存、购物车和结账状态保持同步 自动化部署管道,让您自信地推送更新,并在出现问题时回滚 到最后,您将不仅拥有一个生产就绪的商店前端,还将具备扩展、缩放和保护任何 web 应用程序的架构知识。 在 web 电子商务应用程序 中查看完成的电子商务应用程序的实际操作。 让我们开始吧! 关键要点 快速后端设置 使用 back4app 的低代码平台在几分钟内启动和配置完整的电子商务数据模型。 安全用户流程 使用云代码和会话令牌实现强大的注册、登录和注销功能。 api优先开发 通过自动生成的 rest/graphql 端点和 parse sdk 访问您的数据,适用于任何客户端。 云代码中的业务逻辑 集中核心操作——产品 crud、购物车管理和结账——同时强制执行权限。 容器化前端部署 在 back4app web 部署中打包和部署您的 next js 商店,以实现一致、可扩展的托管。 先决条件和环境设置 要遵循本教程,请确保您具备以下条件 一个 back4app 账户。您可以在 back4app 网站 https //www back4app com/signup 免费注册。 安装 node js,以便您可以使用 npm 或 yarn 管理依赖项。 熟悉 javascript,以便编写自定义云代码并利用 back4app 的 sdk。 对电子商务概念有基本了解,如用户身份验证、产品目录管理和订单处理。 使用 git 进行版本控制,以便您可以跟踪更改、在必要时回滚并无缝协作。 确保您拥有上述项目将帮助您最大化 back4app 的功能,并实现关键的电子商务功能,而不会陷入困境。 创建和配置您的 back4app 账户 要开始在 back4app 上设置您的电子商务后端,请访问 back4app https //www back4app com 并点击 注册 输入您的电子邮件地址,选择一个强密码,并提供任何所需的详细信息。一旦您验证了您的电子邮件,您将获得对 back4app 仪表板的访问权限。 back4app 注册页面 在仪表板内,选择 创建新应用 或 构建新应用 并为您的项目命名——选择一个描述性的名称,以便您可以轻松找到它。 确认您的选择,然后等待应用程序创建过程完成。项目仪表板将显示类、分析和配置。 创建一个 back4app 应用 通过这些步骤,您已经创建了一个电子商务平台。在下一部分中,您将设计数据架构并为产品管理、订单处理和用户身份验证做好准备。 设计您的电子商务数据架构 为了为您的电子商务平台实施可扩展和可维护的后端,您将围绕七个核心表组织数据: 用户 , 买家 , 卖家 , 产品 , 购物车 , 订单 , 和 订单详情 每个表在支持系统的整体功能方面发挥着专注的作用,它们共同形成一个规范化和高效的架构。 首先是 用户 表,它作为所有用户身份的基础。您平台上的每个人——无论他们的角色如何——都将使用此表进行身份验证。 包括诸如 用户名 , 密码 , 和 电子邮件 的字段以处理基本登录。添加 电子邮件验证 以跟踪验证状态,以及 身份验证数据 如果您计划支持第三方登录或oauth流程。 为了区分买家和卖家,定义一个 用户类型 字段。此字段使您能够控制访问、管理视图,并根据用户角色分离功能,同时仍然保持单一、一致的登录系统。 与其将所有用户类型混合在一个表中,不如创建单独的 买家 和 卖家 表,通过 用户 的 用户id 字段进行引用。这使您能够存储特定角色的数据,而不会使核心用户表膨胀。 买家 表可以保存与购买相关的字段,如 支付信息 和 送货地址 , 这些对于结账和交付过程至关重要。相比之下, 卖家 表跟踪与商家相关的属性,如 店名 , 简介 , 和 电话号码 您的 产品 表是存储待售商品的地方。每个产品应包括基本描述信息,如名称、详细描述和一张或多张图片。 对于定价和库存管理,您还需要包括价格、可用状态的布尔字段 isactive ,以及 quantityavailable 下的库存数量。 至关重要的是,每个产品必须与列出它的卖家相关联。要建立这种关系,请添加一个 sellerid 外键,引用 seller 表。 这种结构确保您的目录中的每个项目都与经过验证的商家相关联,并使卖家特定的过滤变得简单。 为了处理购买前的行为,引入一个 cart 表。每个购物车属于一个用户,因此请包括一个 userid 字段,连接回 user 。 存储一个购物车 items 的数组,每个代表用户打算购买的产品,以及您需要的任何元数据,如数量或选择的选项。 跟踪购物车的 totalprice 使您能够快速显示结账摘要,而无需在每次请求时重新计算值。 此设置支持基本的购物车功能,并简化会话持久性。如果您发现数组结构有限,特别是如果您需要记录更复杂的项目级数据,您可以随时将项目重构为单独的表。 当买家下订单时,您将记录交易在 order 表中。每个订单与一个 buyerid 相关联,引用 buyer 表以确保购买者是有效和授权的。 除了这个链接外,还要存储 orderdate 、当前的 orderstatus 和 total 字段下的总货币价值。这为您提供了购买的清晰摘要,并支持订单跟踪和履行等工作流程。 由于每个订单可能包含多个产品,您需要一个 orderdetails 表来管理单个行项目。 该表通过 orderid 将每个项目连接回其父订单,并使用 productid 连接到购买的产品。 为了准确记录购买状态,请包括购买的 数量 和销售时的 单价 。 以这种方式分离订单细节可以让您精确地生成报告、开具发票以及处理退货或调整。 这个架构通过清晰的关注点分离和关系完整性,为您的电子商务系统奠定了坚实的基础。 现在数据模型与您平台的现实工作流程对齐,您准备好在后端实施它了。首先设置 用户 , 买家 , 和 卖家 表,然后从那里向外构建。 在仪表板中设置类 按照以下步骤在 back4app 中创建和链接您的实体。您将为 用户 , 买家 , 卖家 , 购物车 , 产品 , 订单 , 和 订单详情 , 然后建立指针以表示继承、一对多和多对多等关系。 访问数据库浏览器 打开您的应用程序仪表板并创建一个名为用户的新类。 从左侧菜单中点击 数据库 。 back4app dashboard 创建“用户”类 back4app 自动创建一个 用户 类。 用户 类包含以下列: 用户名 密码 电子邮件 等等 要添加 用户类型 列,请选择 添加新列 add new column 提供列的名称、数据类型、默认值等。 add usertype column 注意 在 parse 中,每个类都包含一个默认的 objectid 字段,作为主键。这意味着 objectid 将有效地作为 userid 在你的关系模型中 创建“买家”类 要创建买家类,请返回到 数据库浏览器 并选择 添加类 create new class 选择 自定义 并命名为 买家 add buyer class 添加一个 指针 列,名为 用户 , 指向 user 类 create userid class 这通过引用基础用户的 objectid 来模拟“继承”。 3\ 添加以下列 收货地址 (字符串) 支付信息 (字符串) 当你创建一个新的买家对象时,你将通过设置 buyer user 来链接到现有的用户记录,该用户的 objectid 创建“卖家”类 重复相同的过程来创建一个 卖家 类。添加一个 指针 列,命名为 user ,引用 user 类。添加以下额外列 商店名称 (字符串) 简介 (字符串) 电话号码 (数字) 创建“产品”类 选择 创建一个类 再次命名为 产品 添加一个 指针 列,名为 卖家 指向 卖家 类(这强制执行一对多关系,其中一个卖家可以有多个产品)。添加以下列 名称 (字符串) 描述 (字符串) 价格 (数字) 可用数量 (数字) 是否激活 (布尔值) 图片 (数组) 存储表示产品图片的对象。 创建“购物车”类 点击 创建一个类 , 将类命名为 购物车 添加一个名为 用户 的指针列,指向 用户 类(这将每个购物车链接到特定用户)。然后添加以下列以表示购物车中的物品 物品 (数组) 存储表示每个购物车物品的对象。每个物品可以是一个对象,如 { product 指向产品的指针, quantity 数字 } 总价格 (数字) 创建“订单”类 创建一个名为 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 部分。 find cloud code 当您点击 cloud code , 您将看到一个 main js ,您可以在其中开始实现自定义函数: 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); } }); cloud code 此云函数处理买家的用户注册。它验证输入,创建一个新的 parse 用户,分配一个角色 ( 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' }; }); 这会安全地结束后端的会话,防止进一步使用该令牌。 有了这些云代码功能,您现在拥有一个安全的身份验证系统,它可以: 使用基于角色的逻辑注册用户 使用会话令牌进行身份验证和授权访问 将用户帐户链接到特定买家的记录 支持干净的登录和登出流程 利用back4app内置的会话和电子邮件验证工具 为您的电子商务应用构建业务逻辑 要在您的电子商务平台上管理用户、产品、购物车和交易,您需要在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}`); } }); 此功能检查用户是否已登录并具有卖家角色,是否存在相应的 seller 对象,以及提交的图像不超过三张。一旦验证通过,它将存储产品并将其链接到卖家。 查询所有产品 此功能检索系统中的所有产品,可用于构建公共商店前台。要在您的云代码中实现此功能,请将以下代码块添加到您的 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 命令以在您的本地主机服务器上查看项目。 将前端与后端集成 要将前端与 back4app 后端中的 cloud code 函数连接,您将使用 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在前端处理用户注册、登录和注销。 每个函数对应于您在back4app后端实现的云代码函数或sdk方法。 在您的应用程序中, app/auth/register 目录包含用户注册的逻辑。在 page tsx 中定义 formdata 状态。此状态将保存注册用户所需的凭据。 该 formdata 状态应如下所示: const \[formdata, setformdata] = usestate({ name "", email "", password "", confirmpassword "", shippingaddress "", paymentinfo "", }); 使用传递到 formdata 的凭据,您可以调用在 back4app 应用程序中定义的 signupuser 云函数。确保从 lib/parse js 导入 parse。 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); } } 当您提交注册表单时,此函数将运行。它调用在 cloud code 中定义的 signupuser 云函数。 该函数传递用户凭据和买方特定数据(如运输和支付信息)。最后,如果操作成功,它将记录响应,如果失败则打印错误。 用户登录 要登录用户,请在您的后端中使用 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 "", }); 用下面的逻辑替换 v0 创建的 handlesubmit 函数中的逻辑。 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 来获取数据库中的所有产品。 请注意,您需要在 useeffect() 钩子中调用上面的 getallproducts 函数,以便在页面加载时获取和渲染产品。 像这样: 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 。 该 getsingleproduct 函数使用来自路由参数的 productid 调用你的 getsingleproduct 云代码函数。此 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(); }, \[]); 此代码使用 getsingleproduct 云代码函数从您的 back4app 后端获取单个产品的详细信息,并将详细信息存储在组件状态 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 函数,该函数包含删除产品所需的逻辑。 你将在 page tsx 文件中定义 handledelete 函数,路径为 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 云函数,将当前用户的购物车转换为正式订单。 要在结账时调用此功能,请使用 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 的云函数来更新现有订单的状态。将此功能绑定到组件的 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 镜像中,而不是 juggling 单独的托管服务,从而确保一致的性能并简化维护。 这种基于容器的方法对于 next js 应用程序尤其有用,您可以优化构建以提高速度和可靠性。 要将您的应用程序容器化,请创建一个 dockerfile 以定义您的应用程序如何构建和提供。以下是一个 next js 应用程序的示例 dockerfile: \# 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 web deployment platform 这将带您到网络应用程序部署页面: deploy web application page 点击 部署 web 应用程序 按钮以开始部署过程。 link github repo 然后,点击 导入 github 仓库 并选择包含您的 dockerfile 的仓库。 为您的项目分配一个名称(例如,ecommerce back4app)。 按照引导设置构建和部署您的应用程序。 一旦部署完成,back4app将显示如下确认页面: successful deployment 上图中高亮的区域是您的应用程序在线的url。您可以使用它直接在网页浏览器中查看和测试您的应用。 一旦部署,使用back4app仪表板监控构建日志,跟踪容器健康状况,并在需要时回滚到以前的版本。 仪表板实时显示容器状态更新,使您更容易发现错误并为用户维护稳定的环境。 通过容器,您获得了一个灵活、可移植的前端解决方案,与您的back4app后端无缝配合,为一致、无忧的发布铺平了道路。您可以访问本教程中构建的电子商务网站 这里 https //ecommerceback4app mcphit37 b4a run/ 。 结论 恭喜您——您现在已经组装了一个经过实战检验、准备投入生产的电子商务堆栈的每一层: 后端设置 从配置您的back4app账户到建模用户、产品和订单 安全性与api 实施强大的身份验证,并通过rest和graphql端点公开您的数据 业务逻辑与实时更新 使用云代码自动化工作流程,并通过livequery流式传输实时更改 前端部署 连接javascript sdk并在back4app容器中发布next js商店 接下来是什么?深入了解 back4app文档 https //www back4app com/docs 和 parse平台指南 https //docs parseplatform org 以获取深入参考,或加入社区论坛,从维护者和其他开发者那里获取高级技巧和示例应用。 这个基础只是开始——通过集成网络钩子和定时任务、连接支付网关或构建移动伴侣应用来进一步扩展它。 不断迭代、重构和发展您的代码库——并观察您的商店与您的雄心一起成长。