خرید آنلاین پیکاپ در فروشگاه: غذای Bonjour - قسمت 2 - ساخت سبد خرید

1. معرفی

53003251caaf2be5.png8826bd8cb0c0f1c7.png

آخرین به روز رسانی: 2020-10-30

ساخت سبد خرید در پیام های تجاری!

این دومین آزمایشگاه کد از مجموعه‌ای است که هدف آن ایجاد سفر کاربر خرید آنلاین پیکاپ در فروشگاه است. در بسیاری از سفرهای تجارت الکترونیک، سبد خرید کلید موفقیت تبدیل کاربران به مشتریان پولی است. سبد خرید همچنین راهی برای درک بهتر مشتریان شما و راهی برای ارائه پیشنهادات در مورد موارد دیگری است که ممکن است به آنها علاقه مند باشند. در این کد لبه، ما بر ایجاد تجربه سبد خرید و استقرار برنامه در Google App Engine تمرکز خواهیم کرد. .

یک سبد خرید خوب چیست؟

سبد خرید کلید یک تجربه خرید آنلاین موفق است. همانطور که مشخص است، Business Messages نه تنها در تسهیل پرسش و پاسخ در مورد یک محصول با یک مشتری بالقوه خوب است، بلکه می تواند کل تجربه خرید را تا تکمیل پرداخت در مکالمه تسهیل کند.

9d17537b980d0e62.png

فراتر از یک سبد خرید خوب، یک تجربه خرید خوب به کاربران امکان می دهد اقلام را بر اساس دسته بندی مرور کنند و به کسب و کار اجازه می دهد محصولات دیگری را که ممکن است خریدار به آنها علاقه مند باشد توصیه کند. پس از افزودن موارد بیشتر به سبد خرید، کاربر می تواند کل سبد خرید خود را بررسی کند و قادر به حذف موارد یا اضافه کردن موارد بیشتر قبل از بررسی است.

چیزی که خواهی ساخت

در این بخش از سری Codelab، می‌خواهید عامل دیجیتالی را که در قسمت 1 ساخته‌اید، برای شرکت ساختگی Bonjour Meal گسترش دهید تا کاربران بتوانند فهرستی از اقلام را مرور کنند و اقلامی را به سبد خرید اضافه کنند.

در این لبه کد، برنامه شما این کار را انجام می دهد

  • نمایش کاتالوگی از سوالات در Business Messages
  • مواردی را پیشنهاد دهید که کاربران ممکن است به آنها علاقه مند باشند
  • محتویات سبد خرید را مرور کنید و خلاصه قیمت کل را ایجاد کنید

ab2fb6a4ed33a129.png

چیزی که یاد خواهید گرفت

  • نحوه استقرار یک برنامه وب در App Engine در Google Cloud Platform
  • نحوه استفاده از مکانیزم ذخیره سازی مداوم برای حفظ وضعیت سبد خرید

این Codelab بر گسترش عامل دیجیتال از قسمت 1 این سری Codelab متمرکز شده است.

آنچه شما نیاز دارید

2. راه اندازی

این کد لبه فرض می‌کند که شما اولین عامل خود را ایجاد کرده‌اید و قسمت 1 را تکمیل کرده‌اید. به این ترتیب، ما اصول اولیه فعال کردن پیام‌های تجاری و APIهای ارتباطات تجاری، ایجاد کلیدهای حساب سرویس، استقرار یک برنامه کاربردی، یا راه‌اندازی وبک‌هوک خود را در کنسول ارتباطات تجاری بررسی نمی‌کنیم. با این اوصاف، ما یک برنامه نمونه را شبیه سازی می کنیم تا مطمئن شویم برنامه شما با آنچه در بالای آن می سازیم مطابقت دارد، و API برای Datastore در Google Cloud Platform را فعال می کنیم تا بتواند داده های مربوط به خرید را حفظ کند. سبد خرید

شبیه سازی برنامه از GitHub

در یک ترمینال، نمونه ربات Django Echo را با دستور زیر در فهرست کاری پروژه خود کلون کنید:

$ 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 این کد، API پیام‌های تجاری، API ارتباطات تجاری و API ساخت ابری را فعال کرده‌اید.

برای این کد لبه، از آنجایی که ما با Google Datastore کار خواهیم کرد، باید این API را نیز فعال کنیم:

  1. Google Datastore API را در Google Cloud Console باز کنید.
  2. مطمئن شوید که با پروژه GCP درست کار می کنید.
  3. روی Enable کلیک کنید.

استقرار نمونه برنامه

در یک ترمینال، به دایرکتوری مرحله 2 نمونه بروید.

برای استقرار نمونه دستورات زیر را در ترمینال اجرا کنید:

$ gcloud config set project PROJECT_ID*
$ gcloud app deploy
  • PROJECT_ID شناسه پروژه پروژه ای است که برای ثبت نام با API ها استفاده کرده اید.

به URL برنامه کاربردی در خروجی آخرین دستور توجه کنید:

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

کدی که اخیراً استفاده کردید حاوی یک برنامه وب با یک هوک برای دریافت پیام از Business Messages است. این شامل تمام کارهایی است که ما از قسمت 1 کد لبه انجام داده ایم. اگر قبلاً این کار را انجام نداده اید، لطفاً وب هوک خود را پیکربندی کنید .

این برنامه به برخی از سوالات ساده مانند درخواست کاربر در مورد ساعات کاری Bonjour Meal پاسخ می دهد. شما باید این را در دستگاه تلفن همراه خود از طریق URL های آزمایشی که می توانید از اطلاعات نماینده در کنسول ارتباطات تجاری بازیابی کنید، آزمایش کنید. نشانی‌های اینترنتی آزمایشی تجربه پیام‌های تجاری را در دستگاه تلفن همراه شما راه‌اندازی می‌کنند و می‌توانید در آنجا با نماینده خود تعامل برقرار کنید.

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
        }
    ]
}

بیایید برنامه پایتون را برای خواندن این فایل در آن قرار دهیم!

خواندن از موجودی ما

Inventory یک فایل ثابت به نام "inventory.json" است که در دایرکتوری ./resources یافت می شود. ما باید مقداری منطق پایتون را به views.py اضافه کنیم تا محتویات فایل JSON را بخوانیم و سپس آن را در مکالمه قرار دهیم. بیایید تابعی ایجاد کنیم که داده ها را از فایل JSON می خواند و لیست محصولات موجود را برمی گرداند.

این تعریف تابع را می توان در هر جایی از views.py قرار داد.

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

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

این باید آنچه را که برای خواندن داده ها از موجودی نیاز داریم به ما بدهد. اکنون ما به راهی نیاز داریم تا اطلاعات این محصول را در گفتگو نمایان کنیم.

سطح بندی کاتالوگ محصولات

برای سادگی در این کد، ما یک کاتالوگ محصول کلی داریم تا از طریق یک چرخ فلک کارت غنی، همه اقلام موجودی را در مکالمه Business Messages قرار دهیم.

برای مشاهده کاتالوگ محصول، ما می خواهیم یک پاسخ پیشنهادی ایجاد کنیم که دارای متن "نمایش منو" و postbackData " show-product-catalog " باشد. هنگامی که کاربران روی پاسخ پیشنهادی ضربه می زنند و برنامه وب شما داده های بازپس گیری را دریافت می کند، ما چرخ فلک کارت غنی را ارسال می کنیم. بیایید یک ثابت جدید برای این پاسخ پیشنهادی در بالای views.py اضافه کنیم.

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

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

از اینجا، پیام را تجزیه می کنیم و آن را به یک تابع جدید هدایت می کنیم که یک چرخ فلک کارت غنی حاوی کاتالوگ محصول را ارسال می کند. ابتدا تابع route_message را گسترش دهید تا یک تابع " send_product_catalog " را هنگامی که روی پاسخ پیشنهادی ضربه زده شد فراخوانی کند و سپس ما تابع را تعریف می کنیم.

در قطعه زیر، یک شرط اضافی به دستور if در تابع route_message اضافه کنید تا بررسی کنید که آیا 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, که چرخ فلک کارت‌های غنی را از فایل موجودی که قبلاً خواندیم تولید می‌کند.

تعاریف تابع را می توان در هر جایی از views.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 داده‌های ارسالی حاوی JSON را که مورد و اقدامی را که کاربر می‌خواهد انجام دهد (افزودن یا حذف از سبد خرید) را توصیف می‌کند، به وب‌هوک شما ارسال می‌کند. در بخش بعدی، پیام‌هایی را که شبیه به این هستند تجزیه می‌کنیم تا بتوانیم کالا را به سبد خرید اضافه کنیم.

اکنون که این تغییرات را انجام دادیم، بیایید برنامه وب را در Google App Engine اجرا کنیم و تجربه را امتحان کنیم!

$ gcloud app deploy

هنگامی که صفحه مکالمه را روی دستگاه تلفن همراه خود بارگذاری کردید، پیام "نمایش محصول-کاتالوگ" را ارسال کنید و باید چرخ فلکی از محصولات را مشاهده کنید که شبیه این است.

4639da46bcc5230c.png

اگر روی Add item ضربه بزنید، در واقع هیچ اتفاقی نمی‌افتد، به جز اینکه عامل، داده‌های پس‌بازگشت را از پاسخ پیشنهادی بازتاب می‌دهد. در بخش بعدی، از کاتالوگ محصول استفاده می کنیم و از آن برای ساخت سبد خرید که در آن کالا اضافه می شود، استفاده می کنیم.

کاتالوگ محصولی که به تازگی ساخته اید را می توان به روش های مختلفی گسترش داد. ممکن است گزینه های مختلف منوی نوشیدنی یا گزینه های گیاهخواری داشته باشید. استفاده از چرخ فلک ها یا تراشه های پیشنهادی راهی عالی برای اینکه کاربران گزینه های منو را بررسی کنند تا به مجموعه ای از محصولات مورد نظر خود برسند. به عنوان یک افزونه برای این کد، سعی کنید سیستم کاتالوگ محصولات را گسترش دهید تا کاربر بتواند نوشیدنی ها را جدا از غذا در منو مشاهده کند یا حتی بتواند گزینه های گیاهخواری را مشخص کند.

4. سبد خرید

در این بخش از Codelab، ما عملکرد سبد خرید را بر اساس بخش قبلی ایجاد می کنیم که به ما امکان می دهد محصولات موجود را مرور کنیم.

در میان بسیاری از موارد، تجربه سبد خرید کلیدی به کاربران این امکان را می دهد که مواردی را به سبد خرید اضافه کنند، اقلام را از سبد خرید حذف کنند، تعداد هر کالای موجود در سبد را پیگیری کنند و اقلام موجود در سبد را بررسی کنند.

پیگیری وضعیت سبد خرید به این معنی است که ما باید داده ها را در برنامه وب حفظ کنیم. برای سادگی آزمایش و استقرار، از Google Datastore در Google Cloud Platform برای ماندگاری داده ها استفاده می کنیم. شناسه مکالمه بین کاربر و کسب و کار ثابت می ماند، بنابراین می توانیم از آن برای مرتبط کردن کاربران با اقلام سبد خرید استفاده کنیم.

بیایید با اتصال به Google Datastore و حفظ شناسه مکالمه زمانی که آن را می بینیم شروع کنیم.

ارتباط با Datastore

هر زمان که تعاملی در سبد خرید انجام شود، به عنوان مثال، زمانی که کاربر در حال افزودن یا حذف یک مورد است، ما با Google Datastore ارتباط خواهیم داشت. می‌توانید درباره استفاده از این کتابخانه مشتری برای تعامل با Google Datastore در اسناد رسمی اطلاعات بیشتری کسب کنید.

قطعه زیر تابعی را برای به روز رسانی سبد خرید تعریف می کند. تابع ورودی زیر را دریافت می کند: conversation_id و message . message حاوی JSON است که عملکردی را که کاربر می‌خواهد انجام دهد را توصیف می‌کند، که قبلاً در چرخ فلک شما تعبیه شده است و کاتالوگ محصول را نمایش می‌دهد. این تابع یک سرویس گیرنده Google Datastore ایجاد می کند و فوراً یک موجودی سبد خرید را واکشی می کند، جایی که کلید شناسه مکالمه است.

تابع زیر را در فایل 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)

بیایید این تابع را گسترش دهیم تا یک مورد به سبد خرید اضافه کنیم.

افزودن اقلام به سبد خرید

وقتی کاربر روی یک اقدام پیشنهادی افزودن آیتم از چرخ فلک محصول ضربه می‌زند، داده‌های ارسالی حاوی JSON هستند که عملکردی را که کاربر می‌خواهد انجام دهد، توصیف می‌کند. فرهنگ لغت JSON دارای دو کلید "action" و "item_name" است و این دیکشنری JSON به وب هوک شما ارسال می شود. فیلد "item_name" شناسه منحصر به فردی است که با آیتم موجود در inventory.json مرتبط است.

هنگامی که دستور سبد خرید و مورد سبد خرید را از پیام تجزیه کردیم، می‌توانیم عبارات شرطی برای اضافه کردن آیتم بنویسیم. برخی از موارد لبه ای که در اینجا باید به آنها فکر کنید این است که Datastore هرگز شناسه مکالمه را ندیده باشد یا اینکه سبد خرید برای اولین بار این کالا را دریافت می کند. موارد زیر توسعه‌ای از قابلیت update_shopping_cart تعریف شده در بالا است. این تغییر یک مورد را به سبد خرید اضافه می کند که توسط Google Datastore ادامه دارد.

قطعه زیر پسوندی از تابع قبلی است که به views.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 نامیده می شود. شما همچنین باید با استفاده از قراردادی که در سرتاسر Codelab استفاده می کنیم، یک ثابت برای اضافه کردن آیتم ها تعریف کنید.

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 اجرا کنید، باید بتوانید تغییرات سبد خرید را که در داشبورد Google Datastore موجود در کنسول GCP منعکس شده است، مشاهده کنید. به اسکرین شات زیر کنسول Google Datastore مراجعه کنید، یک موجودیت واحد وجود دارد که به نام Conversation ID و به دنبال آن برخی از روابط با موجودی کالاها و مقدار آن اقلامی که در سبد خرید هستند نامگذاری شده است.

619dc18a8136ea69.png

در بخش بعدی، راهی برای فهرست کردن اقلام در سبد خرید ایجاد خواهیم کرد. مکانیسم بررسی سبد خرید باید همه اقلام موجود در سبد خرید، تعداد آن اقلام و گزینه ای برای حذف یک کالا از سبد خرید را به ما نشان دهد.

بررسی اقلام موجود در سبد خرید

فهرست کردن اقلام در سبد خرید تنها راهی است که می‌توانیم وضعیت سبد خرید را درک کنیم و بدانیم کدام اقلام را می‌توانیم حذف کنیم.

بیایید ابتدا یک پیام دوستانه مانند "در اینجا سبد خرید شما است:" و سپس پیام دیگری که حاوی یک چرخ فلک کارت غنی با پاسخ های پیشنهادی مرتبط برای "حذف یک" یا "افزودن یک" است ارسال کنیم. چرخ فلک کارت غنی علاوه بر این باید تعداد اقلام ذخیره شده در سبد خرید را فهرست کند.

قبل از اینکه وارد شوید و عملکرد خود را بنویسیم باید از آن آگاه باشیم: اگر فقط یک نوع کالا در سبد خرید وجود داشته باشد، نمی‌توانیم آن را به‌عنوان چرخ فلک ارائه کنیم. چرخ و فلک کارت غنی باید حداقل دو کارت داشته باشد. از طرف دیگر، اگر کالایی در سبد خرید وجود نداشته باشد، می‌خواهیم یک پیام ساده نشان دهیم که می‌گوید سبد خرید خالی است.

با در نظر گرفتن این موضوع، اجازه دهید تابعی به نام send_shopping_cart تعریف کنیم. این تابع به Google Datastore متصل می شود و یک موجودی سبد خرید را براساس شناسه مکالمه درخواست می کند. هنگامی که آن را بدست آوریم، تابع get_inventory_data را فراخوانی می کنیم و از یک چرخ فلک کارت غنی برای گزارش وضعیت سبد خرید استفاده می کنیم. همچنین باید شناسه یک محصول را بر اساس نام دریافت کنیم و می‌توانیم یک تابع را برای جستجو در Google Datastore برای تعیین آن مقدار اعلام کنیم. همانطور که چرخ فلک در حال تولید است، می‌توانیم پاسخ‌های پیشنهادی را برای حذف موارد یا اضافه کردن موارد بر اساس شناسه محصول مرتبط کنیم. قطعه زیر تمام این عملیات را انجام می دهد. کد را در هر جایی در 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)

...

مطمئن شوید که قبلاً CMD_SHOW_CART در بالای views.py تعریف کرده‌اید و اگر کاربر پیامی حاوی «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)
...

34801776a97056ac.png

بر اساس منطقی که در تابع send_shopping_cart معرفی کردیم، وقتی «show-cart» را تایپ می‌کنید، پیامی دریافت می‌کنیم مبنی بر اینکه چیزی در سبد خرید وجود ندارد، یک کارت غنی که یک کالا را در سبد خرید نشان می‌دهد، یا چرخ فلکی از کارت هایی که چندین آیتم را نشان می دهد. علاوه بر این، ما سه پاسخ پیشنهادی داریم: "مشاهده قیمت کل"، "تخلیه سبد خرید" و "منو را ببینید".

سعی کنید تغییرات کد بالا را اعمال کنید تا بررسی کنید سبد خرید شما مواردی را که اضافه می‌کنید ردیابی می‌کند و می‌توانید سبد خرید را از روی سطح مکالمات همانطور که در عکس‌های صفحه بالا نشان داده شده است بررسی کنید. می توانید تغییرات را با این دستور اجرا شده از دایرکتوری step-2 که در آن تغییرات خود را اضافه می کنید، اجرا کنید.

$ gcloud app deploy

ویژگی «مشاهده قیمت کل» را در بخش بعدی پس از ساخت قابلیت حذف یک مورد از سبد خرید، ایجاد خواهیم کرد. تابع get_cart_price مشابه ویژگی "مشاهده سبد خرید" عمل می کند به این معنا که داده ها را در Datastore با فایل inventory.json ارجاع می دهد تا قیمت کل سبد خرید را تولید کند. این برای بخش بعدی Codelab که در آن با پرداخت‌ها ادغام می‌شویم مفید خواهد بود.

حذف اقلام از سبد خرید

در نهایت، می‌توانیم با معرفی قابلیت حذف سبد خرید، رفتار سبد خرید را تکمیل کنیم. تابع 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 را گسترش دهیم تا به شناسه مکالمه پیامی مبنی بر اضافه یا حذف شدن کالا ارسال کند و پیشنهادهایی برای بررسی سبد خرید یا مشاهده مجدد منو ارائه دهد.

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

که باید آن را انجام دهد! یک تجربه سبد خرید با امکانات کامل که کاربر را قادر می‌سازد موارد را اضافه کند، موارد را حذف کند و موارد را در سبد بررسی کند.

در این مرحله، اگر می‌خواهید عملکرد سبد خرید را در گفتگوی Business Messages ببینید، برنامه را به منظور تعامل با نماینده خود اجرا کنید. با اجرای این دستور در دایرکتوری step-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)
...

در اینجا چند اسکرین شات برای نشان دادن آنچه که منطق بالا به دست می آورد آورده شده است:

8feacf94ed0ac6c4.png

هنگامی که آماده ادغام با پردازشگر پرداخت در قسمت بعدی Codelab هستیم، تابع get_cart_price را فراخوانی می کنیم تا داده ها را به پردازشگر پرداخت منتقل کنیم و جریان پرداخت را شروع کنیم.

باز هم، می‌توانید این قابلیت سبد خرید را در مکالمه Business Messages با استقرار برنامه و تعامل با نماینده خود امتحان کنید.

$ gcloud app deploy

6. تبریک می گویم

تبریک می‌گوییم، شما با موفقیت تجربه سبد خرید را در Business Messages ایجاد کرده‌اید.

چیزی که ما در این کد لبه بررسی نکردیم، ویژگی خالی کردن کل سبد خرید است. اگر مایل هستید، سعی کنید برنامه را گسترش دهید تا ویژگی "خالی کردن سبد خرید" را انجام دهد. راه حل در مرحله 3 از کد منبعی که شما شبیه سازی کرده اید موجود است.

در بخش آینده، ما با یک پردازشگر پرداخت خارجی ادغام می‌شویم تا کاربران شما بتوانند تراکنش پرداخت را با نام تجاری شما انجام دهند.

یک سبد خرید خوب چیست؟

یک تجربه خوب از سبد خرید در مکالمه با یک اپلیکیشن موبایل یا در یک فروشگاه فیزیکی تفاوتی ندارد. امکان افزودن اقلام، حذف اقلام و محاسبه قیمت سبد خرید تنها چند ویژگی است که در این کد لبه بررسی کردیم. چیزی که با یک سبد خرید در دنیای واقعی متفاوت است این است که می‌توانید قیمت تمام اقلام موجود در سبد را در هر لحظه مشاهده کنید، همانطور که اقلام را اضافه می‌کنید یا موارد را حذف می‌کنید. این نوع ویژگی های با ارزش، تجربه تجارت مکالمه شما را برجسته می کند!

بعدش چی؟

وقتی آماده شدید، برخی از موضوعات زیر را بررسی کنید تا در مورد تعاملات پیچیده تری که می توانید در Business Messages به دست آورید آشنا شوید:

اسناد مرجع