# バッチ設計書 4-ffcrm:comment_replies:run

## 概要

本ドキュメントは、Fat Free CRMにおけるコメント返信メール処理バッチ（ffcrm:comment_replies:run）の設計について記載する。

### 本バッチの処理概要

このバッチは、IMAPメールサーバーに接続し、受信メールの件名に含まれるエンティティ識別子（[entity:id]形式）を解析して、対応するCRMエンティティにコメントを自動追加する処理を行う。メールでの返信によりCRMシステム内のコメント機能を利用できるようになる。

**業務上の目的・背景**：CRMシステムからの通知メールに対して、メールクライアントから直接返信することで、CRM内にコメントを追加できる。これにより、ユーザーはCRMにログインすることなく、メールワークフローの中でコメント登録が可能となる。外出先や移動中でも迅速なコメント追加ができ、業務効率が向上する。

**バッチの実行タイミング**：定期実行（cron等で設定）または手動実行。メール返信の頻度に応じて、数分〜数時間間隔での定期実行が推奨される。

**主要な処理内容**：
1. IMAPサーバーへの接続とログイン
2. 指定フォルダ内の未読メール（NOT SEEN）の検索
3. 各メールの送信者がシステム内の有効なユーザーかを検証
4. メール件名から[entity:id]形式のエンティティ識別子を抽出
5. 対応するエンティティにCommentレコードを作成
6. 処理済みメールのアーカイブまたは破棄

**前後の処理との関連**：本バッチ実行前に、ffcrm:comment_replies:setupによるIMAPフォルダのセットアップが完了している必要がある。Setting.email_comment_repliesに有効な設定が登録されていることが前提。

**影響範囲**：commentsテーブルへのデータ追加が行われる。IMAPサーバー上のメールフラグも変更される。

## バッチ種別

データ連携 / メール処理

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | 随時（cron設定による定期実行推奨） |
| 実行時刻 | 任意 |
| 実行曜日 | 該当なし |
| 実行日 | 該当なし |
| トリガー | cron/手動 |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| IMAP設定完了 | Setting.email_comment_repliesに有効な接続情報が設定されていること |
| フォルダ存在 | scan_folder、move_to_folder、move_invalid_to_folderが存在すること |
| Rails環境 | Railsアプリケーション環境がロードされていること |

### 実行可否判定

IMAPサーバーへの接続が成功した場合のみ処理を継続。

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| なし | - | - | - | Rakeタスクとして引数なしで実行 |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| IMAPメールサーバー | IMAP | 指定フォルダ内の未読メールを取得 |
| settings (Setting.email_comment_replies) | DB | IMAP接続情報、フォルダ設定を取得 |
| users | DB | 送信者の認証に使用 |
| accounts, campaigns, contacts, leads, opportunities, tasks | DB | コメント対象エンティティの検索 |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| comments | DB | メール本文をCommentとして保存 |
| IMAPサーバー | IMAP | メールのフラグ変更（Seen、Deleted） |

### 出力ファイル仕様

ファイル出力なし（データベースおよびIMAPサーバーへの出力のみ）

## 処理フロー

### 処理シーケンス

```
1. CommentRepliesインスタンス生成
   └─ Setting.email_comment_repliesから設定を読み込み
2. IMAPサーバー接続
   └─ @settings[:server]、[:port]、[:ssl]で接続
   └─ @settings[:user]、[:password]でログイン
3. scan_folderを選択
   └─ @imap.select(@settings[:scan_folder])
4. 未読メール検索
   └─ @imap.uid_search(['NOT', 'SEEN'])
5. 各メールに対して処理
   └─ メール取得（RFC822形式）
   └─ 有効性チェック（text/html以外）
   └─ 送信者チェック（Userテーブルに存在確認）
   └─ 件名から[entity:id]を抽出
   └─ エンティティにCommentを作成
   └─ アーカイブ処理
6. 接続終了
   └─ logout、disconnect
```

### フローチャート

```mermaid
flowchart TD
    A[バッチ開始] --> B[CommentRepliesインスタンス生成]
    B --> C[IMAPサーバー接続]
    C --> D{接続成功?}
    D -->|失敗| E[処理中断]
    D -->|成功| F[未読メール検索]
    F --> G{メールあり?}
    G -->|なし| H[処理終了]
    G -->|あり| I[メール取得]
    I --> J{有効なメール?}
    J -->|無効| K[破棄処理]
    J -->|有効| L{送信者確認}
    L -->|不明| K
    L -->|確認済| M[件名解析]
    M --> N{entity:id検出?}
    N -->|なし| K
    N -->|あり| O{エンティティ存在?}
    O -->|なし| K
    O -->|あり| P{権限確認}
    P -->|なし| K
    P -->|あり| Q[Comment作成]
    Q --> R[アーカイブ処理]
    R --> S{次のメール?}
    K --> S
    S -->|あり| I
    S -->|なし| H
    H --> T[ログ出力]
    T --> U[切断]
    U --> V[バッチ終了]
    E --> V
```

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

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

| 処理 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 送信者検索 | users | SELECT | emailまたはalt_emailで検索 |
| エンティティ検索 | accounts, campaigns, contacts, leads, opportunities, tasks | SELECT | IDで検索 |
| コメント作成 | comments | INSERT | メール本文をコメントとして保存 |

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

#### comments

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | user_id | @sender.id | 送信者のユーザーID |
| INSERT | commentable_type | entity_name.capitalize | エンティティの型（ポリモーフィック） |
| INSERT | commentable_id | entity_id | エンティティのID |
| INSERT | comment | parsed_reply | EmailReplyParserで解析した本文 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | IMAP接続エラー | サーバー接続失敗 | 警告ログを出力し処理中断 |
| - | メール処理エラー | 個別メール処理失敗 | 該当メールを破棄し次を処理 |
| - | エンティティ不存在 | 指定IDのエンティティなし | メールを破棄 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし |
| リトライ間隔 | - |
| リトライ対象エラー | - |

### 障害時対応

1. IMAPサーバー接続エラーの場合、接続設定を見直す
2. エンティティ不存在の場合、メールがmove_invalid_to_folderに移動されるので確認

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

| 項目 | 内容 |
|-----|------|
| トランザクション範囲 | メール単位 |
| コミットタイミング | Comment.create時に自動コミット |
| ロールバック条件 | 個別メール処理失敗時 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | 数件〜数百件/実行 |
| 目標処理時間 | メール件数依存 |
| メモリ使用量上限 | 特に制限なし |

## 排他制御

同一バッチの同時実行は推奨されない。

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| 開始ログ | IMAP接続時 | "connecting & logging in to {server}..." |
| 進捗ログ | 各メール処理時 | "fetched new message...", From/Subject/message_id |
| 終了ログ | バッチ終了時 | 処理件数サマリー |
| エラーログ | エラー発生時 | エラー詳細とバックトレース |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| 処理時間 | 設定なし | - |
| エラー件数 | 設定なし | - |

## 備考

- 件名の[entity:id]形式は以下のパターンをサポート：
  - フルネーム: [account:123], [campaign:456], [contact:789], [lead:012], [opportunity:345], [task:678]
  - ショートカット: [ac:123], [ca:456], [co:789], [le:012], [op:345], [ta:678]
- メール本文はEmailReplyParserで解析され、引用部分が除去されたテキストがコメントとして保存される
- 送信者にエンティティへのアクセス権限がない場合、コメントは作成されない
