# 機能設計書 208-Slackアップロード

## 概要

本ドキュメントは、ChatwootのSlackアップロード機能について記載する。Slack連携時にChatwootに保存されたファイルをSlack内で表示するためのリダイレクト機能である。

### 本機能の処理概要

Slackアップロード機能は、Slack連携を通じて共有されたファイルや画像をSlackアプリ内で表示するためのリダイレクトコントローラーである。ActiveStorageに保存されたBlobをキーで検索し、画像の場合はリサイズされた表現（representation）を、それ以外のファイルはそのままのURLにリダイレクトする。Blobが見つからない場合は、sender_typeに応じたデフォルトアバター画像にリダイレクトする。

**業務上の目的・背景**：ChatwootとSlackを連携する際、Chatwoot側で受信したメッセージの添付ファイルやアバター画像をSlack内で表示する必要がある。しかし、ActiveStorageのURLは一時的なものであり、またSlack側のセキュリティ制約もあるため、専用のリダイレクトエンドポイントを提供している。

**機能の利用シーン**：
- Slack連携時に会話メッセージの添付画像をSlack内で表示する場面
- 連絡先やエージェントのアバターをSlack通知内で表示する場面
- ファイル添付を含むメッセージをSlackチャンネルに転送する場面

**主要な処理内容**：
1. blob_keyパラメータからActiveStorage::Blobを検索
2. Blobが存在する場合、画像かどうかを判定
3. 画像の場合はresized representation URLにリダイレクト
4. 画像以外の場合は直接URLにリダイレクト
5. Blobが存在しない場合はデフォルトアバターURLにリダイレクト

**関連システム・外部連携**：ActiveStorage、Slack連携機能。FRONTEND_URL環境変数からアバターのベースURLを取得。

**権限による制御**：本機能は認証なしで公開されるエンドポイントである。blob_keyを知っている場合のみファイルにアクセス可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | Slack通知 | 表示先 | 画像・ファイルの表示 |

## 機能種別

リダイレクト処理 / ファイル配信

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| blob_key | String | Yes | ActiveStorage Blobのキー | - |
| sender_type | String | No | 送信者タイプ（アバターフォールバック用） | - |

### 入力データソース

- URLパラメータ（blob_key、sender_type）
- ActiveStorage::Blobテーブル

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| リダイレクト先URL | String | ファイルまたはアバターのURL |

### 出力先

- HTTPリダイレクトレスポンス（302）

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信
   └─ GET /slack_uploads/:blob_key
2. Blob検索
   └─ ActiveStorage::Blob.find_by(key: blob_key)
3. 条件分岐
   ├─ Blobあり → blob_url生成
   │      ├─ 画像 → representation URL（250pxリサイズ）
   │      └─ 非画像 → 直接URL
   └─ Blobなし → avatar_url生成
          └─ デフォルトアバター画像
4. リダイレクト
   └─ redirect_to URL
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B{Blob存在?}
    B -->|Yes| C{画像?}
    C -->|Yes| D[representation URL生成]
    C -->|No| E[直接URL生成]
    B -->|No| F[avatar URL生成]
    D --> G[リダイレクト]
    E --> G
    F --> G
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 画像リサイズ | 画像は幅250pxにリサイズ | content_typeがimage/*の場合 |
| BR-002 | フォールバック | Blobが存在しない場合はデフォルトアバターを返す | Blob検索失敗時 |
| BR-003 | sender_type別アバター | sender_typeに応じたアバター画像を選択 | フォールバック時 |

### 計算ロジック

画像リサイズロジック：
```ruby
def blob_url
  if @blob.content_type.start_with?('image/')
    url_for(@blob.representation(resize_to_fill: [250, nil]))
  else
    url_for(@blob)
  end
end
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| Blob検索 | active_storage_blobs | SELECT | キーでBlob検索 |

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

#### active_storage_blobs

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | key = params[:blob_key] | find_by使用 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | Blobなし | 指定キーのBlobが存在しない | デフォルトアバターにリダイレクト |

### リトライ仕様

本機能にリトライ処理は実装されていない。

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

参照のみのためトランザクション制御は不要。

## パフォーマンス要件

- リダイレクト処理のため軽量
- 画像リサイズはActiveStorageのバリアントシステムで処理

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

- 認証なしの公開エンドポイント
- blob_keyを推測困難にすることでセキュリティを確保
- 直接ファイルを配信せずリダイレクトで対応
- FRONTEND_URL環境変数を使用してアバターURLを構築

## 備考

- Rails.application.routes.url_helpersをinclude
- ActiveStorage representationでオンデマンドリサイズ
- resize_to_fillで幅250px、高さは自動

---

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

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

### 推奨読解順序

#### Step 1: コントローラーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | slack_uploads_controller.rb | `app/controllers/slack_uploads_controller.rb` | showアクションとリダイレクトロジック |

**読解のコツ**: シンプルなコントローラー。showアクションがエントリーポイントで、条件分岐でリダイレクト先を決定。

**主要処理フロー**:
1. **3行目**: before_action :set_blobでBlobを検索
2. **5-11行目**: showアクションでリダイレクト先を決定
3. **15-17行目**: set_blobでActiveStorage::Blob.find_byを実行
4. **19-26行目**: blob_urlで画像/非画像を判定してURL生成
5. **28-31行目**: avatar_urlでデフォルトアバターURL生成

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

```
SlackUploadsController#show
    │
    ├─ before_action :set_blob
    │      └─ ActiveStorage::Blob.find_by(key: params[:blob_key])
    │
    └─ @blob存在?
           ├─ Yes → blob_url
           │         ├─ content_type.start_with?('image/')
           │         │      └─ Yes → @blob.representation(resize_to_fill: [250, nil])
           │         │      └─ No → url_for(@blob)
           │         └─ redirect_to
           │
           └─ No → avatar_url
                    └─ "#{FRONTEND_URL}/integrations/slack/#{sender_type}.png"
                         └─ redirect_to
```

### データフロー図

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

params[:blob_key] ──▶ SlackUploadsController ──▶ 302 Redirect
params[:sender_type]       │
                      ┌────┴────┐
                      ▼         ▼
               Blob検索    URL生成
                      │         │
                      └────┬────┘
                           ▼
                      redirect_to
                           │
                      ┌────┴────┐
                      ▼         ▼
              ActiveStorage  デフォルトアバター
              URL            URL
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| slack_uploads_controller.rb | `app/controllers/slack_uploads_controller.rb` | コントローラー | リダイレクト処理 |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義 |
