В первой части этой серии руководств я сравнил Медузу и Shopify, чтобы продемонстрировать, как Медуза является альтернативой Shopify с открытым исходным кодом. Там, где Shopify не хватает тарифных планов, минимальных возможностей настройки и неспособности адаптироваться для каждого случая использования в бизнесе, Medusa может это компенсировать.

Medusa — это решение для безголовой коммерции с открытым исходным кодом, которое позволяет вам владеть своим стеком и настраивать его для любого варианта использования, необходимого вашему бизнесу. Это быстро и очень гибко.

В предыдущем уроке вы узнали о трех компонентах Medusa и о том, как установить и запустить каждый из них. Это очень простой процесс, который может запустить ваш магазин за считанные секунды.

В этом руководстве вы начнете вносить изменения в сервер, чтобы сделать его своим. Вы узнаете, как создавать новые конечные точки API, сервисы и подписчиков. API, который вы создадите, будет получать продукты с наибольшим объемом продаж, а вы создадите сервис и подписчика, чтобы помочь нам в этом.

Код этого руководства находится в этом репозитории GitHub.

Предпосылки

В этом руководстве предполагается, что вы уже прочитали и выполнили часть 1. В первой части вы узнаете, как настроить магазин Medusa, в который вы внесете изменения в этом руководстве, а также витрину Medusa и администратора. Если вы еще не прошли его, пожалуйста, сделайте это, прежде чем продолжить этот урок.

Кроме того, вам необходимо установить и запустить 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;
  }
}

Вот несколько замечаний по поводу этой услуги:

  1. Когда эта служба извлекается в других местах вашего кода, служба должна упоминаться как версия имени файла в верблюжьем регистре, за которой следует «Сервис». В данном случае имя файла top-product, поэтому для доступа к нему в других местах мы используем topProductsService.
  2. Аналогично тому, как вы будете использовать этот сервис, мы внедряем в качестве зависимостей 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 может принимать 2 необязательных параметра. Первый указывает, где условия, а второй параметр указывает отношения для этих продуктов, которые нужно получить.

Затем вы сортируете массив с помощью метода sort Array, предоставляя ему функцию сравнения. В функции сравнения вы сравниваете количество продаж, хранящихся в поле metadata. В Medusa у большинства сущностей есть поле metadata, которое позволяет легко добавлять пользовательские атрибуты к сущностям по умолчанию для ваших целей. Здесь вы используете поле metadata для хранения количества продаж. Вы также сортируете товары по убыванию.

Наконец, вы используете метод массива splice для извлечения только первых 5 элементов.

Реализовать updateSales

Далее вы реализуете метод updateSales в файле TopProductsService. Этот метод получает идентификатор заказа в качестве параметра, затем извлекает этот заказ и перебирает заказанные товары в цикле. Затем свойство 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_ для получения заказа по его идентификатору. Метод retrieve принимает идентификатор заказа в качестве первого параметра и объект конфигурации в качестве второго параметра, который аналогичен тем, которые вы использовали в предыдущем методе. Вы передаете ему массив отношений, чтобы получить заказанные товары и их продукты.

Затем вы перебираете элементы и используете идентификатор продукта внутри каждого элемента для получения продукта. После этого вы увеличиваете количество продаж и обновляете продукт, используя метод 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. Этот маршрутизатор может содержать столько маршрутов, сколько вы хотите.

В этом коде вы добавляете новый маршрут GET в конечной точке /store/top-products. Причина, по которой вы используете здесь store в качестве префикса к top-products, заключается в том, что Medusa ставит перед всеми конечными точками витрины префикс /store, а перед всеми конечными точками администратора — /admin. Вам не нужно добавлять этот префикс, но хорошо следовать соглашениям API Medusa.

В этом маршруте вы получаете сервис, созданный в предыдущем разделе, с помощью этой строки:

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, объявленная в начале файла. Его значением является либо URL-адрес Redis, установленный в .env, либо URL-адрес Redis по умолчанию redis://localhost:6379. Если у вас другой URL-адрес Redis, добавьте новую переменную REDIS_URL в .env с URL-адресом.

Затем перезапустите сервер. Это примет обновленную конфигурацию и подключится к вашему серверу 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 с идентификатором объекта, с которым связано это событие. Таким образом, вы передаете этот идентификатор в метод updateSales на this.topProductsService_, чтобы обновить количество продаж для каждого из продуктов в заказе.

Проверьте это

Сейчас вы все проверите. Убедитесь, что сервер запущен. Если нет, запустите его с помощью следующей команды:

npm start

Затем перейдите к установке магазина Medusa и запустите:

npm run dev

Зайдите в магазин и сделайте заказ. Это вызовет TopProductsSubscriber, который обновит продажи продуктов в этом порядке.

Теперь отправьте запрос на /store/top-products, как вы делали это раньше. Вы должны увидеть, что sales внутри свойства metadata продуктов в этом порядке увеличилось.

Попробуйте добавить новый продукт из панели администратора или воспользуйтесь базой данных в репозитории GitHub этого туториала, в которой есть дополнительный продукт. Затем попробуйте сделать больше заказов с этим продуктом. Вы увидите, что сортировка в конечной точке изменилась в зависимости от количества продаж.

Заключение

В этом руководстве вы узнали, как добавить пользовательскую конечную точку API, службу и подписчика. Вы можете использовать эти 3 для реализации любой пользовательской функции или интеграции в свой магазин.

В следующем руководстве вы будете использовать конечную точку API, созданную в этой части, для настройки внешнего интерфейса и добавления слайдера продуктов, который демонстрирует самые продаваемые продукты в вашем магазине.

А пока, если у вас возникнут какие-либо проблемы или вопросы, связанные с Медузой, не стесняйтесь обращаться к команде Медузы через Discord.

Первоначально опубликовано на https://dev.to 8 ноября 2021 г.