DOCUMENT 08
データベース設計書
ER図
テーブル関連図(テキストER図)
1 ─ * = 1対多(必須) / 0..1 ─ 1 = 任意1対1 / * ─ * = 多対多(中間テーブル経由)
【1. Auth / ユーザー】
[roles] 1 ─ * [users]
【2. ペット・健康管理】
[users] 1 ─ 0..* [pets] (owner_user_id)
[pets] 1 ─ 0..* [health_records]
[users] 1 ─ 0..* [health_records] (recorded_by_user_id)
[pets] 1 ─ 0..* [pet_care_records]
[users] 1 ─ 0..* [pet_care_records] (recorded_by_user_id)
[pets] 1 ─ 0..* [symptom_checks]
[users] 1 ─ 0..* [symptom_checks] (requested_by_user_id)
【3. プラン・課金】
[plans] 1 ─ * [plan_features] (PK: plan_id + feature_code)
[plans] 1 ─ 0..* [subscriptions]
[users] 1 ─ 0..* [subscriptions]
[pets] 1 ─ 0..* [subscriptions]
[subscriptions] 1 ─ 0..* [invoices]
[invoices] 1 ─ 0..* [payments]
【4. 予約・診療記録】
[users] 1 ─ 0..* [appointment_slots] (created_by_user_id)
[appointment_slots] 1 ─ 0..* [appointments] (slot_id, NULL可)
[pets] 1 ─ 0..* [appointments]
[users] 1 ─ 0..* [appointments] (owner_user_id)
[users] 0..1 ─ 0..* [appointments] (staff_user_id, NULL可)
[appointments] 0..1 ─ 1 [medical_histories] (UNIQUE FK)
[pets] 1 ─ 0..* [medical_histories]
[users] 1 ─ 0..* [medical_histories] (handled_by_user_id)
[medical_histories] 1 ─ 0..* [medical_attachments]
【5. メッセージング・通知】
[users] 1 ─ 0..* [consult_chat_messages]
[users] 1 ─ 0..* [notifications] (created_by_user_id)
[notifications] * ─ * [users]
└── 中間テーブル: [notification_recipients] (PK: notification_id + user_id)
[email_templates] 1 ─ 0..* [email_messages]
[users] 1 ─ 0..* [email_messages] (recipient_user_id)
[pets] 0..1 ─ 0..* [email_messages]
[appointments] 0..1 ─ 0..* [email_messages]
[invoices] 0..1 ─ 0..* [email_messages]
【6. UX補助】
[pets] 1 ─ 0..* [pet_calendar_marks]
[users] 1 ─ 0..* [pet_calendar_marks] (created_by_user_id)
[users] 1 ─ 0..* [dismissed_reminders]
[users] 1 ─ 0..* [announcements] (created_by_user_id)
[users] 1 ─ 0..* [password_reset_tokens]
ビジュアルER図(ドメイン別カラーマップ)
ドメインごとに色分け。凡例:
||—o{ 1対多(必須FK) |o—o{ 1対多(任意FK) |o—o| 任意1対1(UNIQUE nullable FK)
🔐 Auth
🐾 Pets & Health
💳 Plans & Billing
📅 Appointments & Medical
💬 Messaging
⚙ UX Helpers
正規化評価
正規形チェック結果
実施日: 2026-05-24 / 対象: schema.sql の全24テーブル
| 正規形 | 評価 | 所見 |
|---|---|---|
| 第1正規形(1NF) | ✅ 合格 | 全テーブルで原子値を保持。plans.features_json は JSONB だが制御された非正規化(後述)。 |
| 第2正規形(2NF) | ✅ 合格 | 複合PKを持つ中間テーブル(plan_features, notification_recipients)でも、全非キー属性がPK全体に完全関数従属。 |
| 第3正規形(3NF) | ✅ 合格 | 推移的関数従属なし。subscriptions.user_id は pet_id → pets.owner_user_id 経由で導出可能だが、クエリ性能上の意図的冗長化として許容(後述)。 |
| ボイス・コッド正規形(BCNF) | ✅ 合格 | 全テーブルで決定子がスーパーキーである。候補キーが複数存在するテーブルは users(id, email)のみで矛盾なし。 |
意図的な非正規化(設計上のトレードオフ)
スキーマ変更なし — 以下はすべて合理的かつ意図的な設計判断
| 箇所 | 種別 | 理由・判断 |
|---|---|---|
plans.features_json |
表示用冗長化 | マーケティング表示用に全機能フラグをJSONで保持。プログラマティックなアクセス制御は plan_features テーブルで行う(役割分担が明確)。 |
subscriptions.user_id |
性能最適化 | pet_id → pets.owner_user_id 経由で導出可能だが、ユーザーIDによる直接検索(プラン判定・サブスク一覧)の性能を優先。 |
health_records のスコア5列 |
固定属性展開 | meal/exercise/sleep/mood/overall の5カテゴリは仕様固定。ピボットテーブル化するより列展開の方がクエリが単純で保守しやすい。 |
appointments.zoom_join_url |
APIキャッシュ | Zoom API から動的生成も可能だが、生成済みURLをDBキャッシュすることで2回目以降のアクセスでAPI呼び出しを省略。 |
email_messages.body |
監査用スナップショット | テンプレートから再生成可能だが、実際に送信した内容の監査証跡として送信時点のレンダリング済み本文を保存。 |
テーブル定義(全24テーブル)
全テーブル一覧
見方: 左右スクロール可 / ヘッダー固定 / データ型は PostgreSQL 記法
roles
users
pets
health_records
pet_care_records
symptom_checks
plans
plan_features
subscriptions
invoices
payments
appointment_slots
appointments
medical_histories
medical_attachments
consult_chat_messages
notifications
notification_recipients
email_templates
email_messages
pet_calendar_marks
dismissed_reminders
announcements
password_reset_tokens
roles
| カラム名 | データ型 | NULL許可 | デフォルト値 | 制約 | 説明 |
|---|---|---|---|---|---|
| id | BIGINT | NO | IDENTITY | PK | ロールID |
| role_code | VARCHAR(50) | NO | UNIQUE | ロールコード(ADMIN/SUPER/USER/VET/STAFF) | |
| role_name | VARCHAR(100) | NO | ロール名 | ||
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 作成日時 | |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新日時 |
users
| カラム名 | データ型 | NULL許可 | デフォルト値 | 制約 | 説明 |
|---|---|---|---|---|---|
| id | BIGINT | NO | IDENTITY | PK | ユーザーID |
| role_id | BIGINT | NO | FK → roles.id | ロールID | |
| name | VARCHAR(50) | NO | 氏名 | ||
| VARCHAR(255) | NO | UNIQUE | メールアドレス(ログインID) | ||
| password_hash | VARCHAR(255) | NO | BCryptハッシュパスワード | ||
| phone | VARCHAR(20) | YES | NULL | 電話番号 | |
| slack_user_id | VARCHAR(100) | YES | NULL | Slack ユーザーID(連携用) | |
| line_user_id | VARCHAR(100) | YES | NULL | LINE ユーザーID(連携用) | |
| status | VARCHAR(20) | NO | 'ACTIVE' | CHECK IN (ACTIVE/INACTIVE/SUSPENDED) | アカウント状態 |
| last_login_at | TIMESTAMP | YES | NULL | 最終ログイン日時 | |
| deleted_at | TIMESTAMP | YES | NULL | 論理削除日時 | |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 作成日時 | |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新日時 |
pets
| カラム名 | データ型 | NULL許可 | デフォルト値 | 制約 | 説明 |
|---|---|---|---|---|---|
| id | BIGINT | NO | IDENTITY | PK | ペットID |
| owner_user_id | BIGINT | NO | FK → users.id | 飼い主ユーザーID | |
| name | VARCHAR(40) | NO | ペット名 | ||
| species | VARCHAR(30) | NO | 種別(DOG/CAT 等) | ||
| breed | VARCHAR(100) | YES | NULL | 品種 | |
| sex | VARCHAR(10) | YES | NULL | 性別(MALE/FEMALE 等) | |
| birth_date | DATE | YES | NULL | 生年月日 | |
| weight_baseline_kg | DECIMAL(5,2) | YES | NULL | 基準体重(kg) | |
| image_path | VARCHAR(500) | YES | NULL | プロフィール画像ファイルパス | |
| deceased_at | TIMESTAMP | YES | NULL | 逝去日時 | |
| deleted_at | TIMESTAMP | YES | NULL | 論理削除日時 | |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 作成日時 | |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新日時 |
health_records
| カラム名 | データ型 | NULL許可 | デフォルト値 | 制約 | 説明 |
|---|---|---|---|---|---|
| id | BIGINT | NO | IDENTITY | PK | 記録ID |
| pet_id | BIGINT | NO | FK → pets.id | ペットID | |
| recorded_by_user_id | BIGINT | NO | FK → users.id | 記録者ユーザーID | |
| record_date | DATE | NO | 記録日 | ||
| weight_kg | DECIMAL(5,2) | YES | NULL | 体重(kg) | |
| meal_memo | VARCHAR(300) | YES | NULL | 食事メモ | |
| exercise_minutes | INTEGER | YES | NULL | 運動時間(分) | |
| meal_score | INTEGER | YES | NULL | CHECK 1–5 | 食事スコア |
| exercise_score | INTEGER | YES | NULL | CHECK 1–5 | 運動スコア |
| sleep_score | INTEGER | YES | NULL | CHECK 1–5 | 睡眠スコア |
| mood_score | INTEGER | YES | NULL | CHECK 1–5 | 気分スコア |
| overall_score | INTEGER | YES | NULL | CHECK 1–5 | 総合スコア |
| image_path | VARCHAR(500) | YES | NULL | 添付画像パス | |
| note | TEXT | YES | NULL | 備考 | |
| deleted_at | TIMESTAMP | YES | NULL | 論理削除日時 | |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 作成日時 | |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新日時 |
pet_care_records
| カラム名 | データ型 | NULL許可 | デフォルト値 | 制約 | 説明 |
|---|---|---|---|---|---|
| id | BIGINT | NO | IDENTITY | PK | ケア記録ID |
| pet_id | BIGINT | NO | FK → pets.id | ペットID | |
| recorded_by_user_id | BIGINT | NO | FK → users.id | 記録者ユーザーID | |
| care_type | VARCHAR(30) | NO | CHECK IN (RABIES/HEARTWORM/COMBO_VACCINE/MEDICAL_VISIT/OTHER) | ケア種別 | |
| administered_on | DATE | NO | 実施日 | ||
| next_due_on | DATE | NO | 次回予定日(リマインダー基準) | ||
| memo | VARCHAR(500) | YES | NULL | 備考 | |
| deleted_at | TIMESTAMP | YES | NULL | 論理削除日時 | |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 作成日時 | |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新日時 |
symptom_checks
| カラム名 | データ型 | NULL許可 | デフォルト値 | 制約 | 説明 |
|---|---|---|---|---|---|
| id | BIGINT | NO | IDENTITY | PK | 症状チェックID |
| pet_id | BIGINT | NO | FK → pets.id | ペットID | |
| requested_by_user_id | BIGINT | NO | FK → users.id | 依頼者ユーザーID | |
| symptom_type | VARCHAR(100) | NO | 症状種別 | ||
| onset_text | VARCHAR(100) | YES | NULL | 発症状況テキスト | |
| memo | VARCHAR(500) | YES | NULL | 補足メモ | |
| severity | VARCHAR(10) | NO | CHECK IN (LOW/MEDIUM/HIGH) | 重症度 | |
| recommendation | VARCHAR(10) | NO | CHECK IN (OBSERVE/CONSULT/VISIT) | 推奨対応 | |
| guidance | TEXT | YES | NULL | AIガイダンス本文 | |
| ai_model | VARCHAR(100) | YES | NULL | 使用AIモデル名 | |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 作成日時(更新なし) |
plans
| カラム名 | データ型 | NULL許可 | デフォルト値 | 制約 | 説明 |
|---|---|---|---|---|---|
| id | BIGINT | NO | IDENTITY | PK | プランID |
| name | VARCHAR(100) | NO | プラン名(LIGHT/STANDARD/PREMIUM) | ||
| monthly_fee | DECIMAL(10,2) | NO | 月額料金(円) | ||
| features_json | JSONB | YES | NULL | 機能フラグ一覧(表示用・非正規化②) | |
| is_active | BOOLEAN | NO | TRUE | 有効フラグ | |
| deleted_at | TIMESTAMP | YES | NULL | 論理削除日時 | |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 作成日時 | |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新日時 |
plan_features
アクセス制御用機能コードテーブル。feature_code: AI_SYMPTOM / SLACK_BOT / LINE_BOT / ZOOM_CONSULT
| カラム名 | データ型 | NULL許可 | デフォルト値 | 制約 | 説明 |
|---|---|---|---|---|---|
| plan_id | BIGINT | NO | PK, FK → plans.id | プランID | |
| feature_code | VARCHAR(50) | NO | PK | 機能コード |
subscriptions
ペット単位の契約。user_id は pet_id → pets.owner_user_id 経由で導出可能だが性能上の意図的冗長化(非正規化②)。
| カラム名 | データ型 | NULL許可 | デフォルト値 | 制約 | 説明 |
|---|---|---|---|---|---|
| id | BIGINT | NO | IDENTITY | PK | サブスクリプションID |
| user_id | BIGINT | NO | FK → users.id | 契約ユーザーID(性能最適化) | |
| pet_id | BIGINT | NO | FK → pets.id | 対象ペットID | |
| plan_id | BIGINT | NO | FK → plans.id | プランID | |
| start_date | DATE | NO | 契約開始日 | ||
| end_date | DATE | YES | NULL | 契約終了日(NULLは無期限) | |
| status | VARCHAR(20) | NO | CHECK IN (ACTIVE/PAUSED/CANCELED) | 契約状態 | |
| auto_renew | BOOLEAN | NO | TRUE | 自動更新フラグ | |
| deleted_at | TIMESTAMP | YES | NULL | 論理削除日時 | |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 作成日時 | |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新日時 |
invoices
スキーマのみ実装済み(コントローラー・サービス未実装)
| カラム名 | データ型 | NULL許可 | デフォルト値 | 制約 | 説明 |
|---|---|---|---|---|---|
| id | BIGINT | NO | IDENTITY | PK | 請求書ID |
| subscription_id | BIGINT | NO | FK → subscriptions.id | サブスクリプションID | |
| invoice_number | VARCHAR(50) | NO | UNIQUE | 請求書番号 | |
| invoice_date | DATE | NO | 請求日 | ||
| due_date | DATE | NO | 支払期日 | ||
| amount | DECIMAL(10,2) | NO | 請求金額 | ||
| payment_status | VARCHAR(20) | NO | CHECK IN (UNPAID/PARTIAL/PAID) | 支払状態 | |
| issued_at | TIMESTAMP | YES | NULL | 発行日時 | |
| paid_at | TIMESTAMP | YES | NULL | 支払完了日時 | |
| deleted_at | TIMESTAMP | YES | NULL | 論理削除日時 | |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 作成日時 | |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新日時 |
payments
スキーマのみ実装済み(コントローラー・サービス未実装)
| カラム名 | データ型 | NULL許可 | デフォルト値 | 制約 | 説明 |
|---|---|---|---|---|---|
| id | BIGINT | NO | IDENTITY | PK | 支払ID |
| invoice_id | BIGINT | NO | FK → invoices.id | 請求書ID | |
| paid_amount | DECIMAL(10,2) | NO | 支払金額 | ||
| paid_at | TIMESTAMP | YES | NULL | 支払日時 | |
| payment_method | VARCHAR(20) | NO | CHECK IN (CARD/BANK/OTHER) | 支払方法 | |
| transaction_ref | VARCHAR(100) | YES | NULL | 決済システムトランザクション参照ID | |
| status | VARCHAR(20) | NO | CHECK IN (PENDING/SUCCEEDED/FAILED) | 決済状態 | |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 作成日時(更新なし) |
appointment_slots
| カラム名 | データ型 | NULL許可 | デフォルト値 | 制約 | 説明 |
|---|---|---|---|---|---|
| id | BIGINT | NO | IDENTITY | PK | 予約枠ID |
| slot_datetime | TIMESTAMP | NO | 予約枠日時 | ||
| note | VARCHAR(200) | YES | NULL | 備考 | |
| created_by_user_id | BIGINT | NO | FK → users.id | 作成者ユーザーID | |
| deleted_at | TIMESTAMP | YES | NULL | 論理削除日時 | |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 作成日時(更新なし) |
appointments
| カラム名 | データ型 | NULL許可 | デフォルト値 | 制約 | 説明 |
|---|---|---|---|---|---|
| id | BIGINT | NO | IDENTITY | PK | 予約ID |
| pet_id | BIGINT | NO | FK → pets.id | 対象ペットID | |
| owner_user_id | BIGINT | NO | FK → users.id | 飼い主ユーザーID | |
| staff_user_id | BIGINT | YES | NULL | FK → users.id | 担当スタッフID(承認後に設定) |
| appointment_type | VARCHAR(20) | NO | CHECK IN (CONSULTATION/MEDICAL) | 予約種別 | |
| channel | VARCHAR(20) | NO | CHECK IN (ONLINE/VISIT/HOSPITAL) | 実施チャネル | |
| scheduled_at | TIMESTAMP | NO | 予約日時 | ||
| status | VARCHAR(20) | NO | CHECK IN (REQUESTED/CONFIRMED/CANCELED/COMPLETED) | 予約状態 | |
| zoom_join_url | VARCHAR(500) | YES | NULL | Zoom参加URL(非正規化④) | |
| note | VARCHAR(500) | YES | NULL | 備考 | |
| slot_id | BIGINT | YES | NULL | FK → appointment_slots.id | 関連予約枠ID |
| deleted_at | TIMESTAMP | YES | NULL | 論理削除日時 | |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 作成日時 | |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新日時 |
medical_histories
| カラム名 | データ型 | NULL許可 | デフォルト値 | 制約 | 説明 |
|---|---|---|---|---|---|
| id | BIGINT | NO | IDENTITY | PK | 診療記録ID |
| pet_id | BIGINT | NO | FK → pets.id | 対象ペットID | |
| appointment_id | BIGINT | YES | NULL | UNIQUE, FK → appointments.id | 関連予約ID(1予約1記録) |
| handled_by_user_id | BIGINT | NO | FK → users.id | 対応者(VET/STAFF)ID | |
| performed_on | DATE | NO | 実施日 | ||
| treatment_detail | VARCHAR(1000) | NO | 対応内容 | ||
| diagnosis | VARCHAR(1000) | YES | NULL | 診断内容 | |
| prescription | VARCHAR(1000) | YES | NULL | 処方内容 | |
| deleted_at | TIMESTAMP | YES | NULL | 論理削除日時 | |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 作成日時 | |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新日時 |
medical_attachments
スキーマのみ実装済み(コントローラー・サービス未実装)
| カラム名 | データ型 | NULL許可 | デフォルト値 | 制約 | 説明 |
|---|---|---|---|---|---|
| id | BIGINT | NO | IDENTITY | PK | 添付ファイルID |
| medical_history_id | BIGINT | NO | FK → medical_histories.id | 診療記録ID | |
| file_name | VARCHAR(255) | NO | ファイル名 | ||
| file_path | VARCHAR(500) | NO | 保存先パス | ||
| file_mime_type | VARCHAR(100) | NO | MIMEタイプ | ||
| file_size_bytes | BIGINT | NO | ファイルサイズ(bytes) | ||
| description | VARCHAR(200) | YES | NULL | 説明 | |
| uploaded_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | アップロード日時 | |
| deleted_at | TIMESTAMP | YES | NULL | 論理削除日時 | |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 作成日時 | |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新日時 |
consult_chat_messages
| カラム名 | データ型 | NULL許可 | デフォルト値 | 制約 | 説明 |
|---|---|---|---|---|---|
| id | BIGINT | NO | IDENTITY | PK | メッセージID |
| user_id | BIGINT | NO | FK → users.id | ユーザーID | |
| sender_type | VARCHAR(10) | NO | CHECK IN (USER/BOT) | 送信者種別 | |
| message | VARCHAR(1000) | NO | メッセージ本文 | ||
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 作成日時(更新なし) |
notifications
| カラム名 | データ型 | NULL許可 | デフォルト値 | 制約 | 説明 |
|---|---|---|---|---|---|
| id | BIGINT | NO | IDENTITY | PK | 通知ID |
| notification_type | VARCHAR(20) | NO | CHECK IN (REMINDER/INFO/ALERT) | 通知種別 | |
| title | VARCHAR(100) | NO | 通知タイトル | ||
| body | VARCHAR(2000) | NO | 通知本文 | ||
| scheduled_at | TIMESTAMP | YES | NULL | 配信予定日時 | |
| sent_at | TIMESTAMP | YES | NULL | 配信完了日時 | |
| delivery_status | VARCHAR(20) | NO | CHECK IN (DRAFT/SCHEDULED/SENT/FAILED) | 配信状態 | |
| created_by_user_id | BIGINT | NO | FK → users.id | 作成者ユーザーID | |
| deleted_at | TIMESTAMP | YES | NULL | 論理削除日時 | |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 作成日時 | |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新日時 |
notification_recipients
notifications × users の中間テーブル(多対多)
| カラム名 | データ型 | NULL許可 | デフォルト値 | 制約 | 説明 |
|---|---|---|---|---|---|
| notification_id | BIGINT | NO | PK, FK → notifications.id | 通知ID | |
| user_id | BIGINT | NO | PK, FK → users.id | 受信ユーザーID | |
| read_at | TIMESTAMP | YES | NULL | 既読日時 | |
| delivery_status | VARCHAR(20) | NO | CHECK IN (PENDING/SENT/FAILED) | 配信状態 |
email_templates
スキーマのみ実装済み(コントローラー・サービス未実装)
| カラム名 | データ型 | NULL許可 | デフォルト値 | 制約 | 説明 |
|---|---|---|---|---|---|
| id | BIGINT | NO | IDENTITY | PK | テンプレートID |
| template_code | VARCHAR(100) | NO | UNIQUE | テンプレートコード | |
| subject_template | VARCHAR(200) | NO | 件名テンプレート | ||
| body_template | TEXT | NO | 本文テンプレート | ||
| is_active | BOOLEAN | NO | TRUE | 有効フラグ | |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 作成日時 | |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新日時 |
email_messages
スキーマのみ実装済み。subject/body は送信時点のスナップショット(非正規化⑤)。
| カラム名 | データ型 | NULL許可 | デフォルト値 | 制約 | 説明 |
|---|---|---|---|---|---|
| id | BIGINT | NO | IDENTITY | PK | メール送信ID |
| template_id | BIGINT | NO | FK → email_templates.id | 使用テンプレートID | |
| recipient_user_id | BIGINT | NO | FK → users.id | 宛先ユーザーID | |
| pet_id | BIGINT | YES | NULL | FK → pets.id | 関連ペットID(任意) |
| appointment_id | BIGINT | YES | NULL | FK → appointments.id | 関連予約ID(任意) |
| invoice_id | BIGINT | YES | NULL | FK → invoices.id | 関連請求書ID(任意) |
| subject | VARCHAR(200) | NO | 件名(レンダリング済み) | ||
| body | TEXT | NO | 本文(レンダリング済み) | ||
| send_timing_at | TIMESTAMP | YES | NULL | 送信予定日時 | |
| status | VARCHAR(20) | NO | CHECK IN (QUEUED/SENT/FAILED) | 送信状態 | |
| error_message | TEXT | YES | NULL | エラー内容 | |
| sent_at | TIMESTAMP | YES | NULL | 送信完了日時 | |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 作成日時(更新なし) |
pet_calendar_marks
| カラム名 | データ型 | NULL許可 | デフォルト値 | 制約 | 説明 |
|---|---|---|---|---|---|
| id | BIGINT | NO | IDENTITY | PK | マークID |
| pet_id | BIGINT | NO | FK → pets.id | ペットID | |
| created_by_user_id | BIGINT | NO | FK → users.id | 作成者ユーザーID | |
| mark_date | DATE | NO | マーク日付 | ||
| mark_type | VARCHAR(20) | NO | CHECK IN (KARTE/VACCINE/HOSPITAL/TRIMMING/DOG_RUN/MEDICINE) | マーク種別 | |
| memo | VARCHAR(500) | YES | NULL | メモ | |
| deleted_at | TIMESTAMP | YES | NULL | 論理削除日時 | |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 作成日時 | |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新日時 |
dismissed_reminders
ユーザーが確認済みにしたスケジュールリマインダーを永続化。(user_id, reminder_key) のユニーク制約で重複防止。
| カラム名 | データ型 | NULL許可 | デフォルト値 | 制約 | 説明 |
|---|---|---|---|---|---|
| id | BIGINT | NO | IDENTITY | PK | ID |
| user_id | BIGINT | NO | FK → users.id | ユーザーID | |
| reminder_key | VARCHAR(200) | NO | リマインダーキー(例: care_1_2026-08-01) | ||
| dismissed_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 非表示設定日時 | |
| UNIQUE (user_id, reminder_key) | |||||
announcements
| カラム名 | データ型 | NULL許可 | デフォルト値 | 制約 | 説明 |
|---|---|---|---|---|---|
| id | BIGINT | NO | IDENTITY | PK | お知らせID |
| title | VARCHAR(200) | NO | タイトル | ||
| body | TEXT | NO | 本文 | ||
| is_active | BOOLEAN | NO | TRUE | 表示フラグ | |
| created_by_user_id | BIGINT | NO | FK → users.id | 作成者ユーザーID | |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 作成日時 | |
| updated_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 更新日時 |
password_reset_tokens
| カラム名 | データ型 | NULL許可 | デフォルト値 | 制約 | 説明 |
|---|---|---|---|---|---|
| id | BIGINT | NO | IDENTITY | PK | トークンID |
| user_id | BIGINT | NO | FK → users.id | ユーザーID | |
| token | VARCHAR(100) | NO | UNIQUE | パスワードリセットトークン | |
| expires_at | TIMESTAMP | NO | 有効期限 | ||
| used_at | TIMESTAMP | YES | NULL | 使用日時(NULL=未使用) | |
| created_at | TIMESTAMP | NO | CURRENT_TIMESTAMP | 作成日時(更新なし) |
インデックス設計
インデックス一覧
検索頻度・FK結合・一意制約・ソート用途の観点で選定
| テーブル名 | インデックス名 | 対象カラム | 種類 | 設定理由 |
|---|---|---|---|---|
| Auth | ||||
| roles | uk_roles_role_code | role_code | UNIQUE | ロールコード一意制約 |
| users | uk_users_email | UNIQUE | ログインID一意制約 | |
| users | idx_users_role_id | role_id | INDEX | ロール別検索・FK結合 |
| users | idx_users_status_deleted_at | status, deleted_at | INDEX | 有効ユーザー絞り込み |
| ペット・健康管理 | ||||
| pets | idx_pets_owner_user_id | owner_user_id | INDEX | 飼い主配下のペット検索 |
| pets | idx_pets_species | species | INDEX | 種別検索 |
| health_records | idx_health_records_pet_record_date | pet_id, record_date | INDEX | ペット別時系列取得 |
| health_records | idx_health_records_recorded_by_user_id | recorded_by_user_id | INDEX | 記録者検索・FK結合 |
| pet_care_records | idx_pet_care_records_pet_next_due | pet_id, next_due_on | INDEX | 次回予定日によるリマインダー抽出 |
| symptom_checks | idx_symptom_checks_pet_created_at | pet_id, created_at | INDEX | ペット別チェック履歴 |
| symptom_checks | idx_symptom_checks_requested_by_user_id | requested_by_user_id | INDEX | 依頼者検索・FK結合 |
| プラン・課金 | ||||
| plans | idx_plans_is_active | is_active | INDEX | 有効プラン検索 |
| subscriptions | idx_subscriptions_user_status | user_id, status | INDEX | ユーザー契約一覧・プラン判定 |
| subscriptions | idx_subscriptions_pet_status | pet_id, status | INDEX | ペット契約一覧 |
| subscriptions | idx_subscriptions_plan_id | plan_id | INDEX | プラン別集計・FK結合 |
| invoices | uk_invoices_invoice_number | invoice_number | UNIQUE | 請求書番号一意制約 |
| invoices | idx_invoices_subscription_id | subscription_id | INDEX | FK結合 |
| invoices | idx_invoices_payment_status_due_date | payment_status, due_date | INDEX | 未払い・期限順管理 |
| payments | idx_payments_invoice_id | invoice_id | INDEX | FK結合 |
| payments | idx_payments_status | status | INDEX | 決済状態別抽出 |
| 予約・診療記録 | ||||
| appointment_slots | idx_appointment_slots_datetime | slot_datetime | INDEX | 日時順検索・空き枠確認 |
| appointments | idx_appointments_pet_scheduled_at | pet_id, scheduled_at | INDEX | ペット別予約一覧・ソート |
| appointments | idx_appointments_owner_scheduled_at | owner_user_id, scheduled_at | INDEX | 飼い主別予約検索 |
| appointments | idx_appointments_staff_scheduled_at | staff_user_id, scheduled_at | INDEX | 担当者別予定検索 |
| appointments | idx_appointments_status_scheduled_at | status, scheduled_at | INDEX | 状態別の時系列検索 |
| medical_histories | uk_medical_histories_appointment_id | appointment_id | UNIQUE | 予約と診療記録の1対1担保 |
| medical_histories | idx_medical_histories_pet_performed_on | pet_id, performed_on | INDEX | ペット別履歴検索・日付順 |
| medical_attachments | idx_medical_attachments_history_id | medical_history_id | INDEX | 履歴配下の添付取得 |
| メッセージング・通知 | ||||
| consult_chat_messages | idx_consult_chat_messages_user_id_created | user_id, created_at | INDEX | ユーザー別チャット履歴 |
| notifications | idx_notifications_created_by | created_by_user_id | INDEX | 作成者別検索・FK結合 |
| notifications | idx_notifications_status_scheduled | delivery_status, scheduled_at | INDEX | 配信対象抽出 |
| notification_recipients | pk_notification_recipients | notification_id, user_id | PRIMARY KEY | 中間テーブル複合主キー |
| notification_recipients | idx_notification_recipients_user_status | user_id, delivery_status | INDEX | ユーザー受信一覧 |
| email_templates | uk_email_templates_template_code | template_code | UNIQUE | テンプレートコード一意制約 |
| email_messages | idx_email_messages_recipient_created | recipient_user_id, created_at | INDEX | 宛先別送信履歴 |
| email_messages | idx_email_messages_status_send_timing | status, send_timing_at | INDEX | 送信キュー処理 |
| email_messages | idx_email_messages_template_id | template_id | INDEX | テンプレート別集計 |
| UX補助 | ||||
| pet_calendar_marks | idx_pet_calendar_marks_pet_date | pet_id, mark_date | INDEX | ペット別カレンダー取得 |
| dismissed_reminders | uk_dismissed_reminders_user_key | user_id, reminder_key | UNIQUE | ユーザー単位でのリマインダー重複防止 |
| dismissed_reminders | idx_dismissed_reminders_user_id | user_id | INDEX | ユーザー別リマインダー一覧 |
| password_reset_tokens | uk_password_reset_tokens_token | token | UNIQUE | トークン一意制約 |
初期データ
シードデータ(data.sql)
DataInitializer (BCrypt) でユーザーを作成し、data.sql でマスタ・テストデータを投入
【roles】5件
ADMIN(1) / SUPER(2) / USER(3) / VET(4) / STAFF(5)
【plans】3件
LIGHT(980円) / STANDARD(1980円) / PREMIUM(2980円)
【plan_features】7件
STANDARD: AI_SYMPTOM, SLACK_BOT, LINE_BOT
PREMIUM: AI_SYMPTOM, SLACK_BOT, LINE_BOT, ZOOM_CONSULT
【users】DataInitializer で自動生成(BCryptハッシュ)
super@petlife.local (SUPER)
admin@petlife.local (ADMIN)
vet1@petlife.local (VET)
staff1@petlife.local (STAFF)
owner1@petlife.local / owner2@petlife.local / owner3@petlife.local (USER)
【pets】6件
ポチ(DOG), タロウ(DOG) — owner1
レオン(DOG) — owner2
ピーコ(DOG) / カレン(DOG) / ボス(DOG) — プラン別テストアカウント用
【health_records】3件(各ペットの直近記録)
【pet_care_records】3件(ポチのワクチン・フィラリア・混合ワクチン)
【subscriptions】5件
owner1→ポチ(STANDARD) / owner2→レオン(STANDARD)
owner1→ピーコ(LIGHT) / owner2→カレン(STANDARD) / owner3→ボス(PREMIUM)
実行コマンド:
psql -U postgres -d petlifeplus -f backend/src/main/resources/data.sql