# 機能設計書 28-コメント報告

## 概要

本ドキュメントは、Ghostのコメント報告機能の設計仕様を記述する。コメント報告機能は、メンバーが不適切なコメントをサイト管理者に報告し、コミュニティの健全性維持に貢献できるようにする。

### 本機能の処理概要

**業務上の目的・背景**：オープンなコメントシステムでは、スパムや攻撃的なコンテンツが投稿される可能性がある。コメント報告機能により、コミュニティメンバー自身が問題のあるコンテンツを管理者に通知でき、迅速な対応が可能になる。これにより、管理者の負担を軽減しながら、安全で建設的なコミュニティを維持できる。

**機能の利用シーン**：
- メンバーが不適切なコメントを発見して報告
- 管理者が報告されたコメントを確認
- 管理者が報告件数でコメントをフィルタリング

**主要な処理内容**：
1. コメント報告の登録（CommentReport レコード作成）
2. 管理者への報告通知メール送信
3. 報告件数のカウント表示（管理者向け）

**関連システム・外部連携**：
- Members API（報告操作）
- CommentsServiceEmails（報告通知メール）
- Admin API（報告件数表示）

**権限による制御**：
- 報告操作：認証済みメンバーのみ
- 報告件数の閲覧：管理者のみ

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 83 | コメントセクション（埋め込み） | 主機能 | 報告ボタン操作 |
| - | コメント管理画面（Admin） | 関連 | 報告件数表示・フィルタ |

## 機能種別

CRUD / 通知

## 入力仕様

### 入力パラメータ（報告）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | string | Yes | 報告対象コメントID | 存在するコメント |

### 入力データソース

- **comments テーブル**: コメント存在確認
- **comment_reports テーブル**: 重複チェック
- **members テーブル**: 報告者情報

## 出力仕様

### 出力データ（報告）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| - | - | 成功時は 204 No Content |

### 出力データ（管理者向けコメント表示時）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| count.reports | number | 報告件数 |

### 出力先

- **comment_reports テーブル**: 報告データの永続化
- **メール**: 管理者（オーナー）への報告通知

## 処理フロー

### 処理シーケンス（報告）

```
1. コメント機能の有効性確認
   └─ checkEnabled() で comments_enabled をチェック

2. 対象コメントの存在確認
   └─ Comment.findOne() でコメント取得

3. 重複チェック
   └─ comment_reports テーブルで既存レポートを検索
   └─ 既に報告済みの場合は何もせず終了（エラーにはしない）

4. 報告レコード作成
   └─ comment_reports テーブルに INSERT

5. 管理者通知メール送信
   └─ オーナーユーザーのメールアドレスに通知
```

### フローチャート

```mermaid
flowchart TD
    A[報告リクエスト] --> B{メンバー認証?}
    B -->|No| C[UnauthorizedError]
    B -->|Yes| D{コメント機能有効?}
    D -->|No| E[MethodNotAllowedError]
    D -->|Yes| F[Comment.findOne]
    F --> G{コメント存在?}
    G -->|No| H[NotFoundError]
    G -->|Yes| I{既に報告済み?}
    I -->|Yes| J[何もせず終了 (204)]
    I -->|No| K[comment_reports INSERT]
    K --> L[notifyReport メール送信]
    L --> M[204 No Content]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-28-01 | 認証必須 | 報告操作はログインメンバーのみ可能 | 報告時 |
| BR-28-02 | 重複報告許容 | 同一メンバーが同一コメントを再度報告してもエラーにしない | 報告時 |
| BR-28-03 | 自己報告許可 | 自分のコメントを報告することも可能 | 報告時 |
| BR-28-04 | オーナー通知 | 報告通知メールはサイトオーナーにのみ送信 | 報告時 |
| BR-28-05 | 報告件数閲覧制限 | count.reports は管理者にのみ表示 | コメント取得時 |

### 計算ロジック

**報告件数のカウント**：
```sql
SELECT COUNT(comment_reports.id)
FROM comment_reports
WHERE comment_reports.comment_id = comments.id
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 報告 | comment_reports | INSERT | 新規報告レコード作成 |
| 重複チェック | comment_reports | SELECT | 既存レポート検索 |
| 報告件数取得 | comment_reports | SELECT | COUNT集計 |

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

#### comment_reports

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id | 自動生成 (24文字) | ObjectID |
| INSERT | comment_id | リクエストパラメータ | 外部キー制約 |
| INSERT | member_id | ログインメンバーID | 外部キー制約、setNullDelete |
| INSERT | created_at | 現在日時 | 自動設定 |
| INSERT | updated_at | 現在日時 | 自動設定 |
| SELECT | - | comment_id AND member_id | 重複チェック |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | UnauthorizedError | メンバー未認証 | 'Unable to find member' |
| - | MethodNotAllowedError | コメント機能が無効 | 'Comments are not enabled for this site.' |
| - | NotFoundError | 対象コメントが存在しない | require: true で自動エラー |

### リトライ仕様

- メール送信失敗時はエラーログ出力のみ（報告自体は成功扱い）

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

- 報告レコード作成は単一レコード操作のため、暗黙的トランザクション
- メール送信はトランザクション外で実行

## パフォーマンス要件

- 報告操作: 即時応答
- メール送信: 非同期ではないが、失敗しても報告は成功

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

- メンバー認証による操作制限
- 報告件数は管理者にのみ表示
- 報告者のメールアドレスは報告通知メールに含まれる

## 備考

- 報告レコードにはコメント内容自体は含まれない（comment_id のみ）
- labs フラグ 'commentModeration' が有効な場合、報告通知メールにモデレーションページへのリンクが含まれる
- member_id は setNullDelete のため、メンバー削除時に報告レコードは残るが member_id は NULL になる

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | schema.js | `ghost/core/core/server/data/schema/schema.js` | comment_reports テーブル（992-998行）のスキーマ定義 |
| 1-2 | comment-report.js | `ghost/core/core/server/models/comment-report.js` | CommentReport モデルのリレーション定義 |

**読解のコツ**: comment_reports テーブルは comment_id と member_id を持つ。member_id は setNullDelete なのでメンバー削除時も報告レコードは残る。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | comments-members.js | `ghost/core/core/server/api/endpoints/comments-members.js` | report エンドポイント定義（192-205行） |
| 2-2 | comments-controller.js | `ghost/core/core/server/services/comments/comments-controller.js` | report コントローラー（344-351行） |

**主要処理フロー**:
1. **192-205行目** (endpoints): report - 報告エンドポイント
2. **344-351行目** (controller): report - コントローラー処理

#### Step 3: サービスロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | comments-service.js | `ghost/core/core/server/services/comments/comments-service.js` | reportComment メソッド（146-168行） |

**主要処理フロー**:
- **146-168行目**: reportComment() - 報告のビジネスロジック

#### Step 4: 通知メールを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | comments-service-emails.js | `ghost/core/core/server/services/comments/comments-service-emails.js` | notifyReport メソッド（146-193行） |
| 4-2 | report.hbs | `ghost/core/core/server/services/comments/email-templates/report.hbs` | メールテンプレート |

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

```
Members API
    │
    └─ comments-members.report
           │
           ├─ permissions: false (メンバー認証はコントローラーで実行)
           │
           └─ CommentsController.report(frame)
                  │
                  ├─ #checkMember() - メンバー認証確認
                  │
                  └─ CommentsService.reportComment()
                         │
                         ├─ checkEnabled()
                         │
                         ├─ Comment.findOne() - コメント存在確認
                         │
                         ├─ CommentReport.findOne() - 重複チェック
                         │      └─ 既に報告済みなら return
                         │
                         ├─ CommentReport.add() - 報告作成
                         │
                         └─ CommentsServiceEmails.notifyReport()
                                │
                                ├─ Post.findOne() - 記事情報取得
                                │
                                ├─ Member.findOne() - コメント投稿者情報
                                │
                                ├─ User.getOwnerUser() - 通知先取得
                                │
                                ├─ commentsServiceEmailRenderer.renderEmailTemplate('report')
                                │
                                └─ sendMail() - メール送信
```

### データフロー図

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

comment_id ──────────────▶ reportComment()
reporter (context)               │
                                 ├─ checkEnabled()
                                 │
                                 ├─ Comment.findOne()
                                 │
                                 ├─ CommentReport.findOne()
                                 │      └─ 重複チェック
                                 │
                                 └─ CommentReport.add() ─────▶ comment_reports INSERT
                                        │
                                        └─ notifyReport() ────▶ メール送信
                                               │
                                               ├─ Post情報
                                               ├─ コメント投稿者情報
                                               ├─ 報告者情報
                                               └─ オーナーメールアドレス

[報告件数取得フロー（管理者向け）]

Comment.findPage() ──────▶ countRelations.reports() ─────▶ count__reports
                                 │
                                 └─ COUNT(comment_reports.id)
                                        WHERE comment_id = comments.id
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| comments-members.js | `ghost/core/core/server/api/endpoints/comments-members.js` | API | Members API エンドポイント |
| comments-controller.js | `ghost/core/core/server/services/comments/comments-controller.js` | コントローラー | リクエスト処理 |
| comments-service.js | `ghost/core/core/server/services/comments/comments-service.js` | サービス | ビジネスロジック |
| comments-service-emails.js | `ghost/core/core/server/services/comments/comments-service-emails.js` | サービス | 通知メール送信 |
| comment-report.js | `ghost/core/core/server/models/comment-report.js` | モデル | CommentReportモデル定義 |
| comment.js | `ghost/core/core/server/models/comment.js` | モデル | カウントクエリ定義 |
| report.hbs | `ghost/core/core/server/services/comments/email-templates/report.hbs` | テンプレート | 報告通知メールHTML |
| report.txt.js | `ghost/core/core/server/services/comments/email-templates/report.txt.js` | テンプレート | 報告通知メールテキスト |
| schema.js | `ghost/core/core/server/data/schema/schema.js` | 設定 | DBスキーマ定義 |
