データベース設計
ER図(概念)
┌──────────┐ ┌──────────────┐ ┌──────────────┐
│ users │────<│ user_miles │ │ airports │
└────┬─────┘ └──────────────┘ └──────┬───────┘
│ │
│ ┌──────────────┐ │
├──────────<│ trips │ ┌──────┴───────┐
│ └──────┬───────┘ │ mile_charts │
│ │ └──────────────┘
│ ┌─────────────┼─────────────┐
│ │ │ │
│ ┌──▼────┐ ┌─────▼─────┐ ┌────▼───────┐
│ │ trip_ │ │ trip_ │ │ trip_ │
│ │flights│ │accomm. │ │itinerary │
│ └───────┘ └───────────┘ └────────────┘
│
│ ┌────────────────────┐
│ │ flight_availability│ ← Phase 2
│ └────────────────────┘
│
│ ┌──────────┐
│ │ hotels │ ← Phase 2
│ └──────────┘
│
│ ┌─────────────┐ ┌──────────────────┐
├─────────<│ gourmet_ │>────│ restaurants │ ← Phase 2
│ │ bookmarks │ └──────────────────┘
│ └─────────────┘
│
│ ┌──────────┐
│ │campaigns │ ← Phase 2
└──────────┘テーブル定義
users(ユーザー)
| カラム | 型 | 制約 | 説明 |
|---|---|---|---|
| id | INTEGER | PK, AUTO | ユーザーID |
| TEXT | UNIQUE, NOT NULL | メールアドレス | |
| name | TEXT | NOT NULL | 表示名 |
| password_hash | TEXT | NOT NULL | パスワードハッシュ |
| default_airport | TEXT | デフォルト出発空港コード | |
| created_at | DATETIME | DEFAULT NOW | 作成日時 |
| updated_at | DATETIME | DEFAULT NOW | 更新日時 |
user_miles(マイル残高)
| カラム | 型 | 制約 | 説明 |
|---|---|---|---|
| id | INTEGER | PK, AUTO | ID |
| user_id | INTEGER | FK → users | ユーザーID |
| airline | TEXT | NOT NULL | 'ANA' or 'JAL' |
| balance | INTEGER | NOT NULL | 所持マイル数 |
| expiry_date | DATE | 有効期限 | |
| updated_at | DATETIME | DEFAULT NOW | 更新日時 |
UNIQUE制約: (user_id, airline)
airports(空港マスタ)
| カラム | 型 | 制約 | 説明 |
|---|---|---|---|
| code | TEXT | PK | IATA空港コード (e.g., HND) |
| name | TEXT | NOT NULL | 空港名 |
| city | TEXT | NOT NULL | 都市名 |
| prefecture | TEXT | NOT NULL | 都道府県 |
| region | TEXT | NOT NULL | 地方(北海道、東北、...) |
| latitude | REAL | NOT NULL | 緯度 |
| longitude | REAL | NOT NULL | 経度 |
mile_charts(必要マイルチャート)
| カラム | 型 | 制約 | 説明 |
|---|---|---|---|
| id | INTEGER | PK, AUTO | ID |
| airline | TEXT | NOT NULL | 'ANA' or 'JAL' |
| origin_code | TEXT | FK → airports | 出発空港コード |
| destination_code | TEXT | FK → airports | 到着空港コード |
| seat_class | TEXT | NOT NULL | 座席クラス |
| season | TEXT | NOT NULL | シーズン区分 |
| miles_required | INTEGER | NOT NULL | 必要マイル数 |
| is_round_trip | BOOLEAN | DEFAULT false | 往復フラグ |
| updated_at | DATETIME | DEFAULT NOW | 更新日時 |
インデックス: (airline, origin_code, season)
seasons(シーズン定義)
| カラム | 型 | 制約 | 説明 |
|---|---|---|---|
| id | INTEGER | PK, AUTO | ID |
| airline | TEXT | NOT NULL | 'ANA' or 'JAL' |
| season_type | TEXT | NOT NULL | L/R/H (ANA) or 通常/ディスカウント (JAL) |
| start_date | DATE | NOT NULL | 開始日 |
| end_date | DATE | NOT NULL | 終了日 |
| year | INTEGER | NOT NULL | 適用年度 |
trips(旅行プラン)
| カラム | 型 | 制約 | 説明 |
|---|---|---|---|
| id | INTEGER | PK, AUTO | ID |
| user_id | INTEGER | FK → users | ユーザーID |
| title | TEXT | NOT NULL | プラン名 |
| destination | TEXT | 目的地 | |
| start_date | DATE | 出発日 | |
| end_date | DATE | 帰着日 | |
| status | TEXT | DEFAULT 'draft' | draft / planned / completed |
| notes | TEXT | メモ | |
| created_at | DATETIME | DEFAULT NOW | 作成日時 |
| updated_at | DATETIME | DEFAULT NOW | 更新日時 |
trip_flights(フライト情報)
| カラム | 型 | 制約 | 説明 |
|---|---|---|---|
| id | INTEGER | PK, AUTO | ID |
| trip_id | INTEGER | FK → trips | 旅行プランID |
| airline | TEXT | NOT NULL | 航空会社 |
| flight_number | TEXT | 便名 | |
| origin_code | TEXT | NOT NULL | 出発空港 |
| destination_code | TEXT | NOT NULL | 到着空港 |
| departure_at | DATETIME | 出発日時 | |
| arrival_at | DATETIME | 到着日時 | |
| miles_used | INTEGER | 使用マイル数 | |
| seat_class | TEXT | 座席クラス | |
| booking_status | TEXT | DEFAULT 'planned' | planned / booked / cancelled |
trip_accommodations(宿泊情報)
| カラム | 型 | 制約 | 説明 |
|---|---|---|---|
| id | INTEGER | PK, AUTO | ID |
| trip_id | INTEGER | FK → trips | 旅行プランID |
| provider | TEXT | NOT NULL | 予約サイト名 |
| hotel_name | TEXT | NOT NULL | ホテル名 |
| check_in | DATE | NOT NULL | チェックイン日 |
| check_out | DATE | NOT NULL | チェックアウト日 |
| price_per_night | INTEGER | 1泊料金(円) | |
| total_price | INTEGER | 合計料金(円) | |
| booking_url | TEXT | 予約URL | |
| booking_status | TEXT | DEFAULT 'planned' | planned / booked / cancelled |
trip_itinerary(日程アイテム)
| カラム | 型 | 制約 | 説明 |
|---|---|---|---|
| id | INTEGER | PK, AUTO | ID |
| trip_id | INTEGER | FK → trips | 旅行プランID |
| day_number | INTEGER | NOT NULL | 何日目 |
| sort_order | INTEGER | NOT NULL | 表示順 |
| type | TEXT | NOT NULL | transport / activity / meal / other |
| title | TEXT | NOT NULL | タイトル |
| description | TEXT | 説明 | |
| start_time | TEXT | 開始時刻 | |
| end_time | TEXT | 終了時刻 | |
| location | TEXT | 場所 | |
| cost | INTEGER | DEFAULT 0 | 費用(円) |
accommodation_cache(宿泊料金キャッシュ)
| カラム | 型 | 制約 | 説明 |
|---|---|---|---|
| id | INTEGER | PK, AUTO | ID |
| provider | TEXT | NOT NULL | 予約サイト名 |
| destination | TEXT | NOT NULL | 目的地 |
| check_in | DATE | NOT NULL | チェックイン日 |
| check_out | DATE | NOT NULL | チェックアウト日 |
| guests | INTEGER | NOT NULL | 宿泊人数 |
| response_json | TEXT | NOT NULL | APIレスポンス(JSON) |
| fetched_at | DATETIME | DEFAULT NOW | 取得日時 |
| expires_at | DATETIME | NOT NULL | 有効期限 |
インデックス: (provider, destination, check_in, check_out)
Phase 2 追加テーブル
Phase 2 で追加された新規テーブル群です。マイグレーション 002_flight_availability.sql ~ 005_campaigns.sql で定義されています。
flight_availability(フライト空席情報)
検索結果の空席カレンダー表示・お得フラグの判定に使用します。
| カラム | 型 | 制約 | 説明 |
|---|---|---|---|
| id | INTEGER | PK, AUTO | ID |
| origin_code | TEXT | FK → airports | 出発空港コード |
| destination_code | TEXT | FK → airports | 到着空港コード |
| flight_date | DATE | NOT NULL | フライト日 |
| airline | TEXT | NOT NULL | 'ANA' or 'JAL' |
| seats_available | INTEGER | NOT NULL | 残席数 |
| miles_required | INTEGER | NOT NULL | 必要マイル数 |
| is_deal | BOOLEAN | DEFAULT false | お得便フラグ |
インデックス: (origin_code, destination_code, flight_date), (airline, flight_date)
hotels(ホテルマスタ)
目的地ごとのホテル候補を保持します。Phase 2 ではモックデータとして運用し、Phase 3 で実プロバイダー API に置換予定です。
| カラム | 型 | 制約 | 説明 |
|---|---|---|---|
| id | INTEGER | PK, AUTO | ID |
| destination_code | TEXT | FK → airports | 目的地空港コード |
| name | TEXT | NOT NULL | ホテル名 |
| description | TEXT | 説明文 | |
| image_url | TEXT | 画像URL | |
| price_per_night | INTEGER | NOT NULL | 1泊料金(円) |
| rating | REAL | レーティング (0-5) | |
| review_count | INTEGER | DEFAULT 0 | レビュー件数 |
| provider | TEXT | 予約サイト名 | |
| booking_url | TEXT | 予約URL | |
| amenities | TEXT | アメニティ(JSON配列) |
インデックス: (destination_code, price_per_night)
restaurants(レストランマスタ)
グルメメインタブで使用する、地域ごとのレストラン情報。食べログ・Google 両方のレーティングソースを持ちます。
| カラム | 型 | 制約 | 説明 |
|---|---|---|---|
| id | INTEGER | PK, AUTO | ID |
| region | TEXT | NOT NULL | 地方(北海道、関西 など) |
| area | TEXT | NOT NULL | エリア詳細(札幌駅前 など) |
| name | TEXT | NOT NULL | 店舗名 |
| cuisine | TEXT | NOT NULL | ジャンル(寿司、ラーメン など) |
| price_range | TEXT | 価格帯(¥¥ など) | |
| image_url | TEXT | 画像URL | |
| tabelog_rating | REAL | 食べログ評価 (0-5) | |
| tabelog_reviews | INTEGER | 食べログ口コミ数 | |
| tabelog_url | TEXT | 食べログURL | |
| google_rating | REAL | Google 評価 (0-5) | |
| google_reviews | INTEGER | Google 口コミ数 | |
| google_url | TEXT | Google マップURL | |
| hours | TEXT | 営業時間 | |
| phone | TEXT | 電話番号 | |
| description | TEXT | 説明文 |
インデックス: (region, cuisine)
gourmet_bookmarks(グルメブックマーク)
ユーザーが保存したレストラン。旅行計画提案 UX の入力になります。
| カラム | 型 | 制約 | 説明 |
|---|---|---|---|
| id | INTEGER | PK, AUTO | ID |
| user_id | INTEGER | FK → users | ユーザーID |
| restaurant_id | INTEGER | FK → restaurants | レストランID |
| created_at | DATETIME | DEFAULT NOW | 作成日時 |
UNIQUE制約: (user_id, restaurant_id)
campaigns(航空会社キャンペーン)
ダッシュボードで表示する ANA/JAL のキャンペーン情報です。
| カラム | 型 | 制約 | 説明 |
|---|---|---|---|
| id | INTEGER | PK, AUTO | ID |
| airline | TEXT | NOT NULL | 'ANA' or 'JAL' |
| title | TEXT | NOT NULL | キャンペーン名 |
| description | TEXT | 詳細説明 | |
| image_url | TEXT | 画像URL | |
| start_date | DATE | NOT NULL | 開始日 |
| end_date | DATE | NOT NULL | 終了日 |
| link_url | TEXT | 詳細ページURL | |
| is_active | BOOLEAN | DEFAULT true | 有効フラグ |
| priority | INTEGER | DEFAULT 0 | 表示優先度 |
インデックス: (airline, is_active, priority)
マイグレーション戦略
SQLite のマイグレーションは server/migrations/ ディレクトリにSQLファイルとして管理:
server/migrations/
├── 001_initial_schema.sql
├── 002_flight_availability.sql
├── 003_hotels.sql
├── 004_restaurants_and_bookmarks.sql
└── 005_campaigns.sqlアプリ起動時に未適用のマイグレーションを順番に実行する。