# イベントトラッキング

このガイドでは、Webサイトから Treasure AI にビジネスイベントを定義・送信する方法を説明します。適切に設計されたイベントはIn-Browser Messageのターゲティングの基盤となり、キャンペーンのトリガー条件とパーソナライズ属性の利用可能範囲を決定します。

責務の分担
**開発者が定義するもの**: ビジネスイベント（`view_item`、`purchase`など）と認証済みユーザーID。

**SDKが自動で処理するもの**: ページビュー、セッション計測、デバイス/ブラウザプロパティ、メッセージのインプレッションとクリック計測（現在および将来）。

将来のSDKバージョンではページビューとSPAルート変更も自動計測される予定です。そのため、このページの手動実装パターンは将来的にはオプションになります。

## イベントアーキテクチャ

ページビュー、ビジネスイベント、将来SDKが自動計測するイベントを含む**すべてのイベントを1つのテーブル**に集約します。クエリでは `event_name` でイベント種別を区別します。

| **発生したこと**  | **テーブル**  | **用途**  |
|  --- | --- | --- |
| ページビュー、セッション開始/終了 | `events`（`event_name: 'page_view'`） | RT Entry Criteria、オーディエンスセグメンテーション |
| 商品閲覧、カート、購入 | `events`（`event_name: 'view_item'`、`'add_to_cart'` など） | RT属性、トリガー条件 |
| メッセージ表示 / クリック / 閉じる | `events`（`event_name: 'td_msg_shown'` など）— 将来、自動 | キャンペーンレポート、フリークエンシーキャップ |


**ページビュー数のカウント**: `SELECT count(*) FROM events WHERE event_name = 'page_view'`

## GA4互換のイベント命名

ビジネスイベントには**GA4の標準イベント名**の採用を推奨します。GA4をすでに使用しているチームの実装コストを最小化し、GTMのdataLayerを再利用でき、RT 2.0ターゲティングのための一貫したスキーマを提供します。

### 標準Eコマースイベント

| **イベント名**  | **発火タイミング**  | **主要プロパティ**  |
|  --- | --- | --- |
| `view_item` | ユーザーが商品詳細ページを閲覧 | `item_id`、`item_name`、`price`、`item_category`、`item_brand` |
| `add_to_cart` | ユーザーが商品をカートに追加 | `item_id`、`item_name`、`price`、`quantity` |
| `view_cart` | ユーザーがカートを開く | `cart_total`、`item_count`、`items`（JSON文字列） |
| `purchase` | 注文確認ページ | `transaction_id`、`revenue`、`item_count`、`item_id`、`items` |
| `add_to_wishlist` | ユーザーが商品を保存 | `item_id`、`price` |
| `login` | ユーザーがログイン | `method`（例: `"email"`、`"google"`） |
| `sign_up` | ユーザーが登録 | `method` |


### プロパティ命名規則

| **カテゴリー**  | **プロパティ名**  | **型**  | **備考**  |
|  --- | --- | --- | --- |
| 商品ID | `item_id` | string | GA4互換 |
| 商品名 | `item_name` | string |  |
| 単価 | `price` | number |  |
| 注文合計金額 | `revenue` | number |  |
| 数量 | `quantity` | number |  |
| カテゴリー（最大5階層） | `item_category` ～ `item_category5` | string | GA4互換 |
| ブランド | `item_brand` | string |  |
| 注文ID | `transaction_id` | string | GA4互換 |
| 商品配列 | `items` | string（JSON） | 後述の配列処理を参照 |


## イベントの送信

### 基本イベント

```javascript
td.trackEvent('events', {
  event_name: 'view_item',
  item_id: 'SKU-001',
  item_name: 'Essential Crew Tee',
  price: 29,
  item_category: 'tops',
  item_brand: 'BASIQ',
})
```

### カートイベント

```javascript
// カートに追加
td.trackEvent('events', {
  event_name: 'add_to_cart',
  item_id: 'SKU-001',
  item_name: 'Essential Crew Tee',
  price: 29,
  quantity: 1,
  item_category: 'tops',
})

// カート表示（集計）
td.trackEvent('events', {
  event_name: 'view_cart',
  cart_total: 87,
  item_count: 3,
  items: JSON.stringify([
    { item_id: 'SKU-001', price: 29, quantity: 1 },
    { item_id: 'SKU-014', price: 58, quantity: 2 },
  ]),
})
```

### 購入イベント

```javascript
td.trackEvent('events', {
  event_name: 'purchase',
  transaction_id: 'ORDER-10432',
  revenue: 87,
  item_count: 3,
  // 主要商品のフラットフィールド — RTトリガーや属性で使いやすい
  item_id: 'SKU-001',
  item_category: 'tops',
  // 完全な商品配列をJSON文字列として（網羅性のため）
  items: JSON.stringify([
    { item_id: 'SKU-001', item_name: 'Essential Crew Tee', price: 29, quantity: 1 },
    { item_id: 'SKU-014', item_name: 'Wide-leg Pants', price: 58, quantity: 2 },
  ]),
})
```

### 配列データの扱い（items）

複数商品を含むイベント（カート、購入）では、商品リストの送信方法を検討する必要があります。

| **アプローチ**  | **メリット**  | **デメリット**  |
|  --- | --- | --- |
| JSON-stringify（`items: "[{...}]"`） | 実装が簡単。GTMフレンドリー | TDクエリが複雑になる。RT属性では直接使用不可 |
| フラット化（`item_id_0`、`item_id_1`…） | クエリしやすい | カートサイズに応じてフィールド数が急増 |
| **推奨: 主要フラット + JSON全リスト** | トリガーと網羅性の両立 | 実装がやや複雑 |


**推奨パターンを使用してください**: トリガー条件に最も役立つプロパティ（例: `item_category`、`price`）はフラットフィールドとして送信し、完全なリストはレポート用にJSON文字列として追加します。

```javascript
td.trackEvent('events', {
  event_name: 'purchase',
  transaction_id: 'ORDER-001',
  revenue: 8760,
  item_count: 2,
  // フラット: 主要商品フィールド（RT属性条件で使いやすい）
  item_id: 'SKU-123',
  item_category: 'electronics',
  price: 4380,
  // JSON文字列としての全リスト（レポート向け）
  items: JSON.stringify([
    { item_id: 'SKU-123', item_name: 'Headphones', price: 4380, quantity: 1 },
    { item_id: 'SKU-456', item_name: 'Case', price: 4380, quantity: 1 },
  ]),
})
```

## SDKロード前のイベント送信

SDKスクリプトが非同期でロードされるページでは、SDKの準備が整う前に送信されたイベントが失われる可能性があります。**コマンドキューパターン**を使用すると、いつでも安全にイベントを送信でき、SDKの準備が整い次第再生されます。

```javascript
// いつでも安全に呼び出せる — SDKが未ロードの場合はキューに追加される
;(window.__tdEventQueue = window.__tdEventQueue || []).push([
  'trackEvent', 'events', { event_name: 'view_item', item_id: 'SKU-001', price: 29 }
])

// SDK初期化コード（sdk-loader.jsなど）内で:
;(function drainQueue() {
  var queue = window.__tdEventQueue || []
  window.__tdEventQueue = { push: function(entry) {
    if (!entry || !entry.length) return
    var method = entry[0]
    if (window.td && typeof window.td[method] === 'function') {
      window.td[method].apply(window.td, entry.slice(1))
    }
  }}
  queue.forEach(window.__tdEventQueue.push)
})()
```

## GTM連携

### GA4のdataLayerを活用する

GTM経由でGA4イベントをすでに送信しているサイトでは、最小限の追加コードでdataLayerを傍受してTDにイベントを転送できます。

```javascript
// GTM Custom HTMLタグ — All Pagesで発火
<script>
;(function () {
  var _push = window.dataLayer.push.bind(window.dataLayer)
  window.dataLayer.push = function (event) {
    _push(event)
    // GA4形式のイベントをTDに転送
    if (event && event.event && window.td) {
      var GA4_EVENTS = [
        'view_item', 'add_to_cart', 'view_cart',
        'purchase', 'add_to_wishlist', 'login', 'sign_up'
      ]
      if (GA4_EVENTS.indexOf(event.event) !== -1) {
        var props = Object.assign({}, event)
        props.event_name = props.event   // event_nameフィールドとして保持
        delete props.event
        delete props.gtm  // GTM内部プロパティを削除
        window.td.trackEvent('events', props)
      }
    }
  }
})()
</script>
```

GTMのグローバル変数
TD Web SDKは `window.td`（または `window.Treasure`）をグローバルとして公開しています。GTMのCustom HTMLタグから追加設定なしで `window.td.trackEvent(...)` を呼び出せます。

### イベントごとに個別のGTMタグを作成する

あるいは、対応するGA4イベントまたはカスタムGTMトリガーで発火する**Custom HTML**タグをイベントタイプごとに作成することもできます。

```html
<!-- GTMタグ: GA4の "view_item" イベントで発火 -->
<script>
  if (window.td && {{ecommerce.items}}) {
    var item = {{ecommerce.items}}[0] || {}
    window.td.trackEvent('events', {
      event_name: 'view_item',
      item_id: item.item_id,
      item_name: item.item_name,
      price: item.price,
      item_category: item.item_category,
    })
  }
</script>
```

## RT 2.0でのイベント活用

イベントがTDデータベースに流れ始めると、RT 2.0の以下の機能で利用できるようになります。

| **機能**  | **イベントの活用方法**  |
|  --- | --- |
| **RT属性** | イベント履歴からユーザーごとの属性を計算します（例: `most_recent_product`、`total_spend`）。Entry Criteriaとオーディエンスセグメンテーションのインプットになります。 |
| **Entry Criteria** | 特定のイベント条件が満たされたときにパーソナライズセクションをトリガーします（例: `item_category == "electronics"`）。現在はイベント名マッチング（L1）をサポート。プロパティベースのフィルタリング（L2）は計画中です。 |
| **オーディエンスセグメント** | イベント履歴からバッチセグメントを構築します（例: 「過去30日以内に購入したユーザー」）。キャンペーンターゲティングに使用します。 |


## 将来SDKが自動計測する予定のイベント

以下のイベントは現在手動実装が必要です。将来のSDKバージョンでは自動的に計測されます。テーブルとフィールド名はSDKイベント要件仕様に定義されています。

| **イベント**  | **現在**  | **将来（SDK自動）**  |
|  --- | --- | --- |
| ページビュー | すべてのルート変更で手動 `trackEvent('events', { event_name: 'page_view' })` 呼び出しが必要 | ページロードおよびSPAルート変更時に自動 |
| セッション開始 / 終了 | 利用不可 | 自動（30分タイムアウト。`td_session_id` がすべてのイベントに付与される） |
| 初回訪問 | 利用不可 | 最初のページロード時に自動（`td_first_visit` イベント） |
| メッセージインプレッション | 利用不可 | SDKがポップアップまたはインラインメッセージをレンダリングしたときに自動（`td_msg_shown`） |
| メッセージクリック | 利用不可 | CTAの操作時に自動（`td_msg_clicked`） |
| メッセージ閉じる | 利用不可 | 閉じるボタン操作時に自動（`td_msg_dismissed`） |


将来を見据えた実装
このガイドで説明するGA4互換のイベント名とフラットフィールドパターンを採用すると、将来RT 2.0のトリガー条件機能（L2プロパティマッチング）が追加されてもスキーマ変更なく対応できます。