#898 プレイリストAPI設計レビュー

GitHub Issue — 設計者: 本田さん — レビュー日: 2026-03-18

レビュー指摘事項

削除方針

確定 プレイリスト本体 (vod_playlists) は論理削除
他ユーザーがお気に入りしているプレイリストを作成者が削除しても、お気に入り側からは引き続きアクセス可能にする。取得時に is_deleted = false でフィルタ。favorite_vod_playlists のレコードはそのまま残す(カスケード削除しない)。
確定 vod_playlist_items も論理削除で統一
既存パターンでは中間テーブルは物理削除だが、ディスカッションの結果、プレイリスト関連は紐付け情報を残す方針で論理削除に統一。再生履歴・人気順スコアとの整合性を保つため。
確定 お気に入り解除 (favorite_vod_playlists) は物理削除
ユーザーがお気に入りを解除する操作は物理DELETE。既存の favorite_lessons と同じパターン。プレイリスト本体の削除とは別の話。

レスポンス設計

確定 レスポンスから is_deleted を除外する
内部フラグをクライアントに返す必要はない。既存の favorite_lessons でも返していない。
確定 is_public は boolean (true/false) に統一
現在の設計では 0/1 だが、既存コードベースは boolean 型で統一されている。
要相談 レスポンスに vod_lesson の詳細情報を含めるか
現状 vod_playlist_items は vod_lesson_id のみ返却。詳細画面ではレッスン名・サムネイル・再生時間が必要。JOIN して返すか、クライアント側で別途取得するか方針決めが必要。
要相談 単体取得APIにお気に入り状態・is_ownerフラグを含める
詳細画面で「...」ボタンの出し分け(追加/削除/非表示)を判断するために必要。リクエストユーザーがお気に入り済みか、作成者本人かをレスポンスに含めるべき。

検索API

重要 検索APIが実質未設計(コピペ状態)
リクエスト/レスポンス/ロジックが作成APIのコピーのまま。最も優先度の高い修正事項。
確定 POST vod-playlists/search に変更
既存の VOD レッスン検索 (POST vod-lessons/search) に合わせる。GETクエリパラメータではなくPOSTボディで検索条件を渡す。
確定 ページネーション (page / page_size) が必須
既存パターンに合わせる。page は 0-indexed、page_size は min 10。
要相談 人気順スコア集計方式
vod_playlist_weekly_scores テーブル + バッチ処理(1時間間隔程度)を提案。既存の vod_lesson_weekly_scores と同じパターン。連続再生ポイント加算式、直近7日間で集計。

その他の指摘

確定 編集APIの「全削除→全挿入」方式はOK
シンプルで良い。差分更新より実装・テストが容易。
確定 プレイリスト名の変更は可能にする
#745 の「名称変更不要」は古い記載。編集APIで name を変更可能にする。
確定 自己お気に入り防止のAPIガードは不要
フロント側で「...」ボタンを非表示にするだけで十分。APIレベルのバリデーションは不要。
確定 お気に入り重複登録防止: UNIQUEインデックス必要
favorite_vod_playlists に (user_id, vod_playlist_id) の UNIQUEインデックスを貼る。
要相談 再生結果記録APIのURLネストが深い
4階層のネストは既存パターンにない。POST users/{user_id}/vod-playlists/{id}/results に簡略化し、vod_playlist_item_id はリクエストボディに含める案。
軽微 typo: hitrory → history
user_lesson_hitrory_id → user_lesson_history_id に修正。複数箇所。
軽微 HTTPメソッド誤記: Deleted → DELETE
削除APIのエンドポイント記載が「Deleted」になっている。
追加提案 マイプレイリスト一覧API (GET users/{user_id}/vod-playlists)
HOME画面のプレイリストセクション表示用に、自分のプレイリスト+お気に入りプレイリストを返す専用エンドポイントがあると便利。

テーブル設計・ER図

vod_playlists
idBIGINT AUTO_INCREMENT
user_idVARCHAR(28)
nameVARCHAR(32) NOT NULL
image_urlTEXT NULL
is_publicBOOLEAN DEFAULT false
is_deletedBOOLEAN DEFAULT false
created_atTIMESTAMP
updated_atTIMESTAMP
vod_playlist_items
idBIGINT AUTO_INCREMENT
vod_playlist_idBIGINT
vod_lesson_idBIGINT
sort_orderINT NOT NULL
is_deletedBOOLEAN DEFAULT false
created_atTIMESTAMP
updated_atTIMESTAMP
favorite_vod_playlists
idBIGINT AUTO_INCREMENT
user_idVARCHAR(28)
vod_playlist_idBIGINT
created_atTIMESTAMP
updated_atTIMESTAMP
vod_playlist_play_results
idBIGINT AUTO_INCREMENT
user_idVARCHAR(28)
vod_playlist_idBIGINT
vod_playlist_item_idBIGINT
user_lesson_history_idBIGINT
created_atTIMESTAMP
updated_atTIMESTAMP
NEW vod_playlist_weekly_scores
idBIGINT AUTO_INCREMENT
vod_playlist_idBIGINT
starting_dateDATE NOT NULL
scoreINT DEFAULT 0
rankINT NULL
created_atTIMESTAMP
updated_atTIMESTAMP
users (既存)
idVARCHAR(28)
...

リレーション

users1 ──── Nvod_playlists1人のユーザーが複数プレイリストを作成
vod_playlists1 ──── Nvod_playlist_items1プレイリストに複数レッスン (max 15)
vod_lessons1 ──── Nvod_playlist_items1レッスンが複数プレイリストに含まれる
users1 ──── Nfavorite_vod_playlists1人が複数プレイリストをお気に入り
vod_playlists1 ──── Nfavorite_vod_playlists1プレイリストが複数ユーザーにお気に入りされる
vod_playlists1 ──── Nvod_playlist_play_results再生結果の記録
user_lesson_histories1 ──── 1vod_playlist_play_results各再生結果がレッスン履歴と1:1対応
vod_playlists1 ──── Nvod_playlist_weekly_scores週次スコア集計 (バッチ処理)

スコア集計ロジック(バッチ処理)

vod_playlist_play_results テーブルの直近7日間のデータを集計し、vod_playlist_weekly_scores に書き込む。 プレイリストごとに、1回の再生セッション内で連続再生されたコンテンツ数に応じてポイントを加算する。
+1
2コンテンツ目まで連続再生
+2
3コンテンツ目まで連続再生
---
4コンテンツ目以降は評価しない
実行間隔: 1時間ごと(リアルタイムに近い反映) | 測定期間: 直近7日間 | 既存の vod_lesson_weekly_scores と同パターン

APIエンドポイント一覧

# Method パス 概要 状態
1 POST users/{user_id}/vod-playlists プレイリスト作成 (name, image_url, is_public, vod_lesson_ids) 要修正
2 PUT users/{user_id}/vod-playlists/{id} プレイリスト編集 (全置換方式) 要修正
3 DELETE users/{user_id}/vod-playlists/{id} プレイリスト削除 (論理削除: is_deleted=true) 要修正
4 POST users/{user_id}/vod-playlists/{id}/favorite お気に入り登録 要修正
5 DELETE users/{user_id}/vod-playlists/{id}/favorite お気に入り解除 (物理削除) OK
6 POST vod-playlists/search プレイリスト検索 (keyword, order_by, pagination) 未設計
7 POST users/{user_id}/vod-playlists/{id}/results 再生結果記録 (vod_playlist_item_id, user_lesson_history_id) 要修正
8 GET vod-playlists/{id} プレイリスト単体取得 (is_favorited, is_owner フラグ含む) 要修正
9 GET users/{user_id}/vod-playlists マイプレイリスト一覧 (自作 + お気に入り) 追加提案

各APIの修正ポイント

1,2,3,8レスポンス共通
is_deleted 除外、is_public を boolean 化、vod_lesson の詳細情報を含めるか決定
3削除API
HTTPメソッド誤記修正 (Deleted → DELETE)。items の論理削除は維持。favorites はそのまま残す。
4お気に入り登録
UNIQUE制約 (user_id, vod_playlist_id) の追加。重複時のエラーハンドリング記載。
6検索API(最重要)
コピペ状態を解消。POST メソッド化、ページネーション、ソート (popularity/newest)、キーワード検索の設計が必要。
7再生結果記録
URL簡略化提案、typo修正 (hitrory → history)、人気順スコアとの接続設計。
8単体取得
「不要かも」→ 必要。is_favorited / is_owner フラグ追加。

ユーザーストーリー & API利用フロー

1マイプレイリストを作成する
HOME画面
MY PLAYLIST → MORE
マイプレイリスト一覧
GET users/{id}/vod-playlists
NEW PLAYLIST押下
プレイリスト作成画面へ
レッスン選択
カテゴリ → レッスン選択
(1〜15件)
サムネイル・タイトル入力
名前(max32字)、公開設定
「完了」押下
POST users/{id}/vod-playlists
保存完了画面
「マイプレイリストへ」のみ
2公開プレイリストを探してお気に入りする
HOME画面
PLAYLIST → MORE
公開プレイリスト一覧
POST vod-playlists/search
★要設計
プレイリスト選択
プレイリスト詳細
GET vod-playlists/{id}
is_favorited / is_owner 表示
「...」ボタン押下
他ユーザーのPL → 追加モーダル
自分のPL → 非表示
「追加」押下
POST users/{id}/vod-playlists/{id}/favorite
3プレイリストを再生する
プレイリスト詳細
START押下
VOD 1 再生
POST :id/lessons/:lid/results
既存レッスン結果API
リザルト画面
POST users/{id}/vod-playlists/{id}/results
PL再生結果記録
10秒タイマー
STOP → 一時停止
START → 再開
VOD 2 再生
···
VOD N 再生
GREAT WORKOUT
総再生時間・総カロリー
BPM(不要)
4マイプレイリストを編集・削除する
マイプレイリスト一覧
GET users/{id}/vod-playlists
3点ボタン押下
「編集」or「削除」モーダル
「追加」(不要)
編集を選択
作成と同じ動線
→ 全置換方式で保存 PUT users/{id}/vod-playlists/{id}
削除を選択
論理削除 (is_deleted=true)
お気に入り側は維持 DELETE users/{id}/vod-playlists/{id}
5人気順でプレイリストを一覧表示する
バッチ処理 (1h間隔)
play_results から
直近7日間を集計
weekly_scores 更新
連続再生ポイント加算式
2本目:+1 / 3本目:+2
ユーザーが一覧表示
並び替え: 人気順 選択
検索API実行
POST vod-playlists/search
order_by=popularity
★要設計
結果表示
weekly_scores を JOIN
score DESC でソート
レビュー日: 2026-03-18  |  Issue #898  |  親Issue #745