通知・ウォッチャー 仕様書

in-app/メール通知・ウォッチャー購読・通知設定

ステータス: Draft / 作成日: 2026-05-27 PR #5 — 依存: コア, コメント・アクティビティ


1. データモデル

1.1 task_watchers

担当者以外がタスクの変更を購読する。担当者へのアサイン時に自動登録される。

カラム 制約
task_id UUID NOT NULL, FK→tasks CASCADE
user_id UUID NOT NULL, FK→users CASCADE
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
PRIMARY KEY(task_id, user_id)

1.2 notifications

カラム 制約 説明
id UUID PK  
user_id UUID NOT NULL, FK→users CASCADE 受信者
task_id UUID NULLABLE, FK→tasks CASCADE  
notification_type VARCHAR NOT NULL 下表参照
payload JSONB NOT NULL 表示に必要なデータ
read_at TIMESTAMPTZ NULLABLE NULL = 未読
created_at TIMESTAMPTZ NOT NULL DEFAULT now()  

notification_type と通知対象:

type 通知対象 トリガー
assigned アサインされたユーザー 担当者追加
mentioned @メンションされたユーザー コメント投稿
deadline_soon 担当者 + ウォッチャー 仮締め / Deadline の 1 日前(バッチ)
status_changed ウォッチャー全員 ステータス変更
comment_added ウォッチャー全員(投稿者除く) コメント投稿
pr_merged 担当者 + ウォッチャー GitHub PR マージ
task_blocked 担当者 ブロッキング関係が追加されたとき

1.3 notification_settings

カラム 制約
user_id UUID NOT NULL, FK→users CASCADE
project_id UUID NOT NULL, FK→projects CASCADE
email_events VARCHAR[] NOT NULL DEFAULT ''
in_app_events VARCHAR[] NOT NULL DEFAULT '{assigned,mentioned,deadline_soon,comment_added,pr_merged}'
PRIMARY KEY(user_id, project_id)

2. マイグレーション

CREATE TABLE task_watchers (
    task_id UUID NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    PRIMARY KEY (task_id, user_id)
);

CREATE TABLE notifications (
    id UUID PRIMARY KEY,
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    task_id UUID REFERENCES tasks(id) ON DELETE CASCADE,
    notification_type VARCHAR NOT NULL,
    payload JSONB NOT NULL,
    read_at TIMESTAMPTZ,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE notification_settings (
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
    email_events VARCHAR[] NOT NULL DEFAULT '{}',
    in_app_events VARCHAR[] NOT NULL DEFAULT
        '{assigned,mentioned,deadline_soon,comment_added,pr_merged}',
    PRIMARY KEY (user_id, project_id)
);

CREATE INDEX idx_notifications_user_unread
    ON notifications(user_id, created_at DESC) WHERE read_at IS NULL;

3. 通知生成フロー

タスク更新イベント発生
    │
    ├─ task_watchers から対象ユーザーリストを取得
    ├─ notification_settings で各ユーザーの in_app/email 設定を確認
    ├─ in_app が有効 → notifications テーブルに INSERT
    └─ email が有効 → メール送信ジョブをキューに追加
                       (既存 verification_email ジョブを流用)

4. API

ウォッチャー

メソッド パス 説明
GET /tasks/{id}/watchers 購読者一覧
POST /tasks/{id}/watchers 自分を追加(リクエストボディ不要)
DELETE /tasks/{id}/watchers/{user_id} 解除(自分 or テナントオーナー)

通知(ユーザースコープ)

メソッド パス 説明
GET /v1/users/me/notifications 一覧(未読優先・新着順)
POST /v1/users/me/notifications/read-all 全件既読
PUT /v1/users/me/notifications/{id}/read 1 件既読

GET /v1/users/me/notifications レスポンス:

{
  "unread_count": 3,
  "notifications": [
    {
      "id": "uuid",
      "notification_type": "assigned",
      "task": { "id": "uuid", "seq_id": 42, "title": "OAuth 対応" },
      "payload": { "assigned_by": "田中", "role": "primary" },
      "read_at": null,
      "created_at": "2026-05-27T10:00:00Z"
    }
  ]
}

通知設定(プロジェクトスコープ)

メソッド パス 説明
GET /notification-settings 現在の設定取得
PUT /notification-settings 設定更新

PUT リクエスト:

{
  "email_events": ["assigned", "deadline_soon"],
  "in_app_events": ["assigned", "mentioned", "deadline_soon", "comment_added", "pr_merged"]
}

5. フロントエンド(Phase B)

通知ベル(ヘッダー)

🔔 3   ← 未読バッジ

クリックで通知ドロップダウン:

┌─────────────────────────────────────────┐
│ 通知                         [全て既読] │
├─────────────────────────────────────────┤
│ 🔵 #42 OAuth対応 に担当者追加されました  │
│    田中 → 5分前                          │
├─────────────────────────────────────────┤
│ 🔵 #38 DB設計 にコメントが投稿されました │
│    鈴木 → 1時間前                        │
├─────────────────────────────────────────┤
│ ✓  #30 ログイン の締切が明日です        │
│    → 2時間前                             │
└─────────────────────────────────────────┘

コンポーネント

コンポーネント ファイル
NotificationBell components/layout/NotificationBell.vue
NotificationDropdown components/layout/NotificationDropdown.vue
NotificationSettingsPage pages/settings/notifications/+Page.vue
WatcherList components/tasks/WatcherList.vue