Thông tin cập nhật theo thời gian thực

Phần này mô tả cách bạn có thể gửi thông tin cập nhật kịp thời của các thực thể khoảng không quảng cáo cho Google. Với API cập nhật theo thời gian thực, bạn có thể đẩy các bản cập nhật và xoá các thực thể trong Hộp cát hoặc khoảng không quảng cáo chính thức của mình theo thời gian gần như thực.

Chức năng này chủ yếu dành cho các bản cập nhật mà bạn không thể đoán trước, chẳng hạn như đóng cửa khẩn cấp, xoá mục khỏi trình đơn hoặc cập nhật giá của một mục trong trình đơn. Những nội dung này phải được phản ánh nhanh chóng trong giao diện người dùng Google. Nếu thay đổi của bạn không cần được phản ánh ngay lập tức, bạn có thể sử dụng tính năng nhập hàng loạt. Các bản cập nhật theo thời gian thực được xử lý trong vòng không quá 5 phút.

Điều kiện tiên quyết

Bạn phải thực hiện các mục sau trước khi triển khai cập nhật theo thời gian thực:

  1. API Đặt chỗ trên Maps đang bật:
    • Trong GCP, hãy chuyển đến API và Dịch vụ > Thư viện
    • Tìm "Google Maps Đặt API"
      Tìm API đặt phòng của Google Maps
    • Tìm thực thể Hộp cát ("API đặt phòng của Google Maps (Nhà phát triển)") rồi nhấp vào Bật
    • Tìm phiên bản Sản xuất ("API đặt chỗ của Google Maps") rồi nhấp vào Bật
      Bật API Đặt phòng trên Google Maps
  2. Bạn sẽ tạo một tài khoản dịch vụ với vai trò người chỉnh sửa đối với dự án GCP. Để biết thêm thông tin chi tiết, hãy xem bài viết Thiết lập tài khoản.
  3. Nguồn cấp dữ liệu sản xuất hoặc hộp cát được lưu trữ và nhập. Để biết thêm thông tin chi tiết, hãy xem bài viết Truyền dẫn hàng loạt.
  4. Để xác thực API, bạn nên cài đặt Thư viện ứng dụng của Google bằng ngôn ngữ bạn chọn. Sử dụng “https://www.googleapis.com/auth/mapsbooking” làm phạm vi OAuth. Các mã mẫu dưới đây sử dụng các thư viện này. Nếu không, bạn sẽ cần xử lý các hoạt động trao đổi mã thông báo theo cách thủ công như mô tả trong bài viết Sử dụng OAuth 2.0 để truy cập API của Google.

Tổng quan

API cập nhật theo thời gian thực hỗ trợ hai loại thao tác. Thao tác đầu tiên sẽ cập nhật các thực thể hiện có. Thao tác thứ hai là xoá để xoá thực thể khỏi khoảng không quảng cáo của bạn. Cả hai thao tác này đều được thực hiện trên một loạt các thực thể được liệt kê trong nội dung yêu cầu. Bạn có thể cập nhật cho tối đa 1.000 thực thể trong một lệnh gọi API. API chấp nhận mọi yêu cầu được gửi đến và đặt các yêu cầu đó vào hàng đợi để xử lý thêm. Do đó, các yêu cầu RTU được xử lý không đồng bộ.

API cập nhật theo thời gian thực hoạt động trong hai môi trường: hộp cát và môi trường sản xuất. Môi trường hộp cát dùng để kiểm thử các yêu cầu API và môi trường thực tế để cập nhật nội dung mà người dùng Đặt hàng hai đầu có thể nhìn thấy. Tên máy chủ của cả hai môi trường:

  • Hộp cát – partnerdev-mapsbooking.googleapis.com
  • Sản xuất - mapsbooking.googleapis.com

Điểm cuối

API cập nhật theo thời gian thực hiển thị 2 điểm cuối để xử lý các yêu cầu gửi đến về việc cập nhật kho hàng:

  • UPSERT – /v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchPush
  • XOÁ – /v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchDelete

Bạn có thể tìm thấy tham số PARTNER_ID trên Actions Center (Trung tâm hành động) và hiển thị dưới dạng Mã đối tác trên trang Tài khoản và người dùng, như minh hoạ trên ảnh chụp màn hình dưới đây.

Mã nhận dạng đối tác trên Cổng đối tác

Lấy 10000001 làm giá trị của PARTNER_ID làm ví dụ từ ảnh chụp màn hình ở trên, URL hoàn chỉnh để gửi yêu cầu API trong hộp cát và phiên bản chính thức sẽ có dạng như trong các ví dụ dưới đây.

# Sandbox UPSERT
https://partnerdev-mapsbooking.googleapis.com/v1alpha/inventory/partners/10000001/feeds/owg.v2/record:batchPush
# Sandbox DELETE
https://partnerdev-mapsbooking.googleapis.com/v1alpha/inventory/partners/10000001/feeds/owg.v2/record:batchDelete
# Production UPSERT
https://mapsbooking.googleapis.com/v1alpha/inventory/partners/10000001/feeds/owg.v2/record:batchPush
# Production DELETE
https://mapsbooking.googleapis.com/v1alpha/inventory/partners/10000001/feeds/owg.v2/record:batchDelete

Cập nhật thực thể

Để cập nhật các thực thể trong khoảng không quảng cáo của bạn, hãy sử dụng điểm cuối UPSERT và gửi các yêu cầu POST qua HTTP. Mỗi yêu cầu POST phải bao gồm thông số PARTNER_ID cùng với tải trọng JSON chứa dữ liệu có cấu trúc của bất kỳ loại thực thể nào được liệt kê trong giản đồ khoảng không quảng cáo.

Tải trọng yêu cầu cập nhật và chèn dữ liệu

Nội dung yêu cầu là một đối tượng JSON có danh sách các bản ghi. Mỗi bản ghi tương ứng với một thực thể đang được cập nhật. Thông tin này bao gồm trường data_record với tải trọng thực thể được mã hoá bằng Base64 và generation_timestamp cho biết thời gian cập nhật thực thể:

  {
    "records": [
      {
        "data_record":"BASE_64_ENCODED_ENTITY",
        "generation_timestamp":"UPDATE_TIMESTAMP"
      }
    ]
  }

Trong tải trọng ở trên, hãy thay thế nội dung sau:

  • BASE_64_ENCODED_ENTITY: Chuỗi JSON mã hoá Base64 của thực thể. JSON thực thể đã giải mã phải có cấu trúc giống như trong quy cách nguồn cấp dữ liệu, ví dụ:

    {"@type":"MenuSection","name":"My Updated Menu Section","menuId":{"@id":"10824","displayOrder":1},"@id":"853705"}
  • UPDATE_TIMESTAMP: Hãy nhớ cung cấp dấu thời gian cho biết thời điểm thực thể được tạo trong các hệ thống phụ trợ của bạn. Dấu thời gian này dùng để đảm bảo thứ tự chính xác của các bản cập nhật khoảng không quảng cáo. Nếu không có trường này, thì trường này sẽ được đặt thành thời điểm Google nhận được yêu cầu. Khi cập nhật một thực thể thông qua yêu cầu batchPush, trường generation_timestamp sẽ được dùng để tạo phiên bản thực thể. Xem định dạng dự kiến của giá trị thời gian trong giản đồ khoảng không quảng cáo quan hệ.

Mọi yêu cầu cập nhật theo thời gian thực phải đáp ứng các điều kiện sau:

  • Kích thước phần tải trọng không được vượt quá 5 MB. Tương tự như nguồn cấp dữ liệu hàng loạt, bạn nên loại bỏ khoảng trắng để có thể điều chỉnh thêm dữ liệu.
  • Có thể có tối đa 1.000 thực thể trong một yêu cầu batchPush.

Ví dụ

Ví dụ 1: Cập nhật một nhà hàng

Giả sử bạn cần cập nhật khẩn cấp số điện thoại của một nhà hàng. Nội dung cập nhật của bạn chứa JSON cho toàn bộ nhà hàng.

Hãy cân nhắc sử dụng một nguồn cấp dữ liệu hàng loạt có dạng như sau:

{
  "@type": "Restaurant",
  "@id": "restaurant12345",
  "name": "Some Restaurant",
  "url": "https://www.provider.com/somerestaurant",
  "telephone": "+16501234570",
  "streetAddress": "345 Spear St",
  "addressLocality": "San Francisco",
  "addressRegion": "CA",
  "postalCode": "94105",
  "addressCountry": "US",
  "latitude": 37.472842,
  "longitude": -122.217144
}
  

Thì việc cập nhật theo thời gian thực của bạn bằng HTTP POST sẽ như sau:

JSON

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchPush
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": {
        "@type": "Restaurant",
        "@id": "restaurant12345",
        "name": "Some Restaurant",
        "url": "https://www.provider.com/somerestaurant",
        "telephone": "+16501234570",
        "streetAddress": "345 Spear St",
        "addressLocality": "San Francisco",
        "addressRegion": "CA",
        "postalCode": "94105",
        "addressCountry": "US",
        "latitude": 37.472842,
        "longitude": -122.217144
      }
      "generation_timestamp": "2022-08-19T17:11:10.750Z"
    }
  ]
}
    

Base64

Ví dụ tương tự với tải trọng được mã hoá Base64.

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchPush
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": "eyJAdHlwZSI6IlJlc3RhdXJhbnQiLCJAaWQiOiJyZXN0YXVyYW50MTIzNDUiLCJuYW1lIjoiU29tZSBSZXN0YXVyYW50IiwidXJsIjoiaHR0cHM6Ly93d3cucHJvdmlkZXIuY29tL3NvbWVyZXN0YXVyYW50IiwidGVsZXBob25lIjoiKzE2NTAxMjM0NTcwIiwic3RyZWV0QWRkcmVzcyI6IjM0NSBTcGVhciBTdCIsImFkZHJlc3NMb2NhbGl0eSI6IlNhbiBGcmFuY2lzY28iLCJhZGRyZXNzUmVnaW9uIjoiQ0EiLCJwb3N0YWxDb2RlIjoiOTQxMDUiLCJhZGRyZXNzQ291bnRyeSI6IlVTIiwibGF0aXR1ZGUiOjM3LjQ3Mjg0MiwibG9uZ2l0dWRlIjotMTIyLjIxNzE0NH0="
      "generation_timestamp": "2022-08-19T17:11:10.750Z"
    }
  ]
}
    

Ví dụ 2: Cập nhật nhiều nhà hàng

Để cập nhật hai thực thể nhà hàng trong một lệnh gọi API, yêu cầu POST HTTP sẽ như sau:

JSON

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchPush
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": {
        "@type": "Restaurant",
        "@id": "restaurant12345",
        "name": "Some Restaurant",
        "url": "https://www.provider.com/somerestaurant",
        "telephone": "+16501235555",
        "streetAddress": "345 Spear St",
        "addressLocality": "San Francisco",
        "addressRegion": "CA",
        "postalCode": "94105",
        "addressCountry": "US",
        "latitude": 37.472842,
        "longitude": -122.217144
      },
      "generation_timestamp": "2022-08-19T17:11:10.850Z"
    },
    {
      "data_record": {
        "@type": "Restaurant",
        "@id": "restaurant123",
        "name": "Some Other Restaurant",
        "url": "https://www.provider.com/someotherrestaurant",
        "telephone": "+16501231235",
        "streetAddress": "385 Spear St",
        "addressLocality": "San Mateo",
        "addressRegion": "CA",
        "postalCode": "94115",
        "addressCountry": "US"
      },
      "generation_timestamp": "2022-08-19T17:11:10.850Z"
    }
  ]
}
    

Base64

Ví dụ tương tự với tải trọng được mã hoá Base64.

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchPush
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": "eyJAdHlwZSI6IlJlc3RhdXJhbnQiLCJAaWQiOiJyZXN0YXVyYW50MTIzNDUiLCJuYW1lIjoiU29tZSBSZXN0YXVyYW50IiwidXJsIjoiaHR0cHM6Ly93d3cucHJvdmlkZXIuY29tL3NvbWVyZXN0YXVyYW50IiwidGVsZXBob25lIjoiKzE2NTAxMjM1NTU1Iiwic3RyZWV0QWRkcmVzcyI6IjM0NSBTcGVhciBTdCIsImFkZHJlc3NMb2NhbGl0eSI6IlNhbiBGcmFuY2lzY28iLCJhZGRyZXNzUmVnaW9uIjoiQ0EiLCJwb3N0YWxDb2RlIjoiOTQxMDUiLCJhZGRyZXNzQ291bnRyeSI6IlVTIiwibGF0aXR1ZGUiOjM3LjQ3Mjg0MiwibG9uZ2l0dWRlIjotMTIyLjIxNzE0NH0=",
      "generation_timestamp": "2022-08-19T17:11:10.850Z"
    },
    {
      "data_record": "eyJAdHlwZSI6IlJlc3RhdXJhbnQiLCJAaWQiOiJyZXN0YXVyYW50MTIzIiwibmFtZSI6IlNvbWUgT3RoZXIgUmVzdGF1cmFudCIsInVybCI6Imh0dHBzOi8vd3d3LnByb3ZpZGVyLmNvbS9zb21lcmVzdGF1cmFudCIsInRlbGVwaG9uZSI6IisxNjUwMTIzMTIzNSIsInN0cmVldEFkZHJlc3MiOiIzODUgU3BlYXIgU3QiLCJhZGRyZXNzTG9jYWxpdHkiOiJTYW4gTWF0ZW8iLCJhZGRyZXNzUmVnaW9uIjoiQ0EiLCJwb3N0YWxDb2RlIjoiOTQxMTUiLCJhZGRyZXNzQ291bnRyeSI6IlVTIn0=",
      "generation_timestamp": "2022-08-19T17:11:10.850Z"
    }
  ]
}
    

Ví dụ 3: Cập nhật giá của một món trong thực đơn

Giả sử bạn cần thay đổi giá của một mục trong trình đơn.

Hãy xem xét một nguồn cấp dữ liệu hàng loạt có dạng như sau:

{
  "@type": "MenuItemOffer",
  "@id": "menuitemoffer6680262",
  "sku": "offer-cola",
  "menuItemId": "menuitem896532",
  "price": 2,
  "priceCurrency": "USD"
}

Sau đó, nội dung cập nhật theo thời gian thực của bạn qua POST sẽ như sau:

JSON

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchPush
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": {
        "@type": "MenuItemOffer",
        "@id": "menuitemoffer6680262",
        "sku": "offer-cola",
        "menuItemId": "menuitem896532",
        "price": 2,
        "priceCurrency": "USD"
      },
      "generation_timestamp": "2022-08-19T17:20:10Z"
    }
  ]
}
    

Base64

Ví dụ tương tự với tải trọng được mã hoá Base64.

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchPush
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": "eyJAdHlwZSI6Ik1lbnVJdGVtT2ZmZXIiLCJAaWQiOiJtZW51aXRlbW9mZmVyNjY4MDI2MiIsInNrdSI6Im9mZmVyLWNvbGEiLCJtZW51SXRlbUlkIjoibWVudWl0ZW04OTY1MzIiLCJwcmljZSI6MiwicHJpY2VDdXJyZW5jeSI6IlVTRCJ9",
      "generation_timestamp": "2022-08-19T17:20:10Z"
    }
  ]
}

    

Thêm thực thể

Đừng sử dụng tính năng cập nhật theo thời gian thực để thêm thực thể mới vì điều này có thể khiến dữ liệu không nhất quán. Thay vào đó, hãy sử dụng quy trình nguồn cấp dữ liệu hàng loạt như mô tả cho quá trình nhập hàng loạt.

Xoá thực thể

Để xoá thực thể khỏi khoảng không quảng cáo của bạn, hãy sử dụng điểm cuối XOÁ và gửi yêu cầu POST qua HTTP. Mỗi yêu cầu POST phải bao gồm tham số PARTNER_ID cùng với tải trọng JSON chứa giá trị nhận dạng của bất kỳ thực thể nào trong khoảng không quảng cáo của bạn.

Xoá tải trọng yêu cầu

Phần nội dung của yêu cầu xoá có cấu trúc tương tự như yêu cầu cập nhật. Tệp này cũng có một danh sách bản ghi với các trường data_recorddelete_time:

  {
    "records": [
      {
        "data_record":"BASE_64_ENCODED_REFERENCE",
        "delete_time": "DELETE_TIMESTAMP"
      }
    ]
  }

Trong tải trọng ở trên, hãy thay thế nội dung sau:

  • BASE_64_ENCODED_REFERENCE: Chuỗi JSON mã hoá Base64 của tham chiếu đến thực thể đang bị xoá. Tệp đối chiếu chỉ bao gồm loại thực thể và giá trị nhận dạng, chẳng hạn như bản trình bày JSON của tệp tham chiếu đến MenuSection:

    {"@type":"MenuSection","@id":"853705"}
  • DELETE_TIMESTAMP: Hãy nhớ cung cấp dấu thời gian cho thời điểm thực thể bị xoá trong hệ thống phụ trợ. Dấu thời gian này dùng để xác định thứ tự áp dụng xoá cho khoảng không quảng cáo.

Có thể có tối đa 1.000 thực thể trong một yêu cầu batchDelete.

Ví dụ

Ví dụ 1: Xoá 2 thực thể MenuItem

Để xoá hai mục trong trình đơn trong một lệnh gọi API, yêu cầu POST HTTP sẽ như sau:

JSON

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchDelete
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": {
        "@type": "MenuItem",
        "@id": "item_1234"
      },
      "delete_time": "2022-08-21T15:23:00.000Z"
    },
    {
      "data_record": {
        "@type": "MenuItem",
        "@id": "item_5678"
      },
      "delete_time": "2022-08-21T15:23:00.000Z"
    }
  ]
}
    

Base64

Ví dụ tương tự với tải trọng được mã hoá Base64.

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchDelete
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": "eyJAdHlwZSI6Ik1lbnVJdGVtIiwiQGlkIjoiaXRlbV8xMjM0In0="
      "delete_time": "2022-08-21T15:23:00.000Z"
    },
    {
      "data_record": "eyJAdHlwZSI6Ik1lbnVJdGVtIiwiQGlkIjoiaXRlbV81Njc4In0="
      "delete_time": "2022-08-21T15:23:00.000Z"
    },
  ]
}
    

Ví dụ 2: Xoá thực thể Restaurant

Hãy cân nhắc trường hợp bạn muốn xoá một nhà hàng trong nguồn cấp dữ liệu hàng loạt. Bạn chỉ được xoá thực thể nhà hàng. Đừng xoá các thực thể phụ, chẳng hạn như dịch vụ và trình đơn, vì các thực thể phụ này sẽ tự động bị xoá.

Yêu cầu mẫu để xoá một thực thể nhà hàng có mã https://www.provider.com/restaurant/12345:

JSON

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchDelete
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": {
        "@type": "Restaurant",
        "@id": "https://www.provider.com/restaurant/12345"
      },
      "delete_time": "2022-08-19T17:11:10.750Z"
    }
  ]
}
    

Base64

Ví dụ tương tự với tải trọng được mã hoá Base64.

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchDelete
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": "ewogICJAdHlwZSI6ICJSZXN0YXVyYW50IiwKICAiQGlkIjogImh0dHBzOi8vd3d3LnByb3ZpZGVyLmNvbS9yZXN0YXVyYW50LzEyMzQ1Igp9"
      "delete_time": "2022-08-19T17:11:10.750Z"
    }
  ]
}
    

Mã phản hồi của API và xác thực

Có hai loại xác thực được thực hiện trong các lệnh gọi API cập nhật theo thời gian thực:

  • Cấp yêu cầu – Các quy trình xác thực này kiểm tra để đảm bảo tải trọng tuân theo giản đồ upert hoặc delete và mỗi data_record chứa cả hai trường @id@type. Các quy trình kiểm tra này mang tính đồng bộ và kết quả sẽ được trả về trong nội dung phản hồi của API. Mã phản hồi 200 và nội dung JSON trống {} có nghĩa là các thông tin xác thực này đã được chuyển và các thực thể trong yêu cầu đó đã được đưa vào hàng đợi để xử lý. Mã phản hồi khác với mã 200 có nghĩa là một hoặc nhiều quy trình xác thực trong số này không thành công và toàn bộ yêu cầu đã bị từ chối (bao gồm tất cả thực thể trong tải trọng). Ví dụ: nếu data_record bị thiếu @type, thì phản hồi lỗi sau đây sẽ được trả về:

    {
      "error": {
        "code": 400,
        "message": "Record:{\"@id\":\"2717/86853/DELIVERY\",\"applicableServiceType\":[\"DELIVERY\",\"TAKEOUT\"],\"menuId\":[{\"@id\":\"2717/DELIVERY\",\"displayOrder\":1},{\"@id\":\"2717/TAKEOUT\",\"displayOrder\":2}],\"name\":\"Salad\",\"offeredById\":[\"2717\"]} has following errors: \nThe entity type could not be extracted from the entity value.\n",
        "status": "INVALID_ARGUMENT",
        "details": [
          {
            "@type": "type.googleapis.com/google.rpc.DebugInfo",
            "detail": "[ORIGINAL ERROR] generic::invalid_argument: Failed to parse one or more rtu records. Record:{\"@id\":\"2717/86853/DELIVERY\",\"applicableServiceType\":[\"DELIVERY\",\"TAKEOUT\"],\"menuId\":[{\"@id\":\"2717/DELIVERY\",\"displayOrder\":1},{\"@id\":\"2717/TAKEOUT\",\"displayOrder\":2}],\"name\":\"Salad\",\"offeredById\":[\"2717\"]} has following errors: \nThe entity type could not be extracted from the entity value.\n [google.rpc.error_details_ext] { message: \"Record:{\\\"@id\\\":\\\"2717/86853/DELIVERY\\\",\\\"applicableServiceType\\\":[\\\"DELIVERY\\\",\\\"TAKEOUT\\\"],\\\"menuId\\\":[{\\\"@id\\\":\\\"2717/DELIVERY\\\",\\\"displayOrder\\\":1},{\\\"@id\\\":\\\"2717/TAKEOUT\\\",\\\"displayOrder\\\":2}],\\\"name\\\":\\\"Salad\\\",\\\"offeredById\\\":[\\\"2717\\\"]} has following errors: \\nThe entity type could not be extracted from the entity value.\\n\" }"
          }
        ]
      }
    }
    
  • Cấp thực thể – Mỗi thực thể trong tải trọng được xác thực theo giản đồ quan hệ. Các vấn đề gặp phải trong giai đoạn xác thực này sẽ không được báo cáo trong phản hồi của API. Các báo cáo này chỉ được báo cáo trong trang tổng quan Báo cáo RTU.

Hạn mức API

Các bản cập nhật API theo thời gian thực có hạn mức 1.500 yêu cầu mỗi 60 giây hoặc trung bình 25 yêu cầu mỗi giây. Khi vượt quá hạn mức, Google sẽ phản hồi bằng thông báo lỗi sau đây:

{
  "error": {
    "code": 429,
    "message": "Insufficient tokens for quota ...",
    "status": "RESOURCE_EXHAUSTED",
    "details": [...]
  }
}

Để xử lý việc này, hãy thử gọi lại với những khoảng thời gian lớn hơn theo cấp số nhân cho đến khi thành công. Nếu bạn thường xuyên sử dụng hết hạn mức, hãy cân nhắc đưa thêm nhiều thực thể vào một yêu cầu API. Bạn có thể đưa tối đa 1.000 thực thể vào một lệnh gọi API.

Mã mẫu

Dưới đây là một số mẫu về cách sử dụng API cập nhật theo thời gian thực bằng nhiều ngôn ngữ. Các mẫu này sử dụng Thư viện xác thực của Google để xác thực bằng cách sử dụng tệp khoá tài khoản dịch vụ được tạo trong quá trình Thiết lập tài khoản. Để biết các giải pháp thay thế, hãy tham khảo bài viết Sử dụng OAuth 2.0 cho ứng dụng từ máy chủ đến máy chủ. Hãy cân nhắc sử dụng giản đồ có trong Tạo thư viện ứng dụng để tạo mã nguồn cho khoảng không quảng cáo và các loại đối tượng cập nhật theo thời gian thực.

Cập nhật thực thể

Node.js

Mã này sử dụng thư viện xác thực của Google cho Node.js.

/* Sample code for Real-time update batchPush implementation.
 *
 * Required libraries:
 * - google-auth-library
 */

const {JWT} = require('google-auth-library');

// ACTION REQUIRED: Change this to the path of the service account client secret
// file downloaded from the Google Cloud Console.
const serviceAccountJson = require('./service-account.json');

// ACTION REQUIRED: Change this to your Partner ID received from Google.
// The Partner ID is available on the Partner Portal.
const PARTNER_ID = 1234;

const HOST = {
  prod: 'https://mapsbooking.googleapis.com',
  sandbox: 'https://partnerdev-mapsbooking.googleapis.com'
};

// ACTION REQUIRED: Change to 'prod' for production
const ENV = 'sandbox';

// Feed name for Order with Google including the version.
const FEED_NAME = 'owg.v2';

// Endpoint url
const url = `${HOST[ENV]}/v1alpha/inventory/partners/${PARTNER_ID}/feeds/${
    FEED_NAME}/record:batchPush`;

/**
 * Send a Real-time update request to update/insert entities
 */
async function batchUpsert(entities) {
  /**
   * Sign JWT token using private key from service account secret file
   * provided. The client can be created without providing a service account
   * secret file by implementing Application Default Credentials.
   * https://github.com/googleapis/google-auth-library-nodejs
   */
  const client = new JWT({
    email: serviceAccountJson.client_email,
    key: serviceAccountJson.private_key,
    scopes: ['https://www.googleapis.com/auth/mapsbooking'],
  });
  const request = {records: toPushRecords(entities)};
  const body = JSON.stringify(request);
  try {
    const response = await client.request({
      method: 'POST',
      url,
      data: body,
      headers: {'Content-Type': 'application/json'}
    });
    console.log('request body:', body);
    console.log('response status:', response.status);
    console.log(
        'response data:', response.data);  // successful response returns '{}'
  } catch (error) {
    console.log('error:', error);
  }
}

/**
 * Maps array of entities to records for batch push requests
 */
const toPushRecords = (entities) => {
  return entities.map((entity) => {
    // Using dateModified to set generation_timestamp. Defaulting to the
    // current timestamp for records that do not have dateModified.
    const generation_timestamp =
        entity.dateModified ? entity.dateModified : new Date().toISOString();
    return {data_record: btoa(JSON.stringify(entity)), generation_timestamp};
  });
};

// Call batchUpsert with example entities. dateModified is optional and is
// used to hold the actual timestamp when the entity was updated/created.
batchUpsert([
  {
    '@type': 'MenuItemOffer',
    '@id': '6680261',
    'menuItemId': '18931508',
    'price': 15.5,
    'priceCurrency': 'USD',
    'applicableServiceType': ['DELIVERY', 'TAKEOUT'],
    'inventoryLevel': 0,
    'dateModified': '2022-06-19T15:43:50.970Z'
  },
  {
    '@type': 'MenuItemOffer',
    '@id': '6680262',
    'menuItemId': '18931509',
    'price': 25.5,
    'priceCurrency': 'USD',
    'applicableServiceType': ['DELIVERY', 'TAKEOUT'],
    'inventoryLevel': 0,
    'dateModified': '2022-06-19T15:43:50.970Z'
  }
]);

Python

Mã này sử dụng thư viện xác thực của Google cho Python.

"""Sample code for the Real-time update batchPush implementation."""

# Required libraries:
# - google-auth

import base64
import datetime
import json
from google.auth.transport.requests import AuthorizedSession
from google.oauth2 import service_account

# ACTION REQUIRED: Change this to the Partner ID received from Google.
# Partner ID is available on the Partner Portal.
# https://partnerdash.google.com/apps/reservewithgoogle
_PARTNER_ID = '1234'

# ACTION REQUIRED: Change this to the path of the service account client secret
# file downloaded from the Google Cloud Console.
_SERVICE_ACCOUNT_KEY_JSON_FILE = 'service-account-creds.json'

_HOST_MAP = {
    'sandbox': 'https://partnerdev-mapsbooking.googleapis.com',
    'prod': 'https://mapsbooking.googleapis.com'
}

# ACTION REQUIRED: Change to 'prod' for production
_ENV = 'sandbox'

# Feed name for Order with Google including the version.
_FEED_NAME = 'owg.v2'

_ENDPOINT = '{}/v1alpha/inventory/partners/{}/feeds/{}/record:batchPush'.format(
    _HOST_MAP[_ENV], _PARTNER_ID, _FEED_NAME)


def batch_upsert(entities):
  """Makes a batchPush request using the Real-time updates REST service.

  Args:
      entities: The list of entity objects to update or add.
  """

  # Creates credentials by providing a json file. Credentials can also be
  # provided by implementing Application Default Credentials.
  # https://googleapis.dev/python/google-auth/latest/user-guide.html
  credentials = service_account.Credentials.from_service_account_file(
      _SERVICE_ACCOUNT_KEY_JSON_FILE,
      scopes=['https://www.googleapis.com/auth/mapsbooking'])
  authorized_session = AuthorizedSession(credentials)

  # JSON request object
  batch_request = {'records': [create_push_record(x) for x in entities]}
  response = authorized_session.post(_ENDPOINT, json=batch_request)
  print('request body:', json.dumps(batch_request))
  print('response status:', response.status_code)
  print('response data:', response.text)  # successful response returns '{}'


def create_push_record(entity):
  """Creates a record from an entity for batchPush requests.

  Args:
      entity: The entity object to create the record from.

  Returns:
      The constructed record for the batchPush request payload.
  """
  data_bytes = json.dumps(entity).encode('utf-8')
  base64_bytes = base64.b64encode(data_bytes)
  # Using dateModified to set generation_timestamp. Defaulting to the
  # current timestamp for records that do not have dateModified.
  generation_timestamp = entity.dateModified if 'dateModified' in entity else datetime.datetime.now(
  ).strftime('%Y-%m-%dT%H:%M:%S.%fZ')
  return {
      'generation_timestamp': generation_timestamp,
      'data_record': base64_bytes.decode('utf-8')
  }


# Call batch_upsert with example entities. dateModified is optional and is
# used to hold the actual timestamp when the entity was updated/created.
batch_upsert([{
    '@type': 'MenuItemOffer',
    '@id': '6680261',
    'menuItemId': '18931508',
    'price': 15.5,
    'priceCurrency': 'USD',
    'applicableServiceType': ['DELIVERY', 'TAKEOUT'],
    'inventoryLevel': 0,
    'dateModified': '2022-06-19T15:43:50.970Z'
}, {
    '@type': 'MenuItemOffer',
    '@id': '6680262',
    'menuItemId': '18931509',
    'price': 25.5,
    'priceCurrency': 'USD',
    'applicableServiceType': ['DELIVERY', 'TAKEOUT'],
    'inventoryLevel': 0,
    'dateModified': '2022-06-19T15:43:50.970Z'
}])

Java

Mã này sử dụng thư viện xác thực của Google dành cho Java.

Các mô hình mã nguồn ứng dụng trong các gói rtusamples.inventoryrtusamples.realtime đã được tạo bằng cách làm theo các bước trong phần Tạo thư viện ứng dụng.

/*
 * Required Libraries:
 * - JDK >= 11
 * - google-auth-library-oauth2-http
 */
package rtusamples;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.auth.oauth2.AccessToken;
import com.google.auth.oauth2.GoogleCredentials;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.nio.charset.Charset;
import java.time.Clock;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import rtusamples.inventory.MenuItemOffer;
import rtusamples.inventory.MenuItemOfferType;
import rtusamples.inventory.ServiceTypeElement;
import rtusamples.realtime.BatchPushGenericRecordRequest;
import rtusamples.realtime.GenericRecord;

/** Sample code for Real-time update batchPush implementation. */
public final class BasicPush {
  // ACTION REQUIRED: Change this to your Partner ID received from Google. The Partner ID is
  // available on the Partner Portal.
  private static final long PARTNER_ID = 12345678;

  // ACTION REQUIRED: Change this to the path of the service account client secret file downloaded
  // from the Google Cloud Console.
  private static final String JSON_KEY_FULL_PATH =
      "<path to your JSON credentials>/credentials.json";

  // ACTION REQUIRED: Change this to the endpoint that is needed.
  private static final String ENDPOINT =
      //    "https://partnerdev-mapsbooking.googleapis.com"; // for sandbox
      "https://mapsbooking.googleapis.com"; // for prod

  // Feed name for Order with Google including the version.
  private static final String FEED_NAME = "owg.v2";

  private static final ObjectMapper objectMapper = new ObjectMapper();

  private static final DateTimeFormatter TIMESTAMP_FORMATTER =
      DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.SSS]'Z'");

  private static final Charset UTF_8 = Charset.forName("UTF-8");

  public static void main(String[] args) throws Exception {

    /**
     * Create credentials from service account secret file. Alternatively, the credentials can be
     * created by implementing Application Default Credentials.
     * https://github.com/googleapis/google-auth-library-java
     */
    // GoogleCredentials sourceCredentials =
    //     GoogleCredentials.getApplicationDefault()
    //         .createScoped(Arrays.asList("https://www.googleapis.com/auth/mapsbooking"));

    // ImpersonatedCredentials credentials =
    //     ImpersonatedCredentials.create(
    //         sourceCredentials,
    //         "fo-test@projectname.iam.gserviceaccount.com",
    //         null,
    //         Arrays.asList("https://www.googleapis.com/auth/mapsbooking"),
    //         300);

    GoogleCredentials credentials =
        GoogleCredentials.fromStream(new FileInputStream(JSON_KEY_FULL_PATH))
            .createScoped(Arrays.asList("https://www.googleapis.com/auth/mapsbooking"));

    // Create example MenuItemOffer entities, dateModified is optional and is used to hold
    // the actual timestamp when the entity was updated/created.
    MenuItemOffer menuItemOfferPizza = new MenuItemOffer();
    menuItemOfferPizza.setID("6680261");
    menuItemOfferPizza.setType(MenuItemOfferType.MENU_ITEM_OFFER);
    menuItemOfferPizza.setMenuItemID("18931508");
    menuItemOfferPizza.setPrice(15.5);
    menuItemOfferPizza.setPriceCurrency("USD");
    menuItemOfferPizza.setApplicableServiceType(
        new ServiceTypeElement[] {ServiceTypeElement.TAKEOUT, ServiceTypeElement.DELIVERY});
    menuItemOfferPizza.setInventoryLevel(0.0);
    menuItemOfferPizza.setDateModified("2022-10-07T13:00:00.000Z");

    MenuItemOffer menuItemOfferSalad = new MenuItemOffer();
    menuItemOfferSalad.setID("6680262");
    menuItemOfferSalad.setType(MenuItemOfferType.MENU_ITEM_OFFER);
    menuItemOfferSalad.setMenuItemID("18931509");
    menuItemOfferSalad.setPrice(25.5);
    menuItemOfferSalad.setPriceCurrency("USD");
    menuItemOfferSalad.setApplicableServiceType(
        new ServiceTypeElement[] {ServiceTypeElement.TAKEOUT, ServiceTypeElement.DELIVERY});
    menuItemOfferSalad.setInventoryLevel(0.0);
    menuItemOfferSalad.setDateModified("2022-10-07T13:00:00.000Z");

    // Example array of MenuItemOffer entities to update.
    List<MenuItemOffer> menuItemOffers = Arrays.asList(menuItemOfferPizza, menuItemOfferSalad);

    // Create list of GenericRecord from menuItemOffers.
    List<GenericRecord> menuItemOfferGenericRecords =
        menuItemOffers.stream()
            .map(
                (menuItemOffer) ->
                    toBatchPushRecord(menuItemOffer, menuItemOffer.getDateModified()))
            .collect(Collectors.toList());

    // List of records to be updated/created.
    List<GenericRecord> recordsToBeUpdated = new ArrayList<>();

    // Add list of menuItemOffer generic records.
    recordsToBeUpdated.addAll(menuItemOfferGenericRecords);

    // Request object that contains all records.
    BatchPushGenericRecordRequest batchPushRequest = new BatchPushGenericRecordRequest();
    batchPushRequest.setRecords(recordsToBeUpdated.toArray(new GenericRecord[0]));

    // Execute batchPush request.
    BasicPush basicPush = new BasicPush();
    basicPush.batchPush(batchPushRequest, credentials);
  }

  public void batchPush(
      BatchPushGenericRecordRequest batchPushRequest, GoogleCredentials credentials)
      throws IOException {

    credentials.refreshIfExpired();
    AccessToken token = credentials.getAccessToken();

    String requestBody = objectMapper.writeValueAsString(batchPushRequest);
    HttpClient client = HttpClient.newHttpClient();
    HttpRequest request =
        HttpRequest.newBuilder()
            .uri(
                URI.create(
                    String.format(
                        "%s/v1alpha/inventory/partners/%s/feeds/%s/record:batchPush",
                        ENDPOINT, PARTNER_ID, FEED_NAME)))
            .header("Content-Type", "application/json")
            .header("Authorization", String.format("Bearer %s", token.getTokenValue()))
            .POST(BodyPublishers.ofString(requestBody))
            .build();

    HttpResponse<String> response = null;
    try {
      response = client.send(request, BodyHandlers.ofString());
      System.out.println("Request body:" + requestBody);
      System.out.println("Response status:" + response.statusCode());
      System.out.println("Response body:" + response.body());
    } catch (IOException | InterruptedException e) {
      e.printStackTrace();
    }
  }

  public static <T> GenericRecord toBatchPushRecord(T entity, String dateModified) {
    GenericRecord genericRecord = new GenericRecord();
    try {
      String json = objectMapper.writeValueAsString(entity);
      genericRecord.setDataRecord(Base64.getEncoder().encodeToString(json.getBytes(UTF_8)));
      // Using dateModified to set generation_timestamp. Defaulting to the
      // current timestamp for records that do not have dateModified.
      String generationTimestamp =
          Optional.ofNullable(dateModified)
              .orElse(OffsetDateTime.now(Clock.systemUTC()).format(TIMESTAMP_FORMATTER));
      genericRecord.setGenerationTimestamp(generationTimestamp);
    } catch (JsonProcessingException e) {
      System.out.println(e.getMessage());
    }
    return genericRecord;
  }
}

Xoá thực thể

Node.js

Mã này sử dụng thư viện xác thực của Google cho Node.js.

/* Sample code for Real-time update batchDelete implementation.
 *
 * Required libraries:
 * - google-auth-library
 */

const {JWT} = require('google-auth-library');

// ACTION REQUIRED: Change this to the path of the service account client secret
// file downloaded from the Google Cloud Console.
const serviceAccountJson = require('./service-account.json');

// ACTION REQUIRED: Change this to your Partner ID received from Google.
// The Partner ID is available on the Partner Portal.
const PARTNER_ID = 1234;

const HOST = {
  prod: 'https://mapsbooking.googleapis.com',
  sandbox: 'https://partnerdev-mapsbooking.googleapis.com'
};

// ACTION REQUIRED: Change to 'prod' for production
const ENV = 'sandbox';

// Feed name for Order with Google including the version.
const FEED_NAME = 'owg.v2';

// Endpoint url
const url = `${HOST[ENV]}/v1alpha/inventory/partners/${PARTNER_ID}/feeds/${
    FEED_NAME}/record:batchDelete`;


/**
 * Send a Real-time update request to delete entities
 */
async function batchDelete(entities) {
  try {
    /**
     * Sign JWT token using private key from service account secret file
     * provided. The client can be created without providing a service account
     * secret file by implementing Application Default Credentials.
     * https://github.com/googleapis/google-auth-library-nodejs
     */
    const client = new JWT({
      email: serviceAccountJson.client_email,
      key: serviceAccountJson.private_key,
      scopes: ['https://www.googleapis.com/auth/mapsbooking'],
    });
    const request = {
      records: toDeleteRecords(entities)
    };
    const body = JSON.stringify(request);
    try {
      const response = await client.request({
        method: 'POST',
        url,
        data: body,
        headers: {'Content-Type': 'application/json'}
      });
      console.log('request body:', body);
      console.log('response status:', response.status);
      console.log('response data:', response.data);  // successful response returns '{}'
    } catch (error) {
      console.log('error:', error);
    }
  }

  /**
   * Maps array of entities to records for batch delete requests
   */
  const toDeleteRecords = (entities) => {
    return entities.map((entity) => {
      // Using dateModified to set delete_time. Defaulting to the current
      // timestamp for records that do not have dateModified.
      const delete_time =
          entity.dateModified ? entity.dateModified : new Date().toISOString();
      return {data_record: btoa(JSON.stringify(entity)), delete_time};
    });
  };

  // Call batchDelete with example entities. dateModified is optional and is
  // used to hold the actual timestamp when the entity was deleted.
  batchDelete([
    {
      '@type': 'Menu',
      '@id': '853706',
      'dateModified': '2022-06-19T15:43:50.970Z'
    },
    {
      '@type': 'Menu',
      '@id': '853705',
      'dateModified': '2022-06-19T15:13:00.280Z'
    }
  ]);

Python

Mã này sử dụng thư viện xác thực của Google cho Python.

"""Sample code for the Real-time update batchDelete implementation."""

# Required libraries:
# - google-auth

import base64
import datetime
import json
from google.auth.transport.requests import AuthorizedSession
from google.oauth2 import service_account

# ACTION REQUIRED: Change this to the Partner ID received from Google.
# Partner ID is available on the Partner Portal.
# https://partnerdash.google.com/apps/reservewithgoogle
_PARTNER_ID = '1234'

# ACTION REQUIRED: Change this to the path of the service account client secret
# file downloaded from the Google Cloud Console.
_SERVICE_ACCOUNT_KEY_JSON_FILE = 'service-account-creds.json'

_HOST_MAP = {
    'sandbox': 'https://partnerdev-mapsbooking.googleapis.com',
    'prod': 'https://mapsbooking.googleapis.com'
}

# ACTION REQUIRED: Change to 'prod' for production
_ENV = 'sandbox'

# Feed name for Order with Google including the version.
_FEED_NAME = 'owg.v2'

_ENDPOINT = '{}/v1alpha/inventory/partners/{}/feeds/{}/record:batchDelete'.format(
    _HOST_MAP[_ENV], _PARTNER_ID, _FEED_NAME)


def batch_delete(entities):
  """Makes a batch delete request using the Real-time updates REST service.

  Args:
      entities: The list of entity objects to delete.
  """

  # Creates credentials by providing a json file. Credentials can also be
  # provided by implementing Application Default Credentials.
  # https://googleapis.dev/python/google-auth/latest/user-guide.html
  credentials = service_account.Credentials.from_service_account_file(
      _SERVICE_ACCOUNT_KEY_JSON_FILE,
      scopes=['https://www.googleapis.com/auth/mapsbooking'])
  authorized_session = AuthorizedSession(credentials)

  # JSON request object
  batch_request = {'records': [create_delete_record(x) for x in entities]}
  response = authorized_session.post(_ENDPOINT, json=batch_request)
  print('request body:', json.dumps(batch_request))
  print('response status:', response.status_code)
  print('response data:', response.text)  # successful response returns '{}'


def create_delete_record(entity):
  """Creates a record from an entity for batchDelete requests.

  Args:
    entity: The entity object to create the record from.

  Returns:
    The constructed record for the batchDelete request payload.
  """
  data_bytes = json.dumps(entity).encode('utf-8')
  base64_bytes = base64.b64encode(data_bytes)
  # Using dateModified to set delete_time. Defaulting to the current
  # timestamp for records that do not have dateModified.
  delete_time = entity.dateModified if 'dateModified' in entity else datetime.datetime.now(
  ).strftime('%Y-%m-%dT%H:%M:%S.%fZ')
  return {
      'delete_time': delete_time,
      'data_record': base64_bytes.decode('utf-8')
  }


# Call batch_delete with example entities. dateModified is optional and is
# used to hold the actual timestamp when the entity was deleted.
batch_delete([{
    '@type': 'Menu',
    '@id': '853706',
    'dateModified': '2022-06-19T13:10:00.000Z'
}, {
    '@type': 'Menu',
    '@id': '853705',
    'dateModified': '2022-06-19T13:30:10.000Z'
}])

Java

Mã này sử dụng thư viện xác thực của Google dành cho Java.

Các mô hình mã nguồn ứng dụng trong các gói rtusamples.inventoryrtusamples.realtime đã được tạo bằng cách làm theo các bước trong phần Tạo thư viện ứng dụng.

/*
 * Required Libraries:
 * - JDK >= 11
 * - google-auth-library-oauth2-http
 */
package rtusamples;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.auth.oauth2.AccessToken;
import com.google.auth.oauth2.GoogleCredentials;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.nio.charset.Charset;
import java.time.Clock;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import rtusamples.inventory.Menu;
import rtusamples.inventory.MenuType;
import rtusamples.realtime.BatchDeleteGenericRecordsRequest;
import rtusamples.realtime.GenericDeleteRecord;

/** Sample code for the Real-time update batchDelete implementation. */
public final class BasicDelete {
  // ACTION REQUIRED: Change this to your Partner ID received from Google. The Partner ID is
  // available on the Partner Portal.
  private static final long PARTNER_ID = 123456789;

  // ACTION REQUIRED: Change this to the path of the service account client secret file downloaded
  // from the Google Cloud Console.
  private static final String JSON_KEY_FULL_PATH =
      "<path to your JSON credentials>/credentials.json";

  // ACTION REQUIRED: Change this to the endpoint that is needed.
  private static final String ENDPOINT =
      "https://partnerdev-mapsbooking.googleapis.com"; // for sandbox
  // "https://mapsbooking.googleapis.com" // for prod

  // Feed name for Order with Google including the version.
  private static final String FEED_NAME = "owg.v2";

  private static final ObjectMapper objectMapper = new ObjectMapper();

  private static final DateTimeFormatter TIMESTAMP_FORMATTER =
      DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.SSS]'Z'");

  private static final Charset UTF_8 = Charset.forName("UTF-8");

  public static void main(String[] args) throws Exception {

    /**
     * Create credentials from service account secret file. Alternatively, the credentials can be
     * created by implementing Application Default Credentials.
     * https://github.com/googleapis/google-auth-library-java
     */
    // GoogleCredentials sourceCredentials =
    //     GoogleCredentials.getApplicationDefault()
    //         .createScoped(Arrays.asList("https://www.googleapis.com/auth/mapsbooking"));

    // ImpersonatedCredentials credentials =
    //     ImpersonatedCredentials.create(
    //         sourceCredentials,
    //         "fo-test@projectname.iam.gserviceaccount.com",
    //         null,
    //         Arrays.asList("https://www.googleapis.com/auth/mapsbooking"),
    //         300);

    GoogleCredentials credentials =
        GoogleCredentials.fromStream(new FileInputStream(JSON_KEY_FULL_PATH))
            .createScoped(Arrays.asList("https://www.googleapis.com/auth/mapsbooking"));

    // Create example Menu entities, dateModified is optional and is used to hold
    // the actual timestamp when the entity was deleted.
    Menu menuLunch = new Menu();
    menuLunch.setID("853705");
    menuLunch.setType(MenuType.MENU);
    menuLunch.setDateModified("2022-09-19T13:10:00.000Z");

    Menu menuDinner = new Menu();
    menuDinner.setID("853706");
    menuDinner.setType(MenuType.MENU);
    menuDinner.setDateModified("2022-09-19T13:13:10.000Z");

    // Example array of Menu entities to update.
    List<Menu> menus = Arrays.asList(menuLunch, menuDinner);

    // Create list of GenericDeleteRecord from menus.
    List<GenericDeleteRecord> menuGenericDeleteRecords =
        menus.stream()
            .map((menu) -> toBatchDeleteRecord(menu, menu.getDateModified()))
            .collect(Collectors.toList());

    // List of records to be deleted.
    List<GenericDeleteRecord> recordsToBeDeleted = new ArrayList<>();

    // Add list of menu generic records.
    recordsToBeDeleted.addAll(menuGenericDeleteRecords);

    // Request object that contains all records.
    BatchDeleteGenericRecordsRequest batchDeleteRequest = new BatchDeleteGenericRecordsRequest();
    batchDeleteRequest.setRecords(recordsToBeDeleted.toArray(new GenericDeleteRecord[0]));

    // Execute batchDelete request.
    BasicDelete basicDelete = new BasicDelete();
    basicDelete.batchDelete(batchDeleteRequest, credentials);
  }

  public void batchDelete(
      BatchDeleteGenericRecordsRequest batchDeleteRequest, GoogleCredentials credentials)
      throws IOException {

    credentials.refreshIfExpired();
    AccessToken token = credentials.getAccessToken();

    String requestBody = objectMapper.writeValueAsString(batchDeleteRequest);
    HttpClient client = HttpClient.newHttpClient();
    HttpRequest request =
        HttpRequest.newBuilder()
            .uri(
                URI.create(
                    String.format(
                        "%s/v1alpha/inventory/partners/%s/feeds/%s/record:batchDelete",
                        ENDPOINT, PARTNER_ID, FEED_NAME)))
            .header("Content-Type", "application/json")
            .header("Authorization", String.format("Bearer %s", token.getTokenValue()))
            .POST(BodyPublishers.ofString(requestBody))
            .build();

    HttpResponse<String> response = null;
    try {
      response = client.send(request, BodyHandlers.ofString());
      System.out.println("Request body:" + requestBody);
      System.out.println("Response status:" + response.statusCode());
      System.out.println("Response body:" + response.body());
    } catch (IOException | InterruptedException e) {
      e.printStackTrace();
    }
  }

  public static <T> GenericDeleteRecord toBatchDeleteRecord(T entity, String dateModified) {
    GenericDeleteRecord genericRecord = new GenericDeleteRecord();
    try {
      String json = objectMapper.writeValueAsString(entity);
      genericRecord.setDataRecord(Base64.getEncoder().encodeToString(json.getBytes(UTF_8)));
      // Using dateModified to set delete_time. Defaulting to the current
      // timestamp for records that do not have dateModified.
      String deleteTime =
          Optional.ofNullable(dateModified)
              .orElse(OffsetDateTime.now(Clock.systemUTC()).format(TIMESTAMP_FORMATTER));
      genericRecord.setDeleteTime(deleteTime);
    } catch (JsonProcessingException e) {
      System.out.println(e.getMessage());
    }
    return genericRecord;
  }
}

Trường hợp sử dụng

Các trường hợp sử dụng sau đây là ví dụ về việc cập nhật theo thời gian thực, cập nhật nguồn cấp dữ liệu theo lô và nội dung ở cấp cao trong lệnh gọi API:

Trường hợp Thực thể cần cập nhật Mô tả và hiệu ứng
Tắt dịch vụ Service

Bạn cần tắt một dịch vụ vì một lý do không lường trước được.

Cập nhật theo thời gian thực: Cập nhật thực thể Service được đề cập bằng cách đặt thuộc tính isDisabled của thực thể đó thành true, nhưng giữ nguyên các thuộc tính khác.

Nguồn cấp dữ liệu đầy đủ: Hãy nhớ cập nhật thực thể trong các nguồn cấp dữ liệu đầy đủ để đặt isDisabled thành true trước lần tìm nạp tiếp theo của Google. Nếu không, thực thể sẽ được bật lại.

Mặt hàng cụ thể đã hết hàng MenuItemOffer Cập nhật theo thời gian thực: Gửi thực thể MenuItemOffer đóng gói có inventoryLevel được đặt thành 0 cho MenuItem đã cho và mọi dữ liệu khác không thay đổi.
Thay đổi giá của món trong thực đơn MenuItemOffer Cập nhật theo thời gian thực: Gửi thực thể MenuItemOffer đóng gói có price được đặt thành giá cập nhật cho MenuItem nhất định và mọi dữ liệu khác không thay đổi.

Thêm thực thể cấp cao nhất mới

Chỉ áp dụng cho thực thể thuộc loại Menu, RestaurantService.

Menu, Restaurant, Service

Ví dụ: bạn cần thêm thực đơn mới vào một nhà hàng.

Nguồn cấp dữ liệu đầy đủ: Thêm thực thể vào nguồn cấp dữ liệu rồi chờ nhập hàng loạt.

Xoá vĩnh viễn thực thể cấp cao nhất

Chỉ áp dụng cho thực thể thuộc loại Menu, RestaurantService.

Menu, Restaurant, Service

Thông tin cập nhật theo thời gian thực: Gửi yêu cầu xoá rõ ràng.

Nguồn cấp dữ liệu đầy đủ: Nhớ xoá thực thể khỏi nguồn cấp dữ liệu đầy đủ trước khi Google tìm nạp lần tiếp theo. Nếu không, thực thể sẽ được thêm lại.

Thêm khu vực giao hàng mới trong một Service cụ thể ServiceArea Nguồn cấp dữ liệu theo lô: Gửi thực thể ServiceArea liên quan với toàn bộ các trường của thực thể đó như bình thường trong nguồn cấp dữ liệu đầy đủ, với khu vực phân phối mới được chỉ định trong polygon, geoRadius hoặc postalCode.
Cập nhật thời gian dự kiến giao hàng đến nơi tại Service ServiceHours Nguồn cấp dữ liệu theo lô:Gửi ServiceHours giống như trong nguồn cấp dữ liệu, ngoại trừ leadTimeMin của nguồn cấp dữ liệu được cập nhật tương ứng.
Cập nhật giá giao hàng tại Service Fee Nguồn cấp dữ liệu hàng loạt: Gửi dữ liệu phân phối đầy đủ Fee và cập nhật price.
Cập nhật giờ giao hàng hoặc giờ bán đồ mang đi trong Service ServiceHours Nguồn cấp dữ liệu theo lô:Gửi ServiceHours giống như trong nguồn cấp dữ liệu, ngoại trừ các thuộc tính openscloses của nó được cập nhật tương ứng.
Service (thay đổi số tiền tối thiểu của đơn đặt hàng) Fee Nguồn cấp dữ liệu hàng loạt: Gửi Fee đầy đủ có cập nhật minPrice
Xoá vĩnh viễn MenuItem Menu Nguồn cấp dữ liệu theo lô: Gửi MenuItem giống như trong các nguồn cấp dữ liệu, nhưng không có parentMenuSectionId.

Thời gian xử lý cho các công việc hàng loạt và bản cập nhật theo thời gian thực

Một thực thể được cập nhật hoặc bị xoá thông qua một nguồn cấp dữ liệu hàng loạt sẽ được xử lý trong vòng 2 giờ, còn thực thể được cập nhật thông qua bản cập nhật theo thời gian thực sẽ được xử lý trong 5 phút. Một thực thể đã lỗi thời sẽ bị xoá sau 14 ngày.

Bạn có thể gửi cho Google:

  • Nhiều công việc theo lô mỗi ngày để đảm bảo kho hàng của bạn luôn được cập nhật, HOẶC
  • Một công việc theo lô mỗi ngày và nội dung cập nhật theo thời gian thực để đảm bảo kho hàng của bạn luôn được cập nhật.