1. 簡介
上次更新時間:2020 年 10 月 30 日
在 Business Messages 中設定購物車!
這是系列中的第二個程式碼研究室,旨在建立「線上取貨」使用者歷程。在許多電子商務歷程中,購物車是成功將使用者轉換成付費客戶的關鍵。購物車也能讓您更瞭解顧客,並為他們提供其他可能感興趣的商品建議。在本程式碼研究室中,我們會專注於打造購物車體驗,以及將應用程式部署至 Google App Engine。
下列何者是良好的購物車內容?
購物車是打造成功線上購物體驗的關鍵。結果顯示,Business Messages 不僅可以協助潛在客戶回答產品的問題,還能協助在對話中完成付款,讓整個購物體驗更加順利。
良好的購物體驗除了能讓使用者依類別瀏覽商品,也能讓商家推薦買家可能感興趣的其他產品。將更多商品加入購物車後,使用者就能檢查整個購物車、移除商品或新增更多商品,然後再結帳。
建構項目
在程式碼研究室系列的這一節中,您將會擴充為虛構公司 Bonjour Meal 第 1 部分建構的數位代理程式,讓使用者瀏覽各項商品目錄並將商品加入購物車。
在本程式碼研究室中,您的應用程式將
- 在 Business Messages 中顯示問題目錄
- 建議使用者可能感興趣的項目
- 查看購物車內容並建立總價摘要
課程內容
- 如何在 Google Cloud Platform 上部署 App Engine 網頁應用程式
- 如何使用永久儲存機制儲存購物車狀態
本程式碼研究室著重於擴充數位代理程式,自本程式碼研究室系列第 1 單元開始。
軟硬體需求
- 已註冊,並獲準與 Business Messages 使用的 GCP 專案
- 前往開發人員網站瞭解相關操作說明
- 為 GCP 專案產生的服務帳戶 JSON 憑證檔案
- 搭載 Android 5 以上版本的裝置,或已安裝 Google 地圖應用程式的 iOS 裝置
- 具備網頁應用程式程式設計經驗
- 已連上網際網路!
2. 開始設定
本程式碼研究室假設您已建立第一個代理程式,並完成程式碼研究室的第 1 部分。因此,我們不會探討啟用 Business Messages 和 Business Communications API、建立服務帳戶金鑰、部署應用程式或在 Business Communications Console 上設定 Webhook 的基本概念。也就是說,我們會複製範例應用程式,確保您的應用程式與我們的建構產品一致,並在 Google Cloud Platform 上啟用 Datastore API,以持續保留購物車相關的資料。
從 GitHub 複製應用程式
在終端機中,使用下列指令將 Django Echo Bot Sample 複製到專案的工作目錄:
$ git clone https://github.com/google-business-communications/bm-bonjour-meal-django-starter-code
將為服務帳戶建立的 JSON 憑證檔案複製到範例的資源資料夾,並將憑證重新命名為「bm-agent-service-account-credentials.json」。
bm-bonjour-meal-django-starter-code/bonjourmeal-codelab/step-2/resources/bm-agent-service-account-credentials.json
啟用 Google Datastore API
在本程式碼研究室的第 1 部分,您啟用了 Business Messages API、Business 通訊 API 和 Cloud Build API。
在本程式碼研究室中,由於我們會使用 Google Datastore,因此必須啟用這個 API:
- 在 Google Cloud 控制台中開啟 Google Datastore API。
- 確認您使用的是正確的 GCP 專案。
- 按一下「啟用」。
部署範例應用程式
在終端機中,前往範例的步驟-2 目錄。
在終端機中執行下列指令來部署範例:
$ gcloud config set project PROJECT_ID*
$ gcloud app deploy
- PROJECT_ID 是您用來註冊 API 的專案 ID。
記下上一個指令輸出內容中的已部署應用程式的網址:
Deployed service [default] to [https://PROJECT_ID.appspot.com]
您剛剛部署的程式碼含有網頁應用程式,這個應用程式可透過 Webhook 接收來自 Business Messages 的訊息。其中包含我們在程式碼研究室第 1 部分提供的所有待辦事項。如果您尚未設定 Webhook,請先進行設定。
這個應用程式會回應一些簡單的問題,例如使用者詢問「Bonjour Meal」的營業時間。在行動裝置上,請透過 Business Communications 控制台的「服務專員資訊」擷取測試網址來進行測試。測試網址會在行動裝置上啟動 Business Messages 體驗,方便你開始與服務專員互動。
3. 產品目錄
庫存系統
在多數情況下,你可以透過內部 API 直接與品牌的廣告空間整合。在其他情況下,您可以抓取網頁或建立自己的庫存追蹤系統。我們的重點不是打造庫存系統;我們將使用一個簡單的靜態檔案來代表代理程式,其中包含圖片和產品資訊。在本節中,我們會從這個靜態檔案提取資訊、將資訊顯示在對話中,並讓使用者瀏覽可加入購物車的商品。
靜態商品目錄檔案如下所示:
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
}
]
}
我們現在就來取得 Python 應用程式,以便讀取這個檔案!
從目錄讀取資料
該目錄是一個名為「inventory.json」的靜態檔案可在 ./resources 目錄中找到。我們必須將一些 Python 邏輯新增至 view.py,才能讀取 JSON 檔案的內容,然後將檔案呈現在對話中。接著建立一個函式,從 JSON 檔案讀取資料,並傳回可用產品清單。
此函式定義可放在 view.py 內的任何位置。
bonjourmeal-codelab/step-2/bopis/views.py
...
def get_inventory_data():
f = open(INVENTORY_FILE)
inventory = json.load(f)
return inventory
...
這樣我們應該就能在讀取商品目錄資料時,看到所需的資訊。現在,我們需要將這項產品資訊顯示在對話中。
顯示產品目錄
為簡單起見,我們在本程式碼研究室中提供了一般產品目錄,讓您透過單一複合式資訊卡輪轉介面,將所有商品目錄項目顯示在 Business Messages 對話中。
為了查看產品目錄,我們會建立含有「顯示菜單」文字的建議回覆以及回傳資料「show-product-catalog
」當使用者輕觸建議回覆,且您的網頁應用程式收到回傳資料時,我們就會傳送複合式資訊卡輪轉介面。我們在 view.py 頂端的建議回覆新常數。
bonjourmeal-codelab/step-2/bopis/views.py
...
CMD_SHOW_PRODUCT_CATALOG = 'show-product-catalog'
...
接著,我們會剖析訊息並轉送到新功能,以傳送包含產品目錄的複合式資訊卡輪轉介面。請先擴充 route_message
函式以呼叫函式「send_product_catalog
」然後再輕觸建議回覆即可定義這個函式。
在以下程式碼片段中,為 route_message
函式中的 if 陳述式新增其他條件,檢查 normalized_message
是否等於我們先前定義的常數 CMD_SHOW_PRODUCT_CATALOG
。
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)
...
接著,請務必完成流程並定義 send_product_catalog
。send_product_catalog
會呼叫 get_menu_carousel,
,根據前述的商品目錄檔案產生複合式資訊卡輪轉介面。
函式定義可放在 view.py 內的任何位置,請注意,下列程式碼片段使用了兩個新常數 (應在檔案頂端加入)。
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)
...
如果您檢查輪轉介面項目的建立情形,我們也會建立 BusinessMessagesSuggestion
類別的執行個體。每個建議代表使用者在輪轉介面中選取的產品。使用者輕觸建議回覆時,Business Messages 就會將 PostbackData 傳送至 Webhook,其中含有 JSON 描述商品及使用者想採取的操作 (在購物車中新增或移除)。在下一節中,我們會剖析看起來像的訊息,以便實際將商品加入購物車。
完成這些變更之後,讓我們將網頁應用程式部署至 Google App Engine,嘗試體驗各種服務!
$ gcloud app deploy
當行動裝置載入對話介面後,請傳送「show-product-catalog」訊息就會看到看起來像這個產品的輪轉介面
如果您輕觸「Add item」(新增項目),除了代理程式會回應建議回覆中的回傳資料之外,其他不會有任何改變。在下一節中,我們會利用產品目錄建立購物車,並將商品加入購物車。
您剛建立的產品目錄可以透過多種方式擴充。您可能有不同的飲料菜單或素食選項。使用者可以透過輪轉介面或建議方塊,深入查看選單選項,進而找到所需產品。做為本程式碼研究室的延伸內容,您可以試著擴充產品目錄系統,讓使用者可以從菜單中分開查看飲品,甚至可以指定素食選項。
4. 購物車
在程式碼研究室的這一節中,我們會從上一節開始建構購物車功能,以便瀏覽可用的產品。
在眾多方面,重要的購物車體驗可讓使用者將商品加入購物車、將商品從購物車中移除、追蹤購物車中各商品的數量,以及查看購物車中的商品。
為了持續追蹤購物車狀態,我們需要在網頁應用程式保存資料。為簡化實驗和部署作業,我們將使用 Google Cloud Platform 中的 Google Datastore 保存資料。對話 ID 會在使用者和商家之間保持一致,因此我們可以利用這個 ID 將使用者與購物車商品建立關聯。
首先,讓我們連線至 Google Datastore,並在我們找到對話 ID 時,保留對話 ID。
與 Datastore 連線
只要您對購物車進行任何互動 (例如,使用者新增或刪除項目時),我們就會連線至 Google Datastore。如要進一步瞭解如何使用此用戶端程式庫與 Google Datastore 互動,請參閱官方說明文件。
以下程式碼片段定義用來更新購物車的函式。這個函式會採用下列輸入內容:conversation_id
和 message
。message
包含 JSON,用於說明使用者想採取的動作。該動作已內建於顯示產品目錄的輪轉介面中。此函式會建立 Google Datastore 用戶端,並立即擷取 ShoppingCart 實體,其中鍵為對話 ID。
將以下函式複製到 Views.py 檔案。我們將在後續章節中進一步介紹這項功能。
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)
我們要擴充這個函式,以便將商品加入購物車。
將商品加入購物車
使用者輕觸產品輪轉介面中的「新增項目」建議動作時,postbackData 會包含 JSON 格式,用來描述使用者想採取的動作。JSON 字典有兩個鍵「action」(動作)和「item_name」並將這個 JSON 字典傳送到您的 Webhook「item_name」欄位是與 inventory.json 中商品相關聯的專屬 ID。
取得購物車指令和剖析訊息中的購物車商品後,就可以撰寫條件陳述式來新增商品。這裡有幾個極端情況需要考量,例如 Datastore 從未看過對話 ID,或是購物車是第一次收到這個項目。以下是上述 update_shopping_cart
功能的擴充功能。這項變更會將 Google Datastore 所保存的商品加入購物車。
下列程式碼片段是先前新增至 view.py 的函式的延伸。您可以加入差異,或複製程式碼片段並取代現有版本的 update_shopping_cart
函式。
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)
日後會擴充此函式,以支援 cart_cmd
包含「del-item」字串的情況定義於 CMD_DEL_ITEM
。
同時構思
請務必在 route_message
函式中加入水電行,如果您收到訊息,將商品加入呼叫 update_shopping_cart
函式的購物車,此外,您也需要使用程式碼研究室中使用的慣例來定義新增項目的常數。
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)
...
目前,使用者可以將商品加入購物車。如果將變更部署至 Google App Engine,在 GCP 控制台的 Google Datastore 資訊主頁中,應該就會看見購物車的變更。請參閱 Google 資料儲存庫主控台的螢幕擷取畫面,其中有一個實體以 Conversation ID 命名,後面加上一些與庫存項目和購物車中商品數量的關係,
在下一節中,我們會建立在購物車中列出商品的方法。購物車審查機制應會顯示購物車中的所有商品、數量,以及將商品從購物車中移除的選項。
查看購物車中的商品
為了瞭解購物車狀態,並瞭解我們可以移除哪些商品,您只能在購物車中列出商品。
先傳送一則友善的訊息,例如「這是你的購物車:」,然後傳送另一則訊息,其中包含複合式資訊卡輪轉介面和「移除一個」的相關建議回覆或「新增一個」。複合式資訊卡輪轉介面也應另外列出購物車中儲存的商品數量。
在實際進入並編寫函式之前需要注意的事項:如果購物車中只有一種商品類型,我們就無法將其顯示為輪轉介面。複合式資訊卡輪轉介面必須包含至少兩張資訊卡。另一方面,如果購物車中沒有任何商品,我們希望顯示一則簡單的訊息,說明購物車沒有商品。
瞭解這一點後,請定義名為 send_shopping_cart
的函式。這個函式會連線至 Google Datastore,並根據對話 ID 要求 ShoppingCart 實體。完成後,我們會呼叫 get_inventory_data
函式,並使用複合式資訊卡輪轉介面回報購物車狀態。我們也需要依據名稱取得產品的 ID,並宣告要查詢 Google Datastore 的函式,藉此判斷該值。製作輪轉介面時,我們可以將建議的回覆與刪除項目建立關聯,或依產品 ID 新增項目。下列程式碼片段會執行這些作業。將程式碼複製到 view.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)
...
請確認您已在 view.py 頂端定義 CMD_SHOW_CART
,如果使用者傳送包含「show-cart」的訊息,請呼叫 send_shopping_cart
。
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)
...
根據我們在 send_shopping_cart
函式中引進的邏輯,當您輸入「show-cart」時,系統便會顯示訊息,指出購物車中沒有任何項目、顯示購物車中某個項目的複合式資訊卡,或是顯示多個項目的資訊卡輪轉介面。此外,我們還會提供三個建議回覆:「查看總價」、「清空購物車」以及「查看菜單」
請嘗試部署上述程式碼變更,以測試購物車是否追蹤您新增的商品,而您可以在對話顯示的對話畫面中查看購物車,如上方螢幕截圖所示。您可以在新增變更的 p 步驟 2 目錄中執行這個指令,藉此部署變更。
$ gcloud app deploy
「查看總價」功能。get_cart_price
函式的運作方式與「查看購物車」類似功能會交叉參照 Datastore 中的資料與 inventory.json 檔案,進而產生購物車的總價。在本程式碼研究室的後續部分整合付款功能方面,這將非常實用。
將商品從購物車中移除
最後,我們導入了移除購物車的功能,即可完成購物車行為。使用下列程式碼片段取代現有的 update_shopping_cart
函式。
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)
傳送確認訊息
使用者將商品加入購物車後,您就需要傳送確認訊息,確認他們的動作已處理完畢。這麼做不僅能激發期望,還能持續對話。
讓我們擴充 update_shopping_cart
函式,以便傳送訊息至對話 ID,說明項目已新增或移除,並提供建議顧客檢查購物車或再次查看菜單的建議。
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)
這樣應該可以!完整功能的購物車體驗,可讓使用者新增、移除商品,以及查看購物車中的商品。
現階段,如果您想在 Business Messages 對話中查看購物車功能,請部署應用程式並與服務專員互動。方法是在步驟 2 目錄中執行這個指令。
$ gcloud app deploy
5. 準備付款
為了與本系列文章的下一節整合的付款處理方整合,我們需要設法取得購物車的價格。我們要建立一個函式來擷取價格,做法是交叉參照 Google Datastore 中的購物車資料,從商品目錄中取得每項商品的價格,再乘以購物車中每個商品的數量。
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
...
最後,我們可以使用該函式並傳送訊息給使用者。
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)
...
為了將所有項目組合在一起,讓我們更新 route_message
函式和常數來觸發上述邏輯。
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)
...
以下提供部分螢幕截圖,展示上述邏輯達到的成果:
準備好與程式碼研究室的下一部分付款處理方整合時,我們會呼叫 get_cart_price
函式,將資料傳遞給付款處理方並啟動付款流程。
同樣地,您可以部署應用程式並與服務專員互動,在 Business Messages 對話中試用這項購物車功能。
$ gcloud app deploy
6. 恭喜
恭喜,您已成功在 Business Messages 中建立購物車服務。
在本程式碼研究室中,我們並未介紹的功能是將整個購物車清空。如果有需要,請嘗試擴充應用程式,以執行「將購物車清空」而不是每個特徵的分數您複製的原始碼步驟 3 提供解決方案。
在後續章節中,我們會整合外部付款處理方,讓使用者能完成您品牌的付款交易。
下列何者是良好的購物車內容?
確保良好的購物車體驗,與行動應用程式或實體商店大同小異。本程式碼研究室能探索的部分功能,僅包括新增商品、移除商品及計算購物車價格。與現實生活中的購物車不同的是,購物車中的所有商品價格會在您新增或移除時進行。這些高價值功能可讓您的對話式商務體驗脫穎而出!
後續步驟
準備就緒後,請參閱下列主題,進一步瞭解可在 Business Messages 中採取的更複雜的互動方式:
參考文件
- SuggestedReply
- Business Messages 訊息參考文件
- RichCard 的 JSON 定義