events テーブル定義書
最終更新: 2026-08-30
関連 Issue: #926(eventsテーブル実装), #989(カラム名修正), #997・#998(タイムラインUI表示仕様追加), #1070(imageUrl管理方針追加)【v5.0.0 タイムライン機能】 / #1108(V2設計書整備), #1109(festival統合), #1110(タグシステム)【v7.0.0 タイムライン機能拡張(V2)】 / #1381(イベントテーブル拡張・time カラム追加) / #1472(タイムライン改善: 同日順序・プレースホルダー全文表示・ツアー時刻二重表示解消) / #1551(TO_CHAR()日付比較クエリのunstable_cache化) / MorningStatusApp #1528(イベント登録における属性拡張), morning-status-blume#67(手動イベント登録の会場・楽曲属性設計。venueIdカラム・event_songs/event_song_tracksテーブル追加)
1. 目的
既存データ(Releases / Members / Lives / Radio / Manual Events)を時系列で横断的に扱うための統合イベントレイヤーを構築する。
- 既存データは変更しない
- events は「派生データ」として生成する
- タイムライン UI は events のみを参照する
2. テーブル定義
テーブル名
events
スキーマ
| カラム | 型 | NOT NULL | デフォルト | 説明 |
|---|---|---|---|---|
id |
UUID | ✅ | —(Prisma がアプリ層で UUID 生成) | PK |
date |
DATE | ✅ | — | イベント発生日 |
time |
TIME(0) | — | NULL | イベント開始時刻。lives.time と同じ表現方式(秒精度なし)。時刻不明・未登録時は NULL |
type |
TEXT | ✅ | — | イベント種別(後述) |
title |
TEXT | ✅ | — | イベントタイトル |
description |
TEXT | — | NULL | 補足説明 |
members |
TEXT[] | — | '{}' |
関連メンバー ID 配列(members.json の id と対応) |
tags |
TEXT[] | — | '{}' |
タグ配列(後述) |
imageUrl |
TEXT | — | NULL | 表示画像の URL(Vercel Blob URL 等) |
link |
TEXT | — | NULL | 関連リンク(MV / ニュース / radiko 等) |
venueId |
VARCHAR(8) | — | NULL | 会場ID(venues.venue_id 参照、FK制約あり)。手動イベント(source='manual')で venueName を指定・解決できた場合のみ設定する(§5-5参照)。live 型等、他の source では現状未使用(NULL のまま)。オンライン開催等、物理会場を持たないイベントもあるため任意項目 |
source |
TEXT | ✅ | — | 元データ種別(後述) |
sourceId |
TEXT | ✅ | — | 元データの ID |
createdAt |
TIMESTAMPTZ | ✅ | CURRENT_TIMESTAMP |
作成日時 |
updatedAt |
TIMESTAMPTZ | ✅ | —(Prisma の @updatedAt で管理) |
更新日時(更新時に自動更新) |
2a. event_songs(イベント楽曲)
手動イベントの songs 入力(seq・songTitle)を保持する。radio_onair_songs・setlists と同じ「曲エンティティ自体のテーブル + トラック紐付け中間テーブル」の2階層構成を踏襲する(詳細はラジオオンエア データ設計書 §4-0参照)。イベント(Live・Festival)と同格に、曲情報を独立したレコードとして持たせる方針とする。
CREATE TABLE event_songs (
event_song_id SERIAL PRIMARY KEY,
event_id UUID NOT NULL REFERENCES events(id) ON DELETE CASCADE,
seq INTEGER NOT NULL,
song_title TEXT NOT NULL,
UNIQUE (event_id, seq)
);
| カラム名 | 型 | NOT NULL | 説明 |
|---|---|---|---|
event_song_id |
SERIAL | ✅ | PK。radio_onair_songs.onairId と同じ代理キー方式(events.id が UUID のため、setlists.setlistId のような文字列組み立て PK ではなく独立した連番キーを持たせる) |
event_id |
UUID | ✅ | FK → events(id)。イベント削除時に連動削除(CASCADE) |
seq |
INTEGER | ✅ | イベント内での曲順(1始まり)。入力 JSON の seq をそのまま格納 |
song_title |
TEXT | ✅ | 曲名。入力 JSON の songTitle をそのまま格納 |
インデックス:
CREATE INDEX idx_event_songs_event_id ON event_songs (event_id);
備考:
(event_id, seq)の一意制約により、同一イベント内でのseq重複を防ぐ- トラックID への紐付けは本テーブルの列では持たず、§2b「event_song_tracks」の中間テーブルで管理する(1曲が複数トラック(CD盤・配信版シングル等)に正しく該当しうるため。ラジオオンエア・セットリストと同じ方針)
- 曲名からトラックが1件も解決できなかった場合も、
event_songsには曲名・seqが残る(event_song_tracksへの行のみ作成されない)。これはラジオオンエア・セットリストの「不明な曲」パターンと同じ考え方であり、将来イベントタイムライン UI に曲情報を表示する際の受け皿になる。ただし本書の時点ではイベントタイムライン UI への曲情報表示自体は対象外(画面設計は別途検討)
2b. event_song_tracks(イベント楽曲 × トラック紐付け)
CREATE TABLE event_song_tracks (
event_song_id INTEGER NOT NULL REFERENCES event_songs(event_song_id) ON DELETE CASCADE,
track_id VARCHAR(255) NOT NULL REFERENCES tracks(track_id),
PRIMARY KEY (event_song_id, track_id)
);
CREATE INDEX idx_event_song_tracks_track_id ON event_song_tracks (track_id);
| カラム名 | 型 | NOT NULL | 説明 |
|---|---|---|---|
event_song_id |
INTEGER | ✅ | FK → event_songs(event_song_id)。複合 PK の一部。曲削除時に連動削除(CASCADE) |
track_id |
VARCHAR(255) | ✅ | FK → tracks(track_id)。複合 PK の一部 |
セットリスト側にも同じパターンの中間テーブル(setlist_tracks)があり、詳細はライブ・フェス機能 テーブル設計書 §2-9を参照。
3. type(イベント種別)
| 値 | 説明 | 元データ | V1/V2 |
|---|---|---|---|
release |
シングル・アルバム発売 | releases | V1 |
live |
ライブ・ツアー | lives | V1 |
join |
メンバー加入 | members.json | V1 |
graduate |
メンバー卒業 | members.json | V1 |
media |
ラジオ・TV 出演 | radio_episodes | V1 |
topic |
その他の話題・発表 | manual | V1 |
festival |
外部フェス出演 | festivals | V2追加予定 |
festival type の仕様(V2)
festivalsテーブルからの派生データsource = 'festival'、sourceId = festival.festivalIdmembersフィールドは空配列(メンバー×フェス紐づけテーブルが存在しないため)- フェスが複数日にまたがる場合も
dateEndは持たない(セクション 3b 参照)。V2 時点では開始日のみ表示とし、期間表示が必要になった際に改めて検討する
3a. tags(タグ)仕様
tags フィールドは type とは別軸のイベント性質を表すラベル配列。フィルタリングやUIでの表示に使用する。
| タグ値 | 説明 | 設定対象 type の例 | 付与方法 |
|---|---|---|---|
OG |
OG メンバーが関与するイベント | 全 type | 自動(assignTags) |
Birthday |
誕生日関連イベント | 全 type | 自動(assignTags) |
Solo |
メンバーのソロ活動 | topic, live, media |
手動(manual-events.json) |
Unit |
ユニット活動 | release, live, topic |
手動(manual-events.json) |
Special |
記念・特別イベント | live, topic |
手動(manual-events.json) |
Collaboration |
他アーティストとのコラボ | release, topic |
手動(manual-events.json) |
- タグは任意付与。付与なし(空配列)も有効
- 1イベントに複数タグを付与可能
- 自動付与タグ(
OG・Birthday)はsync-events.tsのassignTags()関数で付与されるOG: イベントに関与するメンバーのうち1名以上がstatus='OG'Birthday: イベント日の UTC 月/日がいずれかの関与メンバーのprofile.dobと一致- 全データソース(Releases / Members / Lives / Radio / Festivals / Manual)に適用される
data/inputs/manual-events.jsonのtagsフィールドで任意タグを明示指定できるtagsを省略した場合 →assignTags()が OG・Birthday を自動付与tagsを明示指定した場合([]を含む)→ 明示値を優先し、assignTags()は適用されない
3b. dateEnd フィールドの要否
dateEnd(終了日)フィールドは当初 V2 設計として検討されたが、追加しないと判断した。
理由:
live型はツアーの各公演を1レコードずつ持つ設計のため、期間を持つ概念がないfestival型の元データ(festivalsテーブル)は単日記録であり、終了日のデータソースがない- タイムライン UI での期間表示が必要になった時点で、データ供給方法と合わせて改めて検討する
4. source / source_id の意味
events は既存データの派生であるため、どのデータから生成されたかを保持する。
| source 値 | sourceId の意味 | 例 |
|---|---|---|
release |
Release.releaseId | "release_001" |
member |
Member.memberId(members.json の id) | "nakazawa-yuko" |
live |
Live.liveId | "2026B001" |
radio |
RadioEpisode.episodeId | "morning-musume-radio-20240517" |
manual |
任意(手動入力時に付与) | "topic-2024-01" |
festival |
Festival.festivalId | "summer-sonic-2025" |
5. 変換ロジック仕様
変換レイヤーの実装は #927 で行う。本書には入力元フィールドのマッピング仕様のみを記載する。
5-1. Releases → events(release)
| events カラム | 元フィールド |
|---|---|
date |
release.releaseDate |
type |
"release" |
title |
release.title |
members |
releases.json のメンバー紐づけ |
imageUrl |
Cover Art Archive から取得したジャケット画像 URL(後述) |
link |
releases.json の MV URL |
source |
"release" |
sourceId |
release.releaseId(= MusicBrainz リリースグループ MBID) |
5-2. Members → events(join / graduate)
加入(join)
| events カラム | 元フィールド |
|---|---|
date |
member.joined(members.json) |
type |
"join" |
title |
"{期}期加入" |
members |
[member.id] |
imageUrl |
member.image(members.json) |
source |
"member" |
sourceId |
member.id |
卒業(graduate)
| events カラム | 元フィールド |
|---|---|
date |
member.graduated(members.json) |
type |
"graduate" |
title |
"{name} 卒業" |
members |
[member.id] |
imageUrl |
member.image(members.json) |
source |
"member" |
sourceId |
member.id |
5-3. Lives → events(live)
| events カラム | 元フィールド |
|---|---|
date |
live.date |
time |
live.time(そのままコピー) |
type |
"live" |
title |
live.title(または tour.title) |
members |
MemberLive 経由の memberId 配列 |
source |
"live" |
sourceId |
live.liveId |
5-4. Radio → events(media)
| events カラム | 元フィールド |
|---|---|
date |
radioEpisode.date |
type |
"media" |
title |
radioShow.name |
members |
RadioMember 経由の memberId 配列 |
source |
"radio" |
sourceId |
radioEpisode.episodeId |
5-5. Manual → events(topic)
全フィールド手動入力。source = "manual"、source_id は任意の識別子を付与する。
data/inputs/manual-events.json の time フィールド("HH:MM" 形式の文字列、任意)を指定すると、time カラムに変換される。省略時は既存値を保持する(imageUrl と同じ undefined/null の使い分け。#1381)。
会場(venueName → venueId)
data/inputs/manual-events.json に任意項目 venueName(会場名の文字列)を指定すると、events.venueId に変換される。物理会場を持たないイベント(オンライン開催等)も扱えるよう venueName は省略可能とする。venueName を省略した場合、既存の venueId を保持する(time と同じ undefined 時保持パターン)。
解決手順は register-tour-festival.ts(ライブ・フェス機能 テーブル設計書)の会場登録ロジックをそのまま流用する。
- 既存
venues.nameと完全一致する会場があれば、そのvenueIdを再利用する - 一致しない場合、Google Maps Geocoding API(環境変数
GEOCODING_API_KEY)でvenueNameをそのままクエリとして緯度経度・都道府県名を取得する(region=jpに加えlanguage=jaを指定し、都道府県名の表記言語を日本語に固定する。#1520) - 都道府県コードへの変換に失敗した場合、または API 呼び出し自体が失敗した場合は、当該会場名を警告ログに出力し、そのイベントの
venueIdは更新しない(既存値があればそれを保持、なければ NULL のまま。他のイベントの処理は継続する。グレースフルな失敗処理) - 新規解決できた会場は都道府県コード + 連番3桁で
venueIdを新規採番し、venuesへ登録する
GEOCODING_API_KEY が未設定の場合、会場解決自体をスキップする(venueName 指定イベントは venueId を更新せず同期される。Releases / Lives / Radio 等、会場を伴わない他のイベント同期には影響しない)。
本仕様は会場の「登録・変更」のみを扱う。venueName に明示的な「未設定に戻す」値(null 等)は用意しない。一度 venueId が設定されたイベントについて、venueName を省略しても venueId は変更されない(前述の undefined 時保持)ため、会場を後から解除したい場合は本仕様の対象外(必要になった時点で別途検討する)。
楽曲(songs → event_songs / event_song_tracks)
data/inputs/manual-events.json に任意項目 songs(SongEntrySchema の配列。scripts/lib/song-entry-schema.ts、seq・songTitle を持つ。ラジオオンエア・セットリストの入力と共通のスキーマ。詳細はラジオオンエア データ設計書 §4-0参照)を指定すると、§2a「event_songs」・§2b「event_song_tracks」に変換される。
同期は seq をキーにした差分方式。 events 本体が source+sourceId をキーに差分upsert(upsertEvent()。既存レコードは id を保持したままUPDATE、新規のみINSERT)する既存設計と対称に揃える。event_songs.event_id は events(id) への FK のため、event_songs/event_song_tracks への書き込みは upsertEvent() で対象イベントの id が確定した後に行う(新規の手動イベントは events 行の INSERT より前に曲を書き込むことはできない。処理順序の詳細はバッチ設計書 W6参照)。songs を省略した場合、既存の event_songs/event_song_tracks を保持する(変更しない)。songs を明示指定した場合([] を含む)は、そのイベントの既存 event_songs(seq で識別)と入力 songs を突き合わせ、以下のように処理を分ける。
| ケース | 処理 |
|---|---|
既存に同じ seq があり songTitle も同じ |
event_songs 行は変更しない(event_song_id を保持) |
既存に同じ seq があるが songTitle が異なる |
該当行の songTitle のみ UPDATE(event_song_id は保持) |
既存にない seq |
新規 INSERT(新しい event_song_id を採番) |
入力 songs に含まれない既存 seq |
該当行を DELETE(event_song_tracks は CASCADE で連動削除) |
- 本仕様は「楽曲の登録・追加・削除・全削除」のいずれも扱う。 入力
songs配列に曲を足せば追加、配列から曲を除けば削除、配列を[]にすれば全削除になる event_song_idは、対応するseqの曲が存在し続ける限り再同期をまたいで保持される。内容に変更がない曲についてevent_songs行を毎回 delete→re-create することはしない- 一方
event_song_tracks(トラック紐付け)は、songsに含まれる全曲について毎回buildTrackCandidatesMapFromPrismaで再解決し、該当event_song_idの既存リンクを削除してから書き直す。event_song_tracksは複合PK(event_song_id・track_id)のみで独自の代理キーを持たないため、event_songsと異なり洗い替えても ID の再採番は発生しない。この再解決を毎回行う理由は、曲名からtrackIdが1件も解決できなかった曲(発表当初はTrack未登録の新曲等)が、後日 Track が正式登録された時点で自動的に紐付くようにするため(追加の再解決バッチを別途用意せずに済む)
songs内の各曲について、lib/track-match.tsのbuildTrackCandidatesMapFromPrismaで曲名から該当する全trackId候補を解決する(1曲が複数トラック(CD盤・配信版シングル等)に正しく該当しうるケースへの対応。ラジオオンエア・セットリストと同じロジックを再利用する)- 上記の差分ルールに従って
event_songsを INSERT/UPDATE/DELETE する - 各曲(
event_song_idが確定したもの全て)について、既存のevent_song_tracksを削除し、解決できたtrackIdを全件登録し直す(lib/track-match.tsのbuildTrackJunctionRowsを再利用) - 該当トラックが1件も見つからない曲は、
event_songsには登録するが(曲名・seqは残る)、event_song_tracksへの登録は行わない。該当曲名を警告ログに出力する(register-tour-festival.tsのunresolvedSongTitlesと同じ扱い)
5-6. Festivals → events(festival)(V2追加予定)
| events カラム | 元フィールド |
|---|---|
date |
festival.date |
type |
"festival" |
title |
festival.name |
members |
[](メンバー×フェス紐づけなし) |
source |
"festival" |
sourceId |
festival.festivalId |
6. imageUrl 管理方針
6-1. release イベントのジャケット画像(#1070)
source = 'release' のイベントは Cover Art Archive(CAA) からジャケット画像を取得して imageUrl に保存する。
データフロー
releases.releaseId
= MusicBrainz リリースグループ MBID
└─ https://coverartarchive.org/release-group/{MBID}/front
→ 307 リダイレクト → archive.org 上の実画像 URL
→ events.imageUrl に保存
取得タイミング
imageUrl の自動設定は行わず、以下のタイミングで手動実行する。
| タイミング | 手順 |
|---|---|
| 初回一括バックフィル | bun run scripts/patch/fetch-release-images.ts を実行 |
| 新シングル発売後 | MusicBrainz 登録・sync-discography・sync-events 完了後に同スクリプトを実行 |
詳細な手順は
docs/operations/discography-operations.mdの「新シングル発売時のカバーアート取得手順」を参照。
取得対象外
source = 'manual'(manual-プレフィックス ID)の手動登録リリース→ MusicBrainz MBID を持たないため CAA 取得不可
6-2. release 以外のイベントの画像表示(#1070、プレースホルダー方式見直し #1472)
imageUrl = NULL かつ type ≠ release のイベントは、EventCard コンポーネントが種別に応じたプレースホルダーを動的生成する。DB への保存は不要。プレースホルダーは表示内容によって2方式に分かれる。
方式A: SVG テキストアート(live・join・graduate・leader)
タイトル文字列そのものではなく、種別ごとに合成した短い定型テキスト(開催年・JOIN/GRAD ラベル・メンバー名等)を固定サイズの viewBox に描画する。表示内容が短く定型のため、折り返しや省略は発生しない。
| type | SVG に表示する内容 |
|---|---|
live |
開催年 + 季節(UTC 月から算出: 3〜5月→春、6〜8月→夏、9〜11月→秋、12〜2月→冬) |
join |
1行目: JOIN / 2行目: メンバー名(1行に収まる範囲、超過は省略) |
graduate |
1行目: GRAD / 2行目: メンバー名(1行に収まる範囲、超過は省略) |
leader |
1行目: 第X代 / 2行目: リーダー就任 |
方式B: HTML テキストブロック(media・topic・festival、#1472)
events.title をそのまま全文表示する種別。SVG の <text> は CSS の折り返し(white-space/overflow-wrap)に対応しておらず、行数を固定して溢れた分を … で省略する方式だと、タイトル長が可変な限り必ず一部のケースで全文を表示できなくなる問題があった(#1472で発覚)。そのため、これらの type は <div> 等の HTML ブロック(overflow-wrap: break-word)で描画し、コンテナは固定の縦横比をやめて min-height のみを指定し、コンテンツ量に応じて高さが伸びる可変レイアウトにする。行数の上限は設けず、events.title を省略なく全文表示する。
| type | HTML ブロックに表示する内容 |
|---|---|
media |
番組名(events.title、全文) |
topic |
タイトル(events.title、全文) |
festival |
フェス名(events.title、全文) |
6-3. タイトル表示の重複回避(#1472)
EventCard は画像・プレースホルダー領域の下に <h3> でタイトルを別途表示するが、6-2 の方式Bはプレースホルダー自体に events.title の全文を表示するため、imageUrl 未設定時は <h3> と内容が完全に重複する。
imageUrl = NULLかつ 6-2 方式B対象の type(media・topic・festival)の場合のみ、<h3>タイトルを非表示にする- 上記以外(
imageUrlが設定されている場合、または方式A対象の type)は、プレースホルダー側がタイトル文字列自体を表示しないため、従来通り<h3>を表示する
7. インデックス
| インデックス対象 | インデックス種別 | 理由 |
|---|---|---|
date |
B-tree | タイムライン表示(ORDER BY date)の主要クエリキー |
type |
B-tree | イベント種別フィルタリング |
source, sourceId |
B-tree | 元データからのルックアップ・重複排除 |
tags |
GIN | タグ配列フィルタリング(@> ARRAY[...])(フィルタ機能実装時に追加予定) |
7-1. TO_CHAR(date, ...) による日付比較クエリのキャッシュ方針(#1551)
「過去のこの日」(トップページ)・タイムラインの年一覧取得は、date の月日・年だけを取り出して比較するため TO_CHAR() を使った生SQLクエリになっている。TO_CHAR() は date カラムに関数をかけてから比較する形になり、7. の date への B-tree インデックスが使えず全件スキャンになる。
| 関数 | 用途 | クエリ内容 |
|---|---|---|
lib/on-this-day.ts getOnThisDayEvents(monthDay) |
トップページ「過去のこの日」セクション | WHERE TO_CHAR(date, 'MM-DD') = ${monthDay} |
lib/timeline.ts getTimelineYears() |
タイムラインページの年タブ一覧 | SELECT DISTINCT TO_CHAR(date, 'YYYY') AS year FROM events |
いずれも unstable_cache(revalidate: 3600秒)でラップし、DBへのクエリ発行自体を最大1時間に1回へ抑える(lib/youtube.ts と同パターン)。全件スキャンそのものを解消する関数インデックスの追加は本番DBへのマイグレーションを伴うため別Issue(#1554)で扱う。
備考: 以前は getOnThisDayEvents() にキャッシュがなく、アクセスの度にNeon DBへ生クエリを発行していた。同じトップページ内の「新着投稿」セクション(キャッシュ経由でほぼ即時に返る)とは別々の Suspense で並行ストリーミングされるため、データ取得が先に完了した方から画面に反映される仕様と相まって、app/page.tsx 上でのセクション配置順(過去のこの日が先)と実際の表示順が逆転する不具合が発生していた(#1551で修正)。
8. タイムライン UI 表示仕様
8-1. 年別表示(#998)
- タイムラインページは 1年分のイベントのみ を表示する
- URL クエリパラメータ
year(例:/timeline?year=2024)で表示年を指定する - 未指定・無効値の場合は最新年を表示する
- ページ上部にすべての年のタブリンクを表示し、選択中の年をハイライトする
8-2. ライブイベントのグループ表示(#997)
同一ツアー(title が同じ type=live イベント群)は1グループとして折りたたんで表示する。
| グループの公演数 | 表示方法 |
|---|---|
| 1件 | 通常のフルサイズカード(会場名を日付の後に表示) |
| 2件 | 2枚のフルサイズカードを並列表示(会場名あり) |
| 3件以上 | 初日・千秋楽をフルサイズカード、中間日をコンパクトリスト(日付 / 会場 / 開演時刻)で表示 |
- 会場名・開演時刻は
livesテーブルをsourceId(liveId)経由でジョインして取得する - 同日に複数公演がある場合は1行にまとめ、時刻を
"14:00 / 18:00"形式で列挙する - 開演時刻が
00:00(未設定)の場合は時刻を表示しない
8-3. EventCard の時刻表示(#1381)
ツアーグループ化されない単発イベント(EventCard で直接描画される全 type)は、events.time を日付の直後に表示する。
timeが NULL または00:00の場合は表示しない(8-2 の Live 表示ルールと同じ判定関数shouldShowTime/formatLiveTimeを共通利用)type=liveのツアーグループ表示(8-2)はlivesテーブルの時刻を別途取得して表示するため、events.timeの表示とは独立している- 8-2 のツアーグループで会場欄に複数公演の時刻一覧(
"14:00 / 18:00")が含まれる代表カード(初日・千秋楽、または同日2件時の1枚カード)は、時刻が会場欄と二重表示にならないようevents.time自体の表示を抑制する(#1472)
8-4. イベントの並び順(#1472)
タイムライン表示(年・週・日・年表の各ビュー)で events を取得するクエリは、date 昇順に加えて time を副次ソートキー として指定する(orderBy: [{ date: "asc" }, { time: "asc" }])。
dateのみでORDER BYすると、同日内のタイ(同値)の並び順は Postgres が保証しないため、物理的な行順(挿入順等)が表示順になり不安定になる(#1472で発覚)timeが NULL のイベントは Postgres のASCデフォルト挙動(NULLS LAST)により末尾に並ぶ
改訂履歴
| 版 | 更新日 | 変更内容 |
|---|---|---|
| 2.5 | 2026-08-30 | 手動イベント登録に「会場」「楽曲」属性を追加する設計を追記。events.venueId カラム、event_songs・event_song_tracks テーブル(§2a・§2b)を新設。§5-5 に venueName → venueId の解決手順(register-tour-festival.ts の会場登録ロジックを流用)と songs → event_songs/event_song_tracks の変換手順(ラジオオンエア・セットリストと共通の SongEntrySchema・buildTrackCandidatesMapFromPrisma を再利用)を追加。実装は別Issueで対応(MorningStatusApp #1528, morning-status-blume#67) |
| 2.4 | 2026-08-23 | 7-1. TO_CHAR(date, ...) を使う日付比較クエリ(getOnThisDayEvents・getTimelineYears)の unstable_cache 化を追記。全件スキャン解消(関数インデックス追加)は別Issue(#1554)に切り出したことを明記(#1551) |
| 2.3 | 2026-08-03 | タイムライン改善(#1472): 同日イベントの並び順仕様(8-4)を追加、ツアーグループ代表カードの時刻二重表示回避仕様(8-3)を追加、media/topic/festival のプレースホルダーをSVG2行折り返しからHTML全文表示(可変高さ)に変更し6-2を更新、タイトル重複回避仕様(6-3)を追加 |
| 2.2 | 2026-07-20 | time カラムを追加。Lives → events(live) の変換仕様・Manual の time 入力仕様・EventCard の時刻表示仕様(8-3)を追記(#1381) |
| 2.1 | 2026-06-14 | tags フィールドを実装済みに更新。OG・Birthday 自動タグ仕様を追記。手動タグ付与フローを追記(#1110) |
| 2.0 | 2026-06-13 | V2 拡張設計を追記(festival type・tags フィールド・dateEnd フィールド・GIN インデックス)。セクション番号重複を修正(#1108) |
| 1.3 | 2026-06-05 | imageUrl 管理方針を追加(CAA によるジャケット画像取得・SVG テキストアート)(#1070) |
| 1.2 | 2026-05-29 | タイムライン UI 表示仕様を追加(年別表示・ライブグループ表示)(#997, #998) |
| 1.1 | 2026-05-24 | カラム名を実際の DB カラム名(camelCase)に修正。id・updatedAt のデフォルト値記述を修正(#989) |
| 1.0 | 2026-05-24 | 初版作成(#926) |