# 機能設計書 190-データインポート

## 概要

本ドキュメントは、Chatwootにおける「データインポート」機能の設計仕様を定義する。本機能は、CSVファイル形式で連絡先データを一括インポートするための機能である。

### 本機能の処理概要

本機能は、管理者がアップロードしたCSVファイルを解析し、連絡先データをシステムに一括登録する。既存の連絡先との重複チェック（identifier、email、phone_number）を行い、重複がある場合は既存データを更新、ない場合は新規作成する。バックグラウンドジョブとして非同期で処理され、完了時に管理者へメール通知が送信される。

**業務上の目的・背景**：顧客データベースの移行やマーケティングリストの一括登録など、大量の連絡先データを効率的にシステムに取り込む必要がある。1件ずつ手動登録すると膨大な時間がかかるため、CSVによる一括インポート機能が必要となる。

**機能の利用シーン**：既存CRMシステムからの顧客データ移行。展示会等で収集した名刺情報の一括登録。マーケティング対象リストの一括インポート。

**主要な処理内容**：
1. CSVファイルのアップロード・保存
2. DataImportレコードの作成
3. バックグラウンドでのCSV解析
4. 重複チェックと連絡先の作成/更新
5. 失敗レコードのCSV出力
6. 完了/失敗通知メールの送信

**関連システム・外部連携**：ActiveStorageによるファイル保存、Sidekiqによる非同期処理、メール配信システム

**権限による制御**：連絡先インポート権限を持つ管理者のみが操作可能

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 20 | 連絡先一覧画面 | 主画面 | インポートボタンからのCSVアップロード |

## 機能種別

バッチ処理 / ファイル処理 / 非同期処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| import_file | File | Yes | CSVファイル | ActiveStorage添付 |
| data_type | String | Yes | データタイプ | "contacts" のみ |

### CSVファイル形式

| カラム名 | 必須 | 説明 |
|---------|-----|------|
| identifier | No | 一意識別子 |
| email | No | メールアドレス |
| phone_number | No | 電話番号 |
| name | No | 名前 |
| company | No | 会社名（additional_attributes） |
| city | No | 都市（additional_attributes） |
| その他 | No | custom_attributesとして保存 |

### 入力データソース

- アップロードされたCSVファイル

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | Integer | DataImportのID |
| status | String | pending, processing, completed, failed |
| processed_records | Integer | 処理成功件数 |
| total_records | Integer | 総件数 |
| failed_records | File | 失敗レコードのCSV（添付ファイル） |

### 出力先

- data_importsテーブル（ステータス管理）
- contactsテーブル（連絡先データ）
- ActiveStorage（失敗レコードCSV）
- メール通知

## 処理フロー

### 処理シーケンス

```
1. CSVファイルアップロード
   └─ import_fileとしてActiveStorageに保存

2. DataImportレコード作成
   └─ status: pending

3. ジョブエンキュー
   └─ after_create_commit で 1分後にDataImportJob実行

4. ジョブ開始
   ├─ status を processing に更新
   └─ CSVファイルを開く

5. CSV解析ループ
   └─ 各行に対して:
          ├─ ContactManager#build_contact で連絡先構築
          ├─ バリデーションチェック
          │      ├─ 有効 → contacts配列に追加
          │      └─ 無効 → rejected_contacts配列に追加（エラー付き）
          └─ 次の行へ

6. 一括インポート
   └─ Contact.import でバルクインサート

7. 後処理
   ├─ status を completed に更新
   ├─ processed_records, total_records を設定
   ├─ 失敗レコードをCSVファイルとして保存
   └─ 管理者へメール通知

8. エラー時
   └─ CSV解析エラーの場合
          ├─ status を failed に更新
          └─ 失敗通知メール送信
```

### フローチャート

```mermaid
flowchart TD
    A[CSVアップロード] --> B[DataImport作成]
    B --> C[1分後ジョブ実行]
    C --> D[status: processing]
    D --> E[CSVファイル読み込み]

    E --> F{CSV解析成功?}
    F -->|No| G[status: failed]
    G --> H[失敗通知メール]

    F -->|Yes| I[行ループ開始]
    I --> J[ContactManager#build_contact]
    J --> K{valid?}
    K -->|Yes| L[contacts配列に追加]
    K -->|No| M[rejected_contactsに追加]

    L --> N{次の行?}
    M --> N
    N -->|Yes| I
    N -->|No| O[Contact.import]

    O --> P[status: completed]
    P --> Q[失敗レコードCSV保存]
    Q --> R[完了通知メール]

    H --> S[終了]
    R --> S
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-190-01 | データタイプ制限 | data_typeは "contacts" のみ許可 | 常時 |
| BR-190-02 | 重複判定優先順位 | identifier > email > phone_number の順で既存データを検索 | 常時 |
| BR-190-03 | 電話番号フォーマット | +で始まらない場合は+を付加 | phone_number処理時 |
| BR-190-04 | 遅延実行 | ファイルアップロード完了を待つため1分後に実行 | ジョブエンキュー時 |
| BR-190-05 | バッチサイズ | Contact.importは1000件単位でバルクインサート | インポート時 |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| インポート作成 | data_imports | INSERT | インポートレコード作成 |
| ステータス更新 | data_imports | UPDATE | processing, completed, failed |
| 連絡先作成 | contacts | INSERT | 新規連絡先の一括作成 |
| 連絡先更新 | contacts | UPDATE | 既存連絡先の更新 |

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

#### data_imports

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | data_type | "contacts" | |
| INSERT | status | pending（デフォルト） | |
| INSERT | account_id | Current.account.id | |
| UPDATE | status | processing, completed, failed | |
| UPDATE | processed_records | 成功件数 | |
| UPDATE | total_records | 成功 + 失敗の合計 | |

#### contacts

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | identifier, email, phone_numberで検索 | 重複チェック |
| INSERT/UPDATE | email, identifier, phone_number, name | CSVデータ | |
| INSERT/UPDATE | additional_attributes | company, city | JSON |
| INSERT/UPDATE | custom_attributes | その他のカラム | JSON |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | CSV::MalformedCSVError | CSVフォーマットエラー | status: failed、失敗通知メール |
| - | ActiveStorage::FileNotFoundError | ファイルが見つからない | 1分待機後リトライ（最大3回） |
| - | バリデーションエラー | 連絡先データ不正 | rejected_contactsに追加 |

### リトライ仕様

- ActiveStorage::FileNotFoundError: 1分待機後リトライ、最大3回
- その他: Sidekiqのデフォルトリトライ設定

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

Contact.importはバルクインサートでトランザクション処理。失敗した場合は該当バッチのみロールバック。

## パフォーマンス要件

- lowキューで実行（低優先度）
- batch_size: 1000でバルクインサート
- find_eachではなくCSV全体をメモリに読み込み

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

- アカウント境界の厳守
- ファイルはActiveStorageで安全に保存
- UTF-8エンコーディングの自動検出・変換

## 備考

- 失敗レコードCSVにはerrorsカラムが追加される
- on_duplicate_key_ignore: true で重複キーエラーを無視
- track_validation_failures: true でバリデーション失敗を追跡

---

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

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

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、DataImportモデルを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | data_import.rb | `app/models/data_import.rb` | モデル定義 |

**読解のコツ**:
- **19-22行目**: belongs_to、バリデーション、enum定義
- **24-25行目**: ActiveStorage添付ファイル定義
- **27-28行目**: after_create_commit コールバック
- **31-33行目**: process_data_import - 1分後のジョブエンキュー

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

CSV処理のメインロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | data_import_job.rb | `app/jobs/data_import_job.rb` | ジョブ実装 |

**主要処理フロー**:
- **5行目**: retry_on ActiveStorage::FileNotFoundError - リトライ設定
- **8-16行目**: perform - メイン処理
- **21-28行目**: process_import_file - ファイル処理
- **30-46行目**: parse_csv_and_build_contacts - CSV解析
- **53-56行目**: import_contacts - バルクインポート
- **88-94行目**: send_import_notification_to_admin - 完了通知

#### Step 3: 連絡先構築ロジックを理解する

重複チェックと連絡先構築を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | contact_manager.rb | `app/services/data_import/contact_manager.rb` | 連絡先管理 |

**主要処理フロー**:
- **6-9行目**: build_contact - エントリーポイント
- **12-17行目**: find_or_initialize_contact - 既存/新規判定
- **20-27行目**: find_existing_contact - 重複検索
- **29-44行目**: 各種検索メソッド（identifier、email、phone_number）
- **47-49行目**: format_phone_number - 電話番号フォーマット
- **61-66行目**: update_contact_attributes - 属性更新

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

```
CSVアップロード API
    │
    └─ DataImport#create
           │
           ├─ ActiveStorage (import_file添付)
           │
           └─ after_create_commit
                  │
                  └─ DataImportJob.set(wait: 1.minute).perform_later
                         │
                         ├─ process_import_file
                         │      │
                         │      ├─ parse_csv_and_build_contacts
                         │      │      │
                         │      │      └─ ContactManager#build_contact
                         │      │             │
                         │      │             ├─ find_or_initialize_contact
                         │      │             │      ├─ find_contact_by_identifier
                         │      │             │      ├─ find_contact_by_email
                         │      │             │      └─ find_contact_by_phone_number
                         │      │             │
                         │      │             └─ update_contact_attributes
                         │      │
                         │      ├─ import_contacts
                         │      │      └─ Contact.import(batch_size: 1000)
                         │      │
                         │      ├─ update_data_import_status
                         │      │
                         │      └─ save_failed_records_csv
                         │
                         └─ send_import_notification_to_admin
                                └─ AccountNotificationMailer
```

### データフロー図

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

CSVファイル ─────────▶ DataImport作成 ─────────────────▶ data_importsテーブル
(ActiveStorage)              │                              (status管理)
                             ▼
                     DataImportJob (非同期)
                             │
                             ▼
                     CSV解析 + ContactManager
                             │
                    ┌────────┴────────┐
                    ▼                 ▼
              contacts           failed_records.csv
              (INSERT/UPDATE)    (ActiveStorage)
                                      │
                                      ▼
                              メール通知
                              (完了/失敗)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| data_import.rb | `app/models/data_import.rb` | モデル | インポートエンティティ |
| data_import_job.rb | `app/jobs/data_import_job.rb` | ジョブ | CSV処理ジョブ |
| contact_manager.rb | `app/services/data_import/contact_manager.rb` | サービス | 連絡先構築 |
| contact.rb | `app/models/contact.rb` | モデル | 連絡先モデル |
| account_notification_mailer.rb | `app/mailers/administrator_notifications/account_notification_mailer.rb` | メーラー | 通知メール |
