# 機能設計書 39-アナウンスメントバー

## 概要

本ドキュメントは、Ghostのアナウンスメントバー機能に関する設計仕様を定義する。この機能により、サイト上部にお知らせやアナウンスメントを表示するバーを設定できる。

### 本機能の処理概要

アナウンスメントバーは、サイトのヘッダー上部に重要なお知らせを表示する機能である。表示対象（訪問者、無料会員、有料会員）を細かく制御でき、背景色も設定可能。

**業務上の目的・背景**：サイト訪問者に対して、重要なお知らせ（セール、イベント、サービス変更など）を目立つ形で表示する。表示対象を会員ステータスに応じて制御することで、ターゲットを絞った効果的なコミュニケーションが可能。

**機能の利用シーン**：
- 期間限定セールやキャンペーンの告知
- サービスメンテナンスの案内
- 新機能リリースの告知
- イベント参加の呼びかけ
- 有料会員限定の特典案内

**主要な処理内容**：
1. アナウンスメント内容の設定（HTML対応）
2. 表示対象（visibility）の設定
3. 背景色の設定
4. フロントエンドでの表示判定
5. ユーザーによる閉じるアクション対応

**関連システム・外部連携**：
- Settings API：設定の保存・取得
- Announcement Bar App：フロントエンドウィジェット
- settingsキャッシュ：設定値の高速参照

**権限による制御**：管理者（Administrator）以上のロールを持つスタッフユーザーのみが設定を編集できる。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 43 | アナウンスバー設定 | 主画面 | サイト上部のお知らせバー設定 |
| 103 | アナウンスバー | 表示画面 | サイト上部のお知らせ表示 |

## 機能種別

設定管理 / UI表示制御

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| announcement_content | String | No | お知らせ内容（HTML対応） | 文字列 |
| announcement_visibility | Array | No | 表示対象リスト | visitors, free_members, paid_members |
| announcement_background | String | No | 背景色 | accent, dark, light |

### 入力データソース

- 管理画面（Ghost Admin > Settings > Announcement bar）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| announcement | String | お知らせ内容（表示対象に該当する場合のみ） |
| announcement_background | String | 背景色設定 |

### 出力先

- settingsテーブル
- settingsキャッシュ
- フロントエンドUI（Announcement Bar App）

## 処理フロー

### 処理シーケンス

```
1. 設定保存
   └─ Settings API edit呼び出し
   └─ settingsテーブルに保存
   └─ settingsキャッシュ更新

2. フロントエンド表示判定
   └─ AnnouncementBarSettings.getAnnouncementSettings(member)呼び出し
   └─ visibilityに基づく表示判定
   └─ 該当する場合はannouncement内容を返却

3. ユーザー操作
   └─ 閉じるボタンクリック
   └─ sessionStorageに非表示フラグを保存
   └─ コンテンツが変更されたらリセット
```

### フローチャート

```mermaid
flowchart TD
    A[アナウンスメント設定] --> B{announcement_content設定あり?}
    B -->|No| C[何も表示しない]
    B -->|Yes| D{visibility設定あり?}
    D -->|No| C
    D -->|Yes| E{現在のユーザー種別?}
    E -->|訪問者| F{visitors含む?}
    E -->|無料会員| G{free_members含む?}
    E -->|有料会員| H{paid_members含む?}
    F -->|Yes| I[アナウンスメント表示]
    F -->|No| C
    G -->|Yes| I
    G -->|No| C
    H -->|Yes| I
    H -->|No| C

    J[ユーザーが閉じる] --> K[sessionStorageに保存]
    K --> L{コンテンツ変更?}
    L -->|Yes| M[再表示]
    L -->|No| N[非表示維持]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-39-01 | visibility制御 | visibilityが空の場合は誰にも表示しない | 表示判定時 |
| BR-39-02 | visitors判定 | メンバーでない場合はvisitorとして扱う | member === null/undefined |
| BR-39-03 | free_members判定 | member.status === 'free'の場合 | 表示判定時 |
| BR-39-04 | paid_members判定 | member && member.status !== 'free'の場合 | 表示判定時 |
| BR-39-05 | 閉じる状態保持 | sessionStorageで閉じた状態を保持 | ユーザー操作時 |
| BR-39-06 | コンテンツ変更で再表示 | コンテンツが変更されたら閉じた状態をリセット | ページ読み込み時 |

### 計算ロジック

visibility判定ロジック:
```javascript
if (visibilities.includes('visitors') && !member) {
    announcement = announcementContent;
} else if (visibilities.includes('free_members') && member?.status === 'free') {
    announcement = announcementContent;
} else if (visibilities.includes('paid_members') && member && member.status !== 'free') {
    announcement = announcementContent;
}
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 設定取得 | settings | SELECT | announcement_*設定の取得 |
| 設定更新 | settings | UPDATE | announcement_*設定の更新 |

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

#### settings

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | value | key IN ('announcement_content', 'announcement_visibility', 'announcement_background') | アナウンスメント設定 |
| UPDATE | value | ユーザー入力値 | 設定更新 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 403 | NoPermissionError | 権限のないユーザーが設定を変更しようとした | 管理者権限で再試行 |

### リトライ仕様

リトライは不要。

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

- 設定更新は単一レコードの更新のため、標準的なトランザクション制御

## パフォーマンス要件

- 設定値はsettingsキャッシュに保存され、高速参照可能
- フロントエンドの判定処理は軽量

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

- announcement_contentはHTMLを許可するため、管理者のみが設定可能
- XSS攻撃への注意（信頼できる管理者のみがアクセス可能な前提）

## 備考

- アナウンスメントバーは全ページで表示される
- 閉じた状態はセッション単位で保持（ブラウザを閉じるとリセット）
- 背景色はaccent（サイトのアクセントカラー）、dark、lightから選択可能

---

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

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

### 推奨読解順序

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

まず、アナウンスメント設定のデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | announcement-visibility-values.js | `ghost/core/core/server/services/announcement-bar-service/announcement-visibility-values.js` | VISITORS, FREE_MEMBERS, PAID_MEMBERSの定数定義 |

**読解のコツ**:
- visibility値は3つの定数で定義されている

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

表示判定ロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | announcement-bar-settings.js | `ghost/core/core/server/services/announcement-bar-service/announcement-bar-settings.js` | **3-54行目**: AnnouncementBarSettingsクラス、getAnnouncementSettings()メソッド |
| 2-2 | index.js | `ghost/core/core/server/services/announcement-bar-service/index.js` | **1-12行目**: サービスのインスタンス生成 |

**主要処理フロー**:
1. **22-51行目**: `getAnnouncementSettings(member)` - 表示判定ロジック
2. **35-41行目**: visibility判定（visitors, free_members, paid_members）

#### Step 3: フロントエンドUIを理解する

フロントエンドのアナウンスメントバー実装を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | announcement-bar.js | `apps/announcement-bar/src/components/announcement-bar.js` | **1-83行目**: Reactコンポーネント実装 |

**主要処理フロー**:
1. **6-7行目**: 表示状態の管理（useState, shouldShowBar）
2. **19-22行目**: 閉じるボタンのハンドラー
3. **43-58行目**: sessionStorageでの状態管理
4. **60-71行目**: コンテンツ変更検出

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

```
Settings API edit
    │
    └─ settingsテーブル UPDATE
           │
           └─ settingsキャッシュ更新
                  │
                  └─ announcement_content
                     announcement_visibility
                     announcement_background

フロントエンド表示
    │
    └─ AnnouncementBarSettings.getAnnouncementSettings(member)
           │
           ├─ settingsCache.get('announcement_content')
           │
           ├─ settingsCache.get('announcement_visibility')
           │
           └─ visibility判定
                  │
                  ├─ visitors && !member
                  ├─ free_members && member.status === 'free'
                  └─ paid_members && member.status !== 'free'
                         │
                         └─ {announcement, announcement_background}

Announcement Bar App
    │
    ├─ shouldShowBar(content) - 表示判定
    │      │
    │      ├─ isContentChanged()
    │      │      └─ sessionStorage比較
    │      │
    │      └─ getBarVisibility()
    │             └─ sessionStorage参照
    │
    └─ handleButtonClick() - 閉じる処理
           └─ setBarVisibility(false)
                  └─ sessionStorage.removeItem()
```

### データフロー図

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

管理画面入力 ───────▶ Settings API edit
                              │
                              ▼
                        settings テーブル
                              │
                              ▼
                        settingsキャッシュ
                              │
                              │
ページ表示 ──────────▶ getAnnouncementSettings(member)
                              │
                              ▼
                        visibility判定
                              │
                              ▼
                        {announcement, background}
                              │
                              ▼
                        Announcement Bar App
                              │
                              ├─▶ 表示 (shouldShowBar === true)
                              │
                              └─▶ 非表示 (閉じた状態 or 対象外)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| announcement-bar-settings.js | `ghost/core/core/server/services/announcement-bar-service/announcement-bar-settings.js` | ソース | 表示判定ロジック |
| announcement-visibility-values.js | `ghost/core/core/server/services/announcement-bar-service/announcement-visibility-values.js` | ソース | visibility定数 |
| index.js | `ghost/core/core/server/services/announcement-bar-service/index.js` | ソース | サービスエントリーポイント |
| announcement-bar.js | `apps/announcement-bar/src/components/announcement-bar.js` | ソース | Reactコンポーネント |
| app.js | `apps/announcement-bar/src/app.js` | ソース | アプリエントリーポイント |
