Online-Abholung im Geschäft: Bonjour-Mahlzeit – Teil 2 – Einkaufswagen erstellen

1. Einführung

53003251caaf2be5.png 8826bd8cb0c0f1c7.png

Zuletzt aktualisiert: 30.10.2020

Warenkorb in Business Messages erstellen

Dies ist das zweite Codelab einer Reihe, in der es darum geht, eine User Journey für „Online kaufen, im Geschäft abholen“ zu erstellen. In vielen E-Commerce-Journeys ist ein Einkaufswagen entscheidend, um Nutzer in zahlende Kunden zu verwandeln. Über den Einkaufswagen können Sie Ihre Kunden besser kennenlernen und ihnen Vorschläge für andere Artikel machen, die sie interessieren könnten. In diesem Codelab konzentrieren wir uns auf die Entwicklung des Einkaufswagens und die Bereitstellung der Anwendung in Google App Engine.

Was macht einen guten Einkaufswagen aus?

Einkaufswagen sind der Schlüssel zu einem erfolgreichen Onlineshopping-Erlebnis. Business Messages eignet sich nicht nur für Fragen und Antworten zu einem Produkt mit einem potenziellen Kunden, sondern kann den gesamten Kaufprozess bis hin zur Zahlung innerhalb der Unterhaltung abwickeln.

9d17537b980d0e62.png

Ein guter Warenkorb ist wichtig, aber ein gutes Einkaufserlebnis umfasst auch die Möglichkeit, Artikel nach Kategorie zu durchsuchen und Empfehlungen für andere Produkte zu erhalten, die den Käufer interessieren könnten. Nachdem der Nutzer weitere Artikel in den Einkaufswagen gelegt hat, kann er sich den gesamten Einkaufswagen ansehen und Artikel entfernen oder hinzufügen, bevor er den Kaufvorgang abschließt.

Umfang

In diesem Abschnitt der Codelab-Reihe erweitern Sie den digitalen Kundenservicemitarbeiter, den Sie in Teil 1 für das fiktive Unternehmen Bonjour Meal erstellt haben. Nutzer sollen nun einen Artikelkatalog durchsuchen und Artikel in einen Einkaufswagen legen können.

In diesem Codelab wird Ihre App

  • Fragenkatalog in Business Messages anzeigen
  • Nutzern Artikel vorschlagen, die sie interessieren könnten
  • Inhalte des Einkaufswagens prüfen und eine Zusammenfassung des Gesamtpreises erstellen

ab2fb6a4ed33a129.png

Lerninhalte

  • Webanwendung in App Engine in der Google Cloud Platform bereitstellen
  • So verwenden Sie einen persistenten Speichermechanismus, um den Status eines Einkaufswagens zu speichern

In diesem Codelab geht es darum, den digitalen Agenten aus Teil 1 dieser Codelab-Reihe zu erweitern.

Voraussetzungen

2. Einrichtung

In diesem Codelab wird davon ausgegangen, dass Sie Ihren ersten Agenten erstellt und Teil 1 des Codelabs abgeschlossen haben. Daher werden wir die Grundlagen zum Aktivieren der Business Messages und Business Communications APIs, zum Erstellen von Dienstkontoschlüsseln, zum Bereitstellen einer Anwendung oder zum Einrichten Ihres Webhooks in der Business Communications Console nicht behandeln. Wir klonen eine Beispielanwendung, um sicherzustellen, dass Ihre Anwendung mit dem übereinstimmt, worauf wir aufbauen. Außerdem aktivieren wir die API für Datastore in der Google Cloud Platform, um Daten zum Einkaufswagen zu speichern.

Anwendung aus GitHub klonen

Klonen Sie in einem Terminal das Django Echo Bot Sample mit dem folgenden Befehl in das Arbeitsverzeichnis Ihres Projekts:

$ git clone https://github.com/google-business-communications/bm-bonjour-meal-django-starter-code

Kopieren Sie die JSON-Datei mit den Anmeldedaten, die für das Dienstkonto erstellt wurde, in den Ressourcenordner des Beispiels und benennen Sie die Anmeldedaten in „bm-agent-service-account-credentials.json“ um.

bm-bonjour-meal-django-starter-code/bonjourmeal-codelab/step-2/resources/bm-agent-service-account-credentials.json

Google Datastore API aktivieren

Im ersten Teil dieses Codelabs haben Sie die Business Messages API, die Business Communications API und die Cloud Build API aktiviert.

Da wir in diesem Codelab mit Google Datastore arbeiten, müssen wir auch diese API aktivieren:

  1. Öffnen Sie die Google Datastore API in der Google Cloud Console.
  2. Achten Sie darauf, dass Sie das richtige GCP-Projekt verwenden.
  3. Klicken Sie auf Aktivieren.

Beispielanwendung bereitstellen

Wechseln Sie in einem Terminal zum Verzeichnis „step-2“ des Beispiels.

Führen Sie die folgenden Befehle in einem Terminal aus, um das Beispiel bereitzustellen:

$ gcloud config set project PROJECT_ID*
$ gcloud app deploy
  • PROJECT_ID ist die Projekt-ID des Projekts, mit dem Sie sich für die APIs registriert haben.

Notieren Sie sich die URL der bereitgestellten Anwendung in der Ausgabe des letzten Befehls:

Deployed service [default] to [https://PROJECT_ID.appspot.com]

Der Code, den Sie gerade bereitgestellt haben, enthält eine Webanwendung mit einem Webhook zum Empfangen von Nachrichten von Business Messages. Es enthält alles, was wir in Teil 1 des Codelabs gemacht haben. Konfigurieren Sie Ihren Webhook, falls noch nicht geschehen.

Die Anwendung reagiert auf einige einfache Anfragen, z. B. wenn ein Nutzer nach den Öffnungszeiten von Bonjour Meal fragt. Sie sollten dies auf Ihrem Mobilgerät über die Test-URLs testen, die Sie in der Business Communications Console unter „Agent-Informationen“ abrufen können. Über die Test-URLs wird die Business Messages-Funktion auf Ihrem Mobilgerät gestartet und Sie können mit Ihrem Agenten interagieren.

3. Der Produktkatalog

Inventarsystem

In den meisten Fällen können Sie das Inventar einer Marke direkt über eine interne API einbinden. In anderen Fällen crawlen Sie möglicherweise eine Webseite oder erstellen ein eigenes Inventarverfolgungssystem. Wir möchten kein Inventarsystem erstellen, sondern verwenden eine einfache statische Datei, die Bilder und Produktinformationen für unseren Agenten enthält. In diesem Abschnitt rufen wir Informationen aus dieser statischen Datei ab, stellen sie im Gespräch dar und ermöglichen es einem Nutzer, die verfügbaren Artikel zu durchsuchen, die einem Einkaufswagen hinzugefügt werden können.

Die statische Inventardatei sieht so aus:

bonjourmeal-codelab/step-2/resources/inventory.json

{

    "food": [
        {
            "id":0,
            "name": "Ham and cheese sandwich",
            "price": "6.99",
            "image_url": "https://storage.googleapis.com/bonjour-rail.appspot.com/ham-and-cheese.png",
            "remaining": 8
        },
        {
            "id":1,
            "name": "Chicken veggie wrap",
            "price": "9.99",
            "image_url": "https://storage.googleapis.com/bonjour-rail.appspot.com/chicken-veggie-wrap.png",
            "remaining": 2
        },
        {
            "id":2,
            "name": "Assorted cheese plate",
            "price": "7.99",
            "image_url": "https://storage.googleapis.com/bonjour-rail.appspot.com/assorted-cheese-plate.png",
            "remaining": 6
        },
        {
            "id":3,
            "name": "Apple walnut salad",
            "price": "12.99",
            "image_url": "https://storage.googleapis.com/bonjour-rail.appspot.com/apple-walnut-salad.png",
            "remaining": 1
        }
    ]
}

Wir lassen die Python-Anwendung diese Datei lesen.

Aus unserem Inventar lesen

Das Inventar ist eine statische Datei namens „inventory.json“ im Verzeichnis „./resources“. Wir müssen der Datei „views.py“ etwas Python-Logik hinzufügen, um den Inhalt der JSON-Datei zu lesen und dann in der Unterhaltung anzuzeigen. Erstellen wir eine Funktion, die Daten aus der JSON-Datei liest und die Liste der verfügbaren Produkte zurückgibt.

Diese Funktionsdefinition kann an beliebiger Stelle in „views.py“ platziert werden.

bonjourmeal-codelab/step-2/bopis/views.py

...
def get_inventory_data():
        f = open(INVENTORY_FILE)
        inventory = json.load(f)
        return inventory
...

So erhalten wir die Informationen, die wir zum Lesen der Daten aus dem Inventar benötigen. Jetzt benötigen wir eine Möglichkeit, diese Produktinformationen in die Unterhaltung einzubinden.

Produktkatalog präsentieren

In diesem Codelab verwenden wir einen allgemeinen Produktkatalog, um alle Inventarelemente in der Business Messages-Unterhaltung über ein einzelnes Karussell mit Rich Cards zu präsentieren.

Um den Produktkatalog aufzurufen, erstellen wir eine vorgeschlagene Antwort mit dem Text „Show Menu“ (Menü anzeigen) und postbackData „show-product-catalog“. Wenn Nutzer auf die vorgeschlagene Antwort tippen und Ihre Webanwendung die Postback-Daten empfängt, senden wir das Rich-Card-Karussell. Fügen wir oben in views.py eine neue Konstante für diesen Antwortvorschlag hinzu.

bonjourmeal-codelab/step-2/bopis/views.py

...
CMD_SHOW_PRODUCT_CATALOG = 'show-product-catalog'
...

Hier wird die Nachricht geparst und an eine neue Funktion weitergeleitet, die ein Rich-Card-Karussell mit dem Produktkatalog sendet. Erweitern Sie zuerst die Funktion route_message, um die Funktion send_product_catalog aufzurufen, wenn auf die vorgeschlagene Antwort getippt wird. Anschließend definieren wir die Funktion.

Fügen Sie im folgenden Snippet der if-Anweisung in der Funktion route_message eine zusätzliche Bedingung hinzu, um zu prüfen, ob normalized_message der zuvor definierten Konstanten CMD_SHOW_PRODUCT_CATALOG entspricht.

bonjourmeal-codelab/step-2/bopis/views.py

...
def route_message(message, conversation_id):
    '''
    Routes the message received from the user to create a response.

    Args:
        message (str): The message text received from the user.
        conversation_id (str): The unique id for this user and agent.
    '''
    normalized_message = message.lower()

    if normalized_message == CMD_RICH_CARD:
        send_rich_card(conversation_id)
    elif normalized_message == CMD_CAROUSEL_CARD:
        send_carousel(conversation_id)
    elif normalized_message == CMD_SUGGESTIONS:
        send_message_with_suggestions(conversation_id)
    elif normalized_message == CMD_BUSINESS_HOURS_INQUIRY:
        send_message_with_business_hours(conversation_id)
    elif normalized_message == CMD_ONLINE_SHOPPING_INQUIRY:
        send_online_shopping_info_message(conversation_id)
    elif normalized_message == CMD_SHOW_PRODUCT_CATALOG:
        send_product_catalog(conversation_id)
    else:
        echo_message(message, conversation_id)
...

Wir müssen den Ablauf abschließen und send_product_catalog definieren. send_product_catalog ruft get_menu_carousel, auf, wodurch das Karussell mit Rich Cards aus der Inventardatei generiert wird, die wir zuvor eingelesen haben.

Die Funktionsdefinitionen können an beliebiger Stelle in „views.py“ platziert werden. Im folgenden Snippet werden zwei neue Konstanten verwendet, die oben in der Datei hinzugefügt werden sollten.

bonjourmeal-codelab/step-2/bopis/views.py

...

CMD_ADD_ITEM = 'add-item'
CMD_SHOW_CART = 'show-cart'

...

def get_menu_carousel():
    """Creates a sample carousel rich card.

    Returns:
       A :obj: A BusinessMessagesCarouselCard object with three cards.
    """

    inventory = get_inventory_data()

    card_content = []

    for item in inventory['food']:
        card_content.append(BusinessMessagesCardContent(
            title=item['name'],
            description=item['price'],
            suggestions=[
                BusinessMessagesSuggestion(
                    reply=BusinessMessagesSuggestedReply(
                        text='Add item',
                        postbackData='{'+f'"action":"{CMD_ADD_ITEM}","item_name":"{item["id"]}"'+'}'))

                ],
            media=BusinessMessagesMedia(
                height=BusinessMessagesMedia.HeightValueValuesEnum.MEDIUM,
                contentInfo=BusinessMessagesContentInfo(
                    fileUrl=item['image_url'],
                    forceRefresh=False))))

    return BusinessMessagesCarouselCard(
        cardContents=card_content,
        cardWidth=BusinessMessagesCarouselCard.CardWidthValueValuesEnum.MEDIUM)

def send_product_catalog(conversation_id):
    """Sends the product catalog to the conversation_id.

    Args:
        conversation_id (str): The unique id for this user and agent.
    """
    rich_card = BusinessMessagesRichCard(carouselCard=get_menu_carousel())

    fallback_text = ''

    # Construct a fallback text for devices that do not support carousels
    for card_content in rich_card.carouselCard.cardContents:
        fallback_text += (card_content.title + '\n\n' + card_content.description
                          + '\n\n' + card_content.media.contentInfo.fileUrl
                          + '\n---------------------------------------------\n\n')

    message_obj = BusinessMessagesMessage(
        messageId=str(uuid.uuid4().int),
        representative=BOT_REPRESENTATIVE,
        richCard=rich_card,
        fallback=fallback_text,
        suggestions=[
        BusinessMessagesSuggestion(
            reply=BusinessMessagesSuggestedReply(
                text='See my cart',
                postbackData=CMD_SHOW_CART)
            ),
        BusinessMessagesSuggestion(
            reply=BusinessMessagesSuggestedReply(
                text='See the menu',
                postbackData=CMD_SHOW_PRODUCT_CATALOG)
            ),
        ]
        )

    send_message(message_obj, conversation_id)
...

Wenn wir uns die Erstellung der Karussell-Elemente ansehen, erstellen wir auch eine Instanz der Klasse BusinessMessagesSuggestion. Jeder Vorschlag steht für die Auswahl eines Produkts im Karussell durch einen Nutzer. Wenn ein Nutzer auf die vorgeschlagene Antwort tippt, sendet Business Messages die postbackData, die JSON mit einer Beschreibung des Artikels und der gewünschten Nutzeraktion (dem Hinzufügen oder Entfernen aus dem Einkaufswagen) enthält, an Ihren Webhook. Im folgenden Abschnitt werden wir Nachrichten, die so aussehen, parsen, um den Artikel tatsächlich in den Einkaufswagen legen zu können.

Nachdem wir diese Änderungen vorgenommen haben, stellen wir die Webanwendung in Google App Engine bereit und probieren sie aus.

$ gcloud app deploy

Wenn Sie die unterhaltungsbasierte Oberfläche auf Ihrem Mobilgerät geladen haben, senden Sie die Nachricht „show-product-catalog“. Daraufhin sollte ein Produktkarussell wie dieses angezeigt werden.

4639da46bcc5230c.png

Wenn Sie auf Element hinzufügen tippen, passiert nichts, außer dass der Agent die Postback-Daten aus der vorgeschlagenen Antwort wiederholt. Im nächsten Abschnitt verwenden wir den Produktkatalog, um den Einkaufswagen zu erstellen, in den der Artikel aufgenommen wird.

Der gerade erstellte Produktkatalog kann auf verschiedene Arten erweitert werden. Möglicherweise haben Sie andere Getränkekartenoptionen oder vegetarische Optionen. Mit Karussells oder Vorschlagschips können Nutzer Menüoptionen durchgehen, um zu einer Reihe von Produkten zu gelangen, nach denen sie suchen. Als Erweiterung dieses Codelabs können Sie das Produktkatalogsystem so erweitern, dass ein Nutzer Getränke im Menü separat von Speisen ansehen oder sogar vegetarische Optionen angeben kann.

4. Der Einkaufswagen

In diesem Abschnitt des Codelabs bauen wir die Warenkorbfunktion auf, die auf dem vorherigen Abschnitt basiert, in dem wir die verfügbaren Produkte durchsuchen können.

Die wichtigsten Funktionen des Einkaufswagens sind, dass Nutzer Artikel in den Einkaufswagen legen, Artikel aus dem Einkaufswagen entfernen, die Anzahl der einzelnen Artikel im Einkaufswagen im Blick behalten und Artikel im Einkaufswagen überprüfen können.

Um den Status des Einkaufswagens im Blick zu behalten, müssen wir Daten in der Webanwendung speichern. Um das Testen und Bereitstellen zu vereinfachen, verwenden wir Google Datastore in der Google Cloud Platform, um Daten zu speichern. Die Unterhaltungs-ID bleibt zwischen einem Nutzer und dem Unternehmen konstant. So können wir Nutzer mit Einkaufswagenartikeln verknüpfen.

Beginnen wir damit, eine Verbindung zu Google Datastore herzustellen und die Unterhaltungs-ID zu speichern, wenn wir sie sehen.

Verbindung zu Datastore herstellen

Wir stellen eine Verbindung zu Google Datastore her, wenn eine Interaktion mit dem Einkaufswagen ausgeführt wird, z. B. wenn ein Nutzer einen Artikel hinzufügt oder löscht. Weitere Informationen zur Verwendung dieser Clientbibliothek für die Interaktion mit Google Datastore finden Sie in der offiziellen Dokumentation.

Im folgenden Snippet wird eine Funktion zum Aktualisieren des Einkaufswagens definiert. Die Funktion akzeptiert die folgenden Eingaben: conversation_id und message. message enthält JSON-Code, der die Aktion beschreibt, die der Nutzer ausführen möchte. Dieser ist bereits in Ihr Karussell mit dem Produktkatalog integriert. Die Funktion erstellt einen Google Datastore-Client und ruft sofort eine ShoppingCart-Entität ab, deren Schlüssel die Konversations-ID ist.

Kopieren Sie die folgende Funktion in Ihre Datei „views.py“. Im nächsten Abschnitt werden wir noch genauer darauf eingehen.

bonjourmeal-codelab/step-2/bopis/views.py

from google.oauth2 import service_account
from google.cloud import datastore

def update_shopping_cart(conversation_id, message):
        credentials = service_account.Credentials.from_service_account_file(
        SERVICE_ACCOUNT_LOCATION)

        client = datastore.Client(credentials=credentials)
        key = client.key('ShoppingCart', conversation_id)
        entity = datastore.Entity(key=key)
        result = client.get(key)
        
        # TODO: Add logic to add and remove items from cart
        
        entity.update(result)
        client.put(entity)

Wir erweitern diese Funktion, um einen Artikel in den Einkaufswagen zu legen.

Artikel in den Einkaufswagen legen

Wenn der Nutzer im Produktkarussell auf die vorgeschlagene Aktion Artikel hinzufügen tippt, enthält „postbackData“ JSON-Code, der die gewünschte Aktion des Nutzers beschreibt. Das JSON-Wörterbuch hat zwei Schlüssel: „action“ und „item_name“. Dieses JSON-Wörterbuch wird an Ihren Webhook gesendet. Das Feld „item_name“ ist die eindeutige Kennung, die dem Artikel in der Datei „inventory.json“ zugewiesen ist.

Sobald wir den Warenkorbbefehl und den Warenkorbartikel aus der Nachricht geparst haben, können wir bedingte Anweisungen schreiben, um den Artikel hinzuzufügen. Einige Grenzfälle, die Sie hier berücksichtigen sollten, sind, wenn die Konversations-ID noch nie im Datenspeicher vorhanden war oder wenn der Einkaufswagen diesen Artikel zum ersten Mal erhält. Das Folgende ist eine Erweiterung der oben definierten update_shopping_cart-Funktionalität. Durch diese Änderung wird dem Einkaufswagen ein Artikel hinzugefügt, der von Google Datastore gespeichert wird.

Das folgende Snippet ist eine Erweiterung der vorherigen Funktion, die Sie Ihrer Datei „views.py“ hinzugefügt haben. Sie können die Differenz hinzufügen oder das Snippet kopieren und die vorhandene Version der update_shopping_cart-Funktion ersetzen.

bonjourmeal-codelab/step-2bopis/views.py

def update_shopping_cart(conversation_id, message):
    credentials = service_account.Credentials.from_service_account_file(
      SERVICE_ACCOUNT_LOCATION)
    inventory = get_inventory_data()

    cart_request = json.loads(message)
    cart_cmd = cart_request["action"]
    cart_item = cart_request["item_name"]

    item_name = inventory['food'][int(cart_item)]['name']

    client = datastore.Client(credentials=credentials)
    key = client.key('ShoppingCart', conversation_id)
    entity = datastore.Entity(key=key)
    result = client.get(key)

    if result is None:
        if cart_cmd == CMD_ADD_ITEM:
            entity.update({
                item_name: 1
            })

    else:
        if cart_cmd == CMD_ADD_ITEM:
            if result.get(item_name) is None:
                result[item_name] = 1
            else:
                result[item_name] = result[item_name] + 1

        entity.update(result)
    client.put(entity)

Diese Funktion wird später erweitert, um das Szenario zu unterstützen, in dem cart_cmd den in CMD_DEL_ITEM definierten String „del-item“ enthält.

Zusammenfassung

Achten Sie darauf, dass Sie die erforderlichen Schritte in der Funktion route_message hinzufügen, damit die Funktion update_shopping_cart aufgerufen wird, wenn Sie eine Nachricht zum Hinzufügen eines Artikels zum Einkaufswagen erhalten. Außerdem müssen Sie eine Konstante zum Hinzufügen von Elementen definieren. Verwenden Sie dazu die Konvention, die wir im gesamten Codelab verwenden.

bonjourmeal-codelab/step-2bopis/views.py

...

CMD_DEL_ITEM = 'del-item'

...

def route_message(message, conversation_id):
    '''
    Routes the message received from the user to create a response.

    Args:
        message (str): The message text received from the user.
        conversation_id (str): The unique id for this user and agent.
    '''
    normalized_message = message.lower()

    if normalized_message == CMD_RICH_CARD:
        send_rich_card(conversation_id)
    elif normalized_message == CMD_CAROUSEL_CARD:
        send_carousel(conversation_id)
    elif normalized_message == CMD_SUGGESTIONS:
        send_message_with_suggestions(conversation_id)
    elif normalized_message == CMD_BUSINESS_HOURS_INQUIRY:
        send_message_with_business_hours(conversation_id)
    elif normalized_message == CMD_ONLINE_SHOPPING_INQUIRY:
        send_online_shopping_info_message(conversation_id)
    elif normalized_message == CMD_SHOW_PRODUCT_CATEGORY:
        send_product_catalog(conversation_id)
    elif CMD_ADD_ITEM in normalized_message or CMD_DEL_ITEM in normalized_message:
       update_shopping_cart(conversation_id, message)
    else:
        echo_message(message, conversation_id)

...

Derzeit können wir Artikel in den Einkaufswagen legen. Wenn Sie Ihre Änderungen in Google App Engine bereitstellen, sollten die Änderungen am Warenkorb im Google Datastore-Dashboard in der GCP Console zu sehen sein. Im Screenshot unten sehen Sie die Google Datastore-Konsole. Es ist eine einzelne Entität zu sehen, die nach der Konversations-ID benannt ist, gefolgt von einigen Beziehungen zu Inventarartikeln und der Menge dieser Artikel im Einkaufswagen.

619dc18a8136ea69.png

Im nächsten Abschnitt erstellen wir eine Möglichkeit, Artikel im Einkaufswagen aufzulisten. Der Mechanismus zur Überprüfung des Einkaufswagens sollte alle Artikel im Einkaufswagen, die Menge dieser Artikel und eine Option zum Entfernen eines Artikels aus dem Einkaufswagen anzeigen.

Artikel im Einkaufswagen prüfen

Nur wenn wir die Artikel im Einkaufswagen auflisten, können wir den Status des Einkaufswagens nachvollziehen und wissen, welche Artikel wir entfernen können.

Senden wir zuerst eine freundliche Nachricht wie „Hier ist dein Einkaufswagen:“, gefolgt von einer weiteren Nachricht mit einem Rich Card-Karussell mit zugehörigen Vorschlägen für Antworten wie „Einen Artikel entfernen“ oder „Einen Artikel hinzufügen“. Im Rich Card-Karussell sollte zusätzlich die Anzahl der im Einkaufswagen gespeicherten Artikel aufgeführt werden.

Bevor wir die Funktion schreiben, sollten wir Folgendes beachten: Wenn sich nur ein Artikeltyp im Einkaufswagen befindet, kann er nicht als Karussell gerendert werden. Karussells mit Rich Cards müssen mindestens zwei Karten enthalten. Wenn sich keine Artikel im Einkaufswagen befinden, soll eine einfache Meldung angezeigt werden, dass der Einkaufswagen leer ist.

Definieren wir also eine Funktion namens send_shopping_cart. Diese Funktion stellt eine Verbindung zu Google Datastore her und fordert eine ShoppingCart-Entität basierend auf der Konversations-ID an. Sobald wir diese Informationen haben, rufen wir die Funktion get_inventory_data auf und verwenden ein Karussell mit Rich Cards, um den Status des Einkaufswagens zu melden. Wir müssen auch die ID eines Produkts anhand des Namens abrufen. Dazu können wir eine Funktion deklarieren, die im Google-Datenspeicher nach diesem Wert sucht. Während das Karussell erstellt wird, können wir vorgeschlagene Antworten zum Löschen oder Hinzufügen von Elementen anhand der Produkt-ID zuordnen. Das folgende Snippet führt alle diese Vorgänge aus. Kopieren Sie den Code an eine beliebige Stelle in „views.py“.

bonjourmeal-codelab/step-2/bopis/views.py

...
def get_id_by_product_name(product_name):
  inventory = get_inventory_data()
  for item in inventory['food']:
    if item['name'] == product_name:
      return int(item['id'])
  return False


def send_shopping_cart(conversation_id):
  credentials = service_account.Credentials.from_service_account_file(
      SERVICE_ACCOUNT_LOCATION)

  # Retrieve the inventory data
  inventory = get_inventory_data()

  # Pull the data from Google Datastore
  client = datastore.Client(credentials=credentials)
  key = client.key('ShoppingCart', conversation_id)
  result = client.get(key)

  shopping_cart_suggestions = [
      BusinessMessagesSuggestion(
          reply=BusinessMessagesSuggestedReply(
              text='See total price', postbackData='show-cart-price')),
      BusinessMessagesSuggestion(
          reply=BusinessMessagesSuggestedReply(
              text='Empty the cart', postbackData='empty-cart')),
      BusinessMessagesSuggestion(
          reply=BusinessMessagesSuggestedReply(
              text='See the menu', postbackData=CMD_SHOW_PRODUCT_CATALOG)),
  ]

  if result is None or len(result.items()) == 0:
    message_obj = BusinessMessagesMessage(
        messageId=str(uuid.uuid4().int),
        representative=BOT_REPRESENTATIVE,
        text='There are no items in your shopping cart.',
        suggestions=shopping_cart_suggestions)

    send_message(message_obj, conversation_id)
  elif len(result.items()) == 1:

    for product_name, quantity in result.items():
      product_id = get_id_by_product_name(product_name)

      fallback_text = ('You have one type of item in the shopping cart')

      rich_card = BusinessMessagesRichCard(
          standaloneCard=BusinessMessagesStandaloneCard(
              cardContent=BusinessMessagesCardContent(
                  title=product_name,
                  description=f'{quantity} in cart.',
                  suggestions=[
                      BusinessMessagesSuggestion(
                          reply=BusinessMessagesSuggestedReply(
                              text='Remove one',
                              postbackData='{'+f'"action":"{CMD_DEL_ITEM}","item_name":"{product_id}"'+'}'))
                  ],
                  media=BusinessMessagesMedia(
                      height=BusinessMessagesMedia.HeightValueValuesEnum.MEDIUM,
                      contentInfo=BusinessMessagesContentInfo(
                          fileUrl=inventory['food'][product_id]
                          ['image_url'],
                          forceRefresh=False)))))

      message_obj = BusinessMessagesMessage(
          messageId=str(uuid.uuid4().int),
          representative=BOT_REPRESENTATIVE,
          richCard=rich_card,
          suggestions=shopping_cart_suggestions,
          fallback=fallback_text)

      send_message(message_obj, conversation_id)
  else:
    cart_carousel_items = []

    # Iterate through the cart and generate a carousel of items
    for product_name, quantity in result.items():
      product_id = get_id_by_product_name(product_name)

      cart_carousel_items.append(
          BusinessMessagesCardContent(
              title=product_name,
              description=f'{quantity} in cart.',
              suggestions=[
                  BusinessMessagesSuggestion(
                      reply=BusinessMessagesSuggestedReply(
                          text='Remove one',
                          postbackData='{'+f'"action":"{CMD_DEL_ITEM}","item_name":"{product_id}"'+'}'))
              ],
              media=BusinessMessagesMedia(
                  height=BusinessMessagesMedia.HeightValueValuesEnum.MEDIUM,
                  contentInfo=BusinessMessagesContentInfo(
                      fileUrl=inventory['food'][product_id]
                      ['image_url'],
                      forceRefresh=False))))

    rich_card = BusinessMessagesRichCard(
        carouselCard=BusinessMessagesCarouselCard(
            cardContents=cart_carousel_items,
            cardWidth=BusinessMessagesCarouselCard.CardWidthValueValuesEnum
            .MEDIUM))

    fallback_text = ''

    # Construct a fallback text for devices that do not support carousels
    for card_content in rich_card.carouselCard.cardContents:
      fallback_text += (
          card_content.title + '\n\n' + card_content.description + '\n\n' +
          card_content.media.contentInfo.fileUrl +
          '\n---------------------------------------------\n\n')

    message_obj = BusinessMessagesMessage(
        messageId=str(uuid.uuid4().int),
        representative=BOT_REPRESENTATIVE,
        richCard=rich_card,
        suggestions=shopping_cart_suggestions,
        fallback=fallback_text,
    )

    send_message(message_obj, conversation_id)

...

Achten Sie darauf, dass Sie CMD_SHOW_CART bereits oben in views.py definiert haben, und rufen Sie send_shopping_cart auf, wenn der Nutzer eine Nachricht mit „show-cart“ sendet.

bonjourmeal-codelab/step-2/bopis/views.py

...
def route_message(message, conversation_id):
    '''
    Routes the message received from the user to create a response.

    Args:
        message (str): The message text received from the user.
        conversation_id (str): The unique id for this user and agent.
    '''
    normalized_message = message.lower()

    if normalized_message == CMD_RICH_CARD:
        send_rich_card(conversation_id)
    elif normalized_message == CMD_CAROUSEL_CARD:
        send_carousel(conversation_id)
    elif normalized_message == CMD_SUGGESTIONS:
        send_message_with_suggestions(conversation_id)
    elif normalized_message == CMD_BUSINESS_HOURS_INQUIRY:
        send_message_with_business_hours(conversation_id)
    elif normalized_message == CMD_ONLINE_SHOPPING_INQUIRY:
        send_online_shopping_info_message(conversation_id)
    elif normalized_message == CMD_SHOW_PRODUCT_CATEGORY:
        send_product_catalog(conversation_id)
    elif CMD_ADD_ITEM in normalized_message or CMD_DEL_ITEM in normalized_message:
        update_shopping_cart(conversation_id, message)
    elif normalized_message == CMD_SHOW_CART:
        send_shopping_cart(conversation_id)
    else:
        echo_message(message, conversation_id)
...

34801776a97056ac.png

Gemäß der Logik, die wir in der send_shopping_cart-Funktion eingeführt haben, erhalten wir, wenn Sie „show-cart“ eingeben, entweder eine Meldung, dass sich nichts im Warenkorb befindet, eine Rich Card mit dem einen Artikel im Warenkorb oder ein Karussell mit Karten mit mehreren Artikeln. Außerdem haben wir drei Antwortvorschläge: „Gesamtpreis ansehen“, „Warenkorb leeren“ und „Menü ansehen“.

Stellen Sie die oben genannten Codeänderungen bereit, um zu testen, ob in Ihrem Einkaufswagen Artikel erfasst werden, die Sie hinzufügen, und ob Sie den Einkaufswagen über die Konversationsoberfläche aufrufen können, wie in den Screenshots oben gezeigt. Sie können die Änderungen mit diesem Befehl bereitstellen, der im Verzeichnis „step-2“ ausgeführt wird, in dem Sie die Änderungen vornehmen.

$ gcloud app deploy

Die Funktion „Gesamtpreis ansehen“ wird im nächsten Abschnitt erstellt, nachdem die Funktion zum Entfernen eines Artikels aus dem Einkaufswagen implementiert wurde. Die Funktion get_cart_price verhält sich ähnlich wie die Funktion „Warenkorb ansehen“, da sie Daten in Datastore mit der Datei „inventory.json“ abgleicht, um einen Gesamtpreis für den Warenkorb zu ermitteln. Das ist für den nächsten Teil des Codelabs nützlich, in dem wir die Zahlungsfunktion einbinden.

Artikel aus dem Einkaufswagen entfernen

Schließlich können wir das Verhalten des Einkaufswagens vervollständigen, indem wir eine Funktion zum Entfernen des Einkaufswagens einführen. Ersetzen Sie die vorhandene update_shopping_cart-Funktion durch das folgende Snippet.

bonjourmeal-codelab/step-2/ bopis/views.py

def update_shopping_cart(conversation_id, message):
    credentials = service_account.Credentials.from_service_account_file(
      SERVICE_ACCOUNT_LOCATION)
    inventory = get_inventory_data()

    cart_request = json.loads(message)
    cart_cmd = cart_request["action"]
    cart_item = cart_request["item_name"]

    item_name = inventory['food'][int(cart_item)]['name']


    client = datastore.Client(credentials=credentials)
    key = client.key('ShoppingCart', conversation_id)
    entity = datastore.Entity(key=key)
    result = client.get(key)

    if result is None:
        if cart_cmd == CMD_ADD_ITEM:
            entity.update({
                item_name: 1
            })
        elif cart_cmd == CMD_DEL_ITEM:
            # The user is trying to delete an item from an empty cart. Pass and skip
            pass

    else:
        if cart_cmd == CMD_ADD_ITEM:
            if result.get(item_name) is None:
                result[item_name] = 1
            else:
                result[item_name] = result[item_name] + 1

        elif cart_cmd == CMD_DEL_ITEM:
            if result.get(item_name) is None:
                # The user is trying to remove an item that's no in the shopping cart. Pass and skip
                pass
            elif result[item_name] - 1 > 0:
                result[item_name] = result[item_name] - 1
            else:
                del result[item_name]

        entity.update(result)
    client.put(entity)

Bestätigungsnachricht senden

Wenn der Nutzer einen Artikel in den Warenkorb legt, sollten Sie eine Bestätigungsnachricht senden, in der Sie seine Aktion bestätigen und angeben, dass Sie seine Anfrage bearbeitet haben. So werden nicht nur Erwartungen geweckt, sondern auch die Unterhaltung am Laufen gehalten.

Wir erweitern die Funktion update_shopping_cart so, dass eine Nachricht mit der Konversations-ID gesendet wird, in der angegeben wird, dass der Artikel hinzugefügt oder entfernt wurde. Außerdem werden Vorschläge gemacht, den Einkaufswagen zu überprüfen oder das Menü noch einmal aufzurufen.

bonjourmeal-codelab/step-2/bopis/views.py

def update_shopping_cart(conversation_id, message):

     # No changes to the function, except appending the following logic
     ...
   
    if cart_cmd == CMD_ADD_ITEM:
        message = 'Great! You\'ve added an item to the cart.'
    else:
        message = 'You\'ve removed an item from the cart.'

    message_obj = BusinessMessagesMessage(
        messageId=str(uuid.uuid4().int),
        representative=BOT_REPRESENTATIVE,
        text=message,
        suggestions=[
            BusinessMessagesSuggestion(
            reply=BusinessMessagesSuggestedReply(
                text='Review shopping cart',
                postbackData=CMD_SHOW_CART)
            ),
            BusinessMessagesSuggestion(
            reply=BusinessMessagesSuggestedReply(
                text='See menu again',
                postbackData=CMD_SHOW_PRODUCT_CATALOG)
            ),
            ])
    send_message(message_obj, conversation_id)

905a1f3d89893ba0.png

Das sollte es gewesen sein. Ein voll funktionsfähiger Einkaufswagen, in dem Nutzer Artikel hinzufügen, entfernen und überprüfen können.

Wenn Sie die Einkaufswagenfunktion in der Business Messages-Unterhaltung sehen möchten, stellen Sie die Anwendung bereit, um mit Ihrem Kundenservice-Agent zu interagieren. Führen Sie dazu diesen Befehl im Verzeichnis „step-2“ aus.

$ gcloud app deploy

5. Vorbereitung auf Zahlungen

Zur Vorbereitung auf die Integration mit einem Zahlungsabwickler im nächsten Teil der Reihe benötigen wir eine Möglichkeit, den Preis des Einkaufswagens abzurufen. Wir erstellen eine Funktion, die den Preis abruft, indem sie die Warenkorbdaten in Google Datastore abgleicht, den Preis jedes Artikels aus dem Inventar abruft und den Preis mit der Menge jedes Artikels im Warenkorb multipliziert.

bonjourmeal-codelab/step-2/bopis/views.py

...
def get_cart_price(conversation_id):
    # Pull the data from Google Datastore
    credentials = service_account.Credentials.from_service_account_file(
    SERVICE_ACCOUNT_LOCATION)
    client = datastore.Client(credentials=credentials)
    key = client.key('ShoppingCart', conversation_id)
    entity = datastore.Entity(key=key)
    result = client.get(key)

    # Retrieve the inventory data
    inventory = get_inventory_data()
   
    # Start off with a total of 0 before adding up the total
    total_price = 0

    if len(result.items()) != 0:
      for product_name, quantity in result.items():
        total_price = total_price + float(
            inventory['food'][get_id_by_product_name(product_name)]['price']) * int(quantity)

    return total_price

...

Schließlich können wir diese Funktion nutzen und eine Nachricht an den Nutzer senden.

bonjourmeal-codelab/step-2/bopis/views.py

...

def send_shopping_cart_total_price(conversation_id):
    cart_price = get_cart_price(conversation_id)

    message_obj = BusinessMessagesMessage(
        messageId=str(uuid.uuid4().int),
        representative=BOT_REPRESENTATIVE,
        suggestions=[],
        text=f'Your cart\'s total price is ${cart_price}.')

    send_message(message_obj, conversation_id)
...

Um alles zusammenzufassen, aktualisieren wir die Funktion route_message und die Konstante, um die obige Logik auszulösen.

bonjourmeal-codelab/step-2/bopis/views.py

...
CMD_GET_CART_PRICE = 'show-cart-price'
...
def route_message(message, conversation_id):
    '''
    Routes the message received from the user to create a response.

    Args:
        message (str): The message text received from the user.
        conversation_id (str): The unique id for this user and agent.
    '''
    normalized_message = message.lower()

    if normalized_message == CMD_RICH_CARD:
        send_rich_card(conversation_id)
    elif normalized_message == CMD_CAROUSEL_CARD:
        send_carousel(conversation_id)
    elif normalized_message == CMD_SUGGESTIONS:
        send_message_with_suggestions(conversation_id)
    elif normalized_message == CMD_BUSINESS_HOURS_INQUIRY:
        send_message_with_business_hours(conversation_id)
    elif normalized_message == CMD_ONLINE_SHOPPING_INQUIRY:
        send_online_shopping_info_message(conversation_id)
    elif normalized_message == CMD_SHOW_PRODUCT_CATEGORY:
        send_product_catalog(conversation_id)
    elif CMD_ADD_ITEM in normalized_message or CMD_DEL_ITEM in normalized_message:
        update_shopping_cart(conversation_id, message)
    elif normalized_message == CMD_SHOW_CART:
        send_shopping_cart(conversation_id)
    elif normalized_message == CMD_GET_CART_PRICE:
        send_shopping_cart_total_price(conversation_id)
    else:
        echo_message(message, conversation_id)
...

Hier sind einige Screenshots, die zeigen, was mit der oben genannten Logik erreicht wird:

8feacf94ed0ac6c4.png

Wenn wir im nächsten Teil des Codelabs bereit sind, die Integration mit dem Zahlungsabwickler vorzunehmen, rufen wir die Funktion get_cart_price auf, um die Daten an den Zahlungsabwickler zu übergeben und den Zahlungsablauf zu starten.

Sie können diese Warenkorbfunktion noch einmal in der Business Messages-Unterhaltung testen, indem Sie die Anwendung bereitstellen und mit Ihrem Agent interagieren.

$ gcloud app deploy

6. Glückwunsch

Glückwunsch! Sie haben erfolgreich eine Warenkorbfunktion in Business Messages erstellt.

Eine Funktion, die wir in diesem Codelab nicht behandelt haben, ist das Leeren des gesamten Einkaufswagens. Wenn Sie möchten, können Sie die Anwendung so erweitern, dass sie die Funktion „Warenkorb leeren“ unterstützt. Die Lösung ist in Schritt 3 des geclonten Quellcodes verfügbar.

In einem späteren Abschnitt werden wir einen externen Zahlungsabwickler einbinden, damit Ihre Nutzer eine Zahlungstransaktion mit Ihrer Marke abschließen können.

Was macht einen guten Einkaufswagen aus?

Ein guter Einkaufswagen in einem Gespräch unterscheidet sich nicht von einem in einer mobilen App oder in einem physischen Geschäft. Artikel hinzufügen, Artikel entfernen und den Preis des Einkaufswagens berechnen – das sind nur einige der Funktionen, die wir in diesem Codelab untersucht haben. Ein Unterschied zum Einkaufswagen in der realen Welt besteht darin, dass Sie den Preis aller Artikel im Einkaufswagen jederzeit sehen können, wenn Sie Artikel hinzufügen oder entfernen. Mit diesen hochwertigen Funktionen können Sie Ihr dialogorientiertes Kauferlebnis von der Konkurrenz abheben.

Nächste Schritte

Wenn Sie bereit sind, können Sie sich die folgenden Themen ansehen, um mehr über komplexere Interaktionen zu erfahren, die Sie in Business Messages ermöglichen können:

Referenzdokumente