开始学习美杜莎第二部分:打造属于你自己的服务器
在本系列教程的第一部分中,我对比了Medusa和 Shopify,展示了Medusa如何 成为 Shopify 的开源替代方案。Shopify 在定价方案、定制功能有限以及无法满足所有业务需求等方面存在不足,而 Medusa 可以弥补这些缺陷。
Medusa 是一款开源的无头电商解决方案,它允许您自主掌控技术栈,并使其适应您业务所需的任何用例。它速度快、灵活性高。
在之前的教程中,您学习了 Medusa 的三个组件以及如何安装和运行它们。整个过程非常简单,只需几秒钟即可让您的网店上线运行。
在本教程中,您将开始对服务器进行修改,使其符合您的需求。您将学习如何创建新的 API 端点、服务和订阅者。您创建的 API 将检索销量最高的产品,您还将创建一个服务和订阅者来帮助我们实现这一目标。
本教程的代码位于此 GitHub 存储库中。
先决条件
本教程假设您已阅读并完成了第一部分。在第一部分中,您将学习如何设置 Medusa 商店(本教程中您将对其进行修改)、Medusa 店面和管理后台。如果您尚未完成第一部分,请先完成该部分再继续学习本教程。
此外,您需要在机器上安装并运行 Redis 才能使用订阅者功能。因此,如果您尚未安装 Redis 并想跟随本教程进行操作,请先进行安装。
添加服务
如前所述,您将创建一个 API 端点,以便获取最畅销的产品,即销量最高的产品。
在 Medusa 中,服务通常集中处理模型或实体的逻辑。它们包含辅助函数,允许您检索这些模型或对其执行操作。一旦将它们放入服务中,您就可以从 Medusa 项目中的任何位置访问该服务。
因此,在本教程中,您将创建一个服务,TopProductsService该服务将包含更新产品销售数量以及按销售数量排序检索产品所需的所有逻辑。
要创建服务,首先创建src/services/top-products.js包含以下内容的文件:
import { BaseService } from "Medusa-interfaces";
class TopProductsService extends BaseService {
constructor({ productService, orderService }) {
super();
this.productService_ = productService;
this.orderService_ = orderService;
}
}
关于这项服务,有几点需要注意:
- 当在代码的其他地方检索此服务时,应使用文件名的驼峰式命名法加上“Service”来引用该服务。在本例中,文件名是
top-product,因此要在其他地方访问它,我们使用topProductsService。 - 与您使用此服务的方式类似,我们在构造函数中注入依赖项
productService。orderService在 Medusa 中创建类时,您可以使用依赖注入来访问服务。
实现 getTopProducts 函数
下一步是将该方法添加getTopProducts到TopProductsService类中。该方法将从数据库中检索产品,按销量排序,然后返回销量排名前 5 的产品。
在TopProductsService类中添加新方法:
async getTopProducts() {
const products = await this.productService_.list({
status: ['published']
}, {
relations: ["variants", "variants.prices", "options", "options.values", "images", "tags", "collection", "type"]
});
products.sort((a, b) => {
const aSales = a.metadata && a.metadata.sales ? a.metadata.sales : 0;
const bSales = b.metadata && b.metadata.sales ? b.metadata.sales : 0;
return aSales > bSales ? -1 : (aSales < bSales ? 1 : 0);
});
return products.slice(0, 4);
}
首先,您可以使用该方法this.productService_检索产品列表。请注意,该list方法可以接受两个可选参数。第一个参数指定筛选条件,第二个参数指定要检索的产品之间的关系。
然后,使用`sort Array` 方法对数组进行排序,并为其添加一个比较函数。在比较函数中,比较metadata字段中存储的销售数量。在 Medusa 中,大多数实体都包含该metadata字段,方便您根据需要轻松地在默认实体中添加自定义属性。在这里,您使用该metadata字段来存储销售数量。此外,您还按降序对产品进行排序。
最后,您可以使用splice Array 方法仅检索前 5 个项目。
实施更新销售
接下来,您将实现该updateSales方法TopProductsService。此方法接收一个订单 ID 作为参数,然后检索该订单并遍历订购的商品。之后,递增sales内部属性并更新产品信息。metadata
添加新方法TopProductsService:
async updateSales(orderId) {
const order = await this.orderService_.retrieve(orderId, {
relations: ["items", "items.variant", "items.variant.product"]
});
if (order.items && order.items.length) {
for (let i = 0; i < order.items.length; i++) {
const item = order.items[i];
//retrieve product by id
const product = await this.productService_.retrieve(item.variant.product.id, {
relations: ["variants", "variants.prices", "options", "options.values", "images", "tags", "collection", "type"]
});
const sales = product.metadata && product.metadata.sales ? product.metadata.sales : 0;
//update product
await this.productService_.update(product.id, {
metadata: { sales: sales + 1 }
});
}
}
}
首先,您可以使用该方法this.orderService_通过订单 ID 获取订单。该retrieve方法以订单 ID 作为第一个参数,以配置对象作为第二个参数,该配置对象与您在之前的方法中使用的配置对象类似。然后,您需要传递关联数组以检索已订购的商品及其对应的产品。
然后,遍历所有商品,并使用每件商品中的产品 ID 来检索该产品。之后,增加销售数量,并使用该update方法更新产品this.productService_。
该服务现已准备就绪,可以更新产品销售数据并根据销售数据检索已订购的产品。
添加 API 端点
现在,您需要添加一个 API 端点来检索热门产品。要添加 API 端点,您可以创建src/api/index.js包含以下内容的文件:
import { Router } from "express"
export default () => {
const router = Router()
router.get("/store/top-products", async (req, res) => {
const topProductsService = req.scope.resolve("topProductsService")
res.json({
products: await topProductsService.getTopProducts()
})
})
return router;
}
创建端点很简单,只需导出Express Router 即可。这个路由器可以包含任意数量的路由。
在这段代码中,您在端点添加了一个新的 GET 路由/store/top-products。之所以在这里使用 `@ storestorefront` 作为前缀,top-products是因为 Medusa 会将所有 storefront 端点都加上 `@storefront` 前缀/store,将所有 admin 端点都加上 `@admin`/admin前缀。您并非必须添加此前缀,但遵循 Medusa API 的约定是有益的。
在这条路径中,您可以使用以下代码行检索上一节中创建的服务:
const topProductsService = req.scope.resolve("topProductsService")
您可以使用 `.` 来检索路由中的任何服务。如服务部分所述,在代码中引用服务时,req.scope.resolve需要使用驼峰式文件名,后跟 `.` 。Service
获取服务后,您就可以使用您创建的方法了。因此,您将返回一个 JSON 响应,其中包含键products,值将是返回的热门产品数组getTopProducts。
让我们来测试一下。您可以通过以下地址访问此端点localhost:9000/store/top-products:。由于这是一个 GET 请求,您可以通过浏览器或使用Postman或Thunder Client等客户端来完成此操作。
你应该会在响应中看到一个产品数组。目前,由于你还没有实现用于更新销售数据的订阅者,所以数据尚未排序。
添加订阅者
最后,您将添加一个订阅者,该订阅者将在下订单时更新产品的销售数量。
创建订阅者之前,您需要确保 Redis 已安装并运行在您的机器上。您可以通过在终端中运行以下命令进行测试:
redis-cli ping
如果命令返回“PONG”,则表示 Redis 服务正在运行。
然后,转到Medusa-config.js项目根目录。你会看到导出的配置文件末尾有一行被注释掉了:
// redis_url: REDIS_URL,
移除注释。这段代码使用REDIS_URL文件开头声明的变量。它的值可以是设置的 Redis URL,.env也可以是默认的 Redis URL redis://localhost:6379。如果您使用了不同的 Redis URL,请将新变量及其对应的 URL 添加REDIS_URL到.env代码中。
然后重启服务器。这将应用更新后的配置并连接到您的 Redis 服务器。
src/subscribers/top-products.js现在,您将实现订阅器。创建包含以下内容的文件:
class TopProductsSubscriber {
constructor({ topProductsService, eventBusService }) {
this.topProductsService_ = topProductsService;
eventBusService.subscribe("order.placed", this.handleTopProducts);
}
handleTopProducts = async (data) => {
this.topProductsService_.updateSales(data.id);
};
}
export default TopProductsSubscriber;
和您实现的方式类似TopProductsService,您需要topProductsService在构造函数中使用依赖注入传递参数。您还需要传递参数eventBusService。这用于在构造函数中订阅事件处理程序。
您可以通过以下代码订阅订单已下单事件:
eventBusService.subscribe("order.placed", this.handleTopProducts);
该subscribe方法eventBusService以事件名称作为第一个参数,以处理程序作为第二个参数。
然后,您需要在类中定义handleTopProducts处理order.placed事件的方法。Medusa 中的事件处理程序通常会接收一个data对象,该对象包含一个id属性,其中包含与此事件相关的实体的 ID。因此,您需要将此 ID 传递给更新订单中每个产品销售数量的updateSales方法。this.topProductsService_
测试一下
现在您需要测试所有功能。请确保服务器正在运行。如果服务器没有运行,请使用以下命令运行它:
npm start
然后,前往 Medusa 店铺安装页面并运行:
npm run dev
前往店铺页面下单。这将触发系统TopProductsSubscriber更新该订单中商品的销售数据。
/store/top-products现在,像之前一样发送请求。你应该会看到该订单中产品的属性值有所增加sales。metadata
尝试从管理面板添加新产品,或者使用本教程GitHub 代码库中的数据库(其中包含一个额外的产品)。然后,尝试使用该产品下更多订单。您会发现,端点中的排序方式已根据销售数量而改变。
结论
在本教程中,您学习了如何添加自定义 API 端点、服务和订阅者。您可以使用这三者在您的商店中实现任何自定义功能或集成。
在下一个教程中,您将使用在本部分创建的 API 端点来自定义前端,并添加一个产品滑块来展示您商店中最畅销的产品。
与此同时,如果您有任何与 Medusa 相关的问题或疑问,请随时通过Discord联系 Medusa 团队。
文章来源:https://dev.to/medusajs/get-started-with-medusa-part-2-make-the-server-your-own-gka

