# 機能設計書 206-IPルックアップ

## 概要

本ドキュメントは、ChatwootのIPルックアップ機能について記載する。連絡先のIPアドレスから地理情報（都市、国、国コード）を取得し、連絡先の属性として保存する機能である。

### 本機能の処理概要

IPルックアップ機能は、Web Widgetなどを通じてアクセスした連絡先のIPアドレスをGeocoderを使用して地理情報に変換し、連絡先の追加属性として保存するバックグラウンド処理機能である。これにより、連絡先の地理的な分布を把握したり、地域に応じたサポート対応が可能になる。

**業務上の目的・背景**：カスタマーサポートにおいて、顧客の所在地を把握することは重要である。特にグローバルなサービスでは、タイムゾーンや言語の推定、地域別の問い合わせ傾向分析などに活用できる。IPアドレスから自動的に地理情報を取得することで、顧客に位置情報を入力してもらう手間を省ける。

**機能の利用シーン**：
- Web Widgetから新規連絡先がアクセスした際のバックグラウンド処理
- 既存連絡先の地理情報が未取得の場合の補完処理
- 連絡先の地域分析レポート作成の基礎データ収集

**主要な処理内容**：
1. 連絡先のIPアドレスを取得（created_at_ip または updated_at_ip）
2. GeoLite2データベースを使用してIP地理情報をルックアップ
3. 取得した情報（都市、国、国コード）を連絡先の追加属性に保存

**関連システム・外部連携**：MaxMind GeoLite2データベースを使用。IP_LOOKUP_API_KEY環境変数が設定されている場合に有効化される。

**権限による制御**：本機能はバックグラウンドジョブとして自動実行されるため、ユーザー権限による制御は適用されない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 連絡先詳細画面 | 結果表示画面 | 地理情報の表示 |

## 機能種別

バックグラウンド処理 / データ連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| contact | Contact | Yes | 地理情報を取得する連絡先オブジェクト | - |

### 入力データソース

- 連絡先のadditional_attributes['created_at_ip']
- 連絡先のadditional_attributes['updated_at_ip']
- GeoLite2データベース（vendor/db/GeoLiteCity.mmdb）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| city | String | 都市名 |
| country | String | 国名 |
| country_code | String | 国コード（ISO 3166-1） |

### 出力先

連絡先のadditional_attributes（JSONB）

## 処理フロー

### 処理シーケンス

```
1. ジョブ実行
   └─ ContactIpLookupJob.perform(contact)
2. IPアドレス取得
   └─ updated_at_ip または created_at_ip から取得
3. データベース存在確認
   └─ GeoLiteCity.mmdbの存在を確認
4. Geocoder検索
   └─ Geocoder.search(ip_address)
5. 結果取得
   └─ 最初の結果から地理情報を抽出
6. 連絡先更新
   └─ additional_attributesに city, country, country_code を保存
```

### フローチャート

```mermaid
flowchart TD
    A[ContactIpLookupJob開始] --> B{IPアドレス存在?}
    B -->|No| C[処理終了]
    B -->|Yes| D{DBファイル存在?}
    D -->|No| C
    D -->|Yes| E[Geocoder.search実行]
    E --> F{結果あり?}
    F -->|No| C
    F -->|Yes| G[地理情報抽出]
    G --> H[additional_attributes更新]
    H --> I[contact.save!]
    I --> J[処理完了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | IP優先順位 | updated_at_ipを優先し、なければcreated_at_ipを使用 | IPアドレス取得時 |
| BR-002 | DB必須 | GeoLite2データベースファイルが存在しない場合は処理をスキップ | 常時 |
| BR-003 | タイムアウトハンドリング | Errno::ETIMEDOUTをrescueしてログ出力のみ | タイムアウト発生時 |

### 計算ロジック

IPアドレス取得ロジック：
```ruby
def get_contact_ip(contact)
  contact.additional_attributes&.dig('updated_at_ip') ||
  contact.additional_attributes&.dig('created_at_ip')
end
```

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 地理情報保存 | contacts | UPDATE | additional_attributesカラム更新 |

### テーブル別操作詳細

#### contacts

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | additional_attributes | city, country, country_code追加 | JSONBマージ |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | Errno::ETIMEDOUT | Geocoderタイムアウト | ログ出力して処理継続 |
| - | DBファイル不在 | GeoLiteCity.mmdbが存在しない | 処理をスキップ |

### リトライ仕様

ActiveJobのデフォルトリトライ動作に準拠。Errno::ETIMEDOUTはrescueされるためリトライ対象外。

## トランザクション仕様

単一レコードの更新のため、明示的なトランザクション制御は不要。contact.save!で保存。

## パフォーマンス要件

- バックグラウンドジョブとして非同期実行
- キュー名：default
- GeoLite2データベースはローカルファイル参照のため高速

## セキュリティ考慮事項

- IPアドレスは個人情報に該当する可能性あり
- GeoLite2データベースはMaxMindライセンスに準拠して使用
- IP_LOOKUP_API_KEY環境変数で機能の有効/無効を制御

## 備考

- Geocoder gemを使用
- GeoLite2データベースはvendor/db/GeoLiteCity.mmdbに配置
- Geocoderの初期設定はconfig/initializers/geocoder.rbで行う

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: サービス層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ip_lookup_service.rb | `app/services/ip_lookup_service.rb` | Geocoder検索とDB存在確認 |

**読解のコツ**: シンプルなサービスクラス。performメソッドがエントリーポイント。

**主要処理フロー**:
1. **2行目**: performメソッドでIPアドレスを受け取り
2. **3行目**: IPアドレスが空またはDBファイル不在の場合はreturn
3. **5行目**: Geocoder.searchでルックアップ実行
4. **6-8行目**: Errno::ETIMEDOUTをrescueしてログ出力
5. **12-14行目**: ip_database_available?でDBファイル存在確認

#### Step 2: ジョブ層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | contact_ip_lookup_job.rb | `app/jobs/contact_ip_lookup_job.rb` | ジョブ実行と結果保存 |

**主要処理フロー**:
1. **4-8行目**: performメソッドでジョブ実行
2. **12-20行目**: update_contact_location_from_ipで地理情報を保存
3. **22-24行目**: get_contact_ipでIPアドレス取得（優先順位付き）

#### Step 3: 設定を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | geocoder.rb | `config/initializers/geocoder.rb` | Geocoder初期設定 |

**主要処理フロー**:
- **22-24行目**: GeocoderConfiguration::LOOK_UP_DBでDBパス定義
- **26行目**: IP_LOOKUP_API_KEY存在時にgeoip2設定

### プログラム呼び出し階層図

```
ContactIpLookupJob#perform(contact)
    │
    └─ update_contact_location_from_ip(contact)
           │
           ├─ get_contact_ip(contact)
           │      └─ additional_attributes['updated_at_ip'] || ['created_at_ip']
           │
           ├─ IpLookupService.new.perform(ip_address)
           │      ├─ ip_database_available?
           │      │      └─ File.exist?(GeoLiteCity.mmdb)
           │      │
           │      └─ Geocoder.search(ip_address).first
           │
           └─ contact.save!
                  └─ additional_attributes更新
                       (city, country, country_code)
```

### データフロー図

```
[入力]               [処理]                    [出力]

Contact ──────────▶ ContactIpLookupJob ──────▶ Contact.additional_attributes
    │                      │
    │                 ┌────┴────┐
    │                 ▼         ▼
    │           IPアドレス   IpLookupService
    │           取得              │
    │                        ┌────┴────┐
    │                        ▼         ▼
    │                   DB確認    Geocoder検索
    │                        │         │
    │                        └────┬────┘
    │                             ▼
    │                        geocoder_result
    │                        (city, country, country_code)
    │                             │
    └─────────────────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ip_lookup_service.rb | `app/services/ip_lookup_service.rb` | サービス | IP地理情報ルックアップ |
| contact_ip_lookup_job.rb | `app/jobs/contact_ip_lookup_job.rb` | ジョブ | バックグラウンド処理 |
| geocoder.rb | `config/initializers/geocoder.rb` | 設定 | Geocoder初期設定 |
| contact.rb | `app/models/contact.rb` | モデル | 連絡先データ |
| GeoLiteCity.mmdb | `vendor/db/GeoLiteCity.mmdb` | データ | GeoLite2データベース |
