← 成果物一覧に戻る

データベース設計書

Version 1.0.0 | SB-Ticket(シュートボクシング)

SB-Ticket データベーススキーマ設計書

データベース: Supabase PostgreSQL バージョン: 001_initial_schema + 002_rls_policies + 003_functions 最終更新: 2026-03-17


目次

  1. ER図
  2. ENUM型定義
  3. テーブル一覧
  4. テーブル詳細
  5. ビュー
  6. インデックス一覧
  7. RLSポリシー
  8. ストアド関数
  9. トリガー

ER図

erDiagram
    players ||--o{ event_players : "出場"
    players ||--o{ tickets : "販売"
    players ||--o{ fans : "ファン管理"
    players ||--o{ sponsors : "スポンサー"
    players ||--o{ kickbacks : "キックバック"
    players ||--o{ player_rewards : "還元"
    players ||--o{ notifications : "通知受信"
    players ||--o{ page_views : "PV"

    admins ||--o{ events : "作成"
    admins ||--o{ lps : "公開承認"
    admins ||--o{ player_rewards : "確定承認"

    events ||--o{ event_players : "出場選手"
    events ||--o{ tickets : "チケット"
    events ||--o{ lps : "LP(1:1)"
    events ||--o{ notifications : "関連通知"
    events ||--o{ player_rewards : "還元"
    events ||--o{ page_views : "PV"
    events ||--o{ fans : "最終購入イベント"

    fans ||--o{ tickets : "購入"

    sponsors ||--o{ kickbacks : "キックバック"

    players {
        UUID id PK
        VARCHAR login_id UK
        VARCHAR password_hash
        VARCHAR name
        VARCHAR name_kana
        VARCHAR gym
        VARCHAR weight_class
        TEXT profile_text
        TEXT profile_image_url
        TEXT phone_encrypted
        TEXT email_encrypted
        JSONB sns_links
        VARCHAR slug UK
        user_role role
        BOOLEAN is_locked
        INTEGER failed_attempts
        TIMESTAMPTZ locked_at
        VARCHAR last_password_hash
        BOOLEAN must_change_password
        TIMESTAMPTZ password_changed_at
        BOOLEAN phone_verified
        JSONB push_subscription
        TIMESTAMPTZ created_at
        TIMESTAMPTZ updated_at
        TIMESTAMPTZ deleted_at
    }

    admins {
        UUID id PK
        VARCHAR login_id UK
        VARCHAR password_hash
        VARCHAR name
        TEXT phone_encrypted
        user_role role
        BOOLEAN is_locked
        INTEGER failed_attempts
        TIMESTAMPTZ locked_at
        VARCHAR last_password_hash
        BOOLEAN must_change_password
        BOOLEAN phone_verified
        JSONB push_subscription
        TIMESTAMPTZ created_at
        TIMESTAMPTZ updated_at
        TIMESTAMPTZ deleted_at
    }

    events {
        UUID id PK
        VARCHAR name
        DATE event_date
        VARCHAR venue
        TIME door_open_time
        TIME start_time
        DATE ticket_sale_date
        DATE ticket_deadline_date
        JSONB seat_types
        TEXT_ARRAY sales_channels
        TEXT pia_base_url
        TEXT description
        TEXT_ARRAY promo_images
        VARCHAR status
        UUID created_by FK
        TIMESTAMPTZ created_at
        TIMESTAMPTZ updated_at
        TIMESTAMPTZ deleted_at
    }

    event_players {
        UUID id PK
        UUID event_id FK
        UUID player_id FK
        INTEGER quota
        VARCHAR ref_code UK
        TEXT pia_url
        TEXT eplus_url
        TEXT_ARRAY promo_images
        DECIMAL reward_rate
        TIMESTAMPTZ created_at
    }

    fans {
        UUID id PK
        UUID player_id FK
        VARCHAR name
        TEXT phone_encrypted
        TEXT email_encrypted
        TEXT memo
        UUID last_purchase_event_id FK
        TIMESTAMPTZ last_purchase_at
        INTEGER total_purchases
        BOOLEAN is_dormant
        TIMESTAMPTZ created_at
        TIMESTAMPTZ updated_at
        TIMESTAMPTZ deleted_at
    }

    tickets {
        UUID id PK
        UUID event_id FK
        UUID player_id FK
        UUID fan_id FK
        VARCHAR seat_type
        INTEGER quantity
        INTEGER unit_price
        INTEGER total_amount
        ticket_route route
        VARCHAR buyer_name
        payment_method payment_method
        BOOLEAN is_paid
        TIMESTAMPTZ paid_at
        VARCHAR memo
        JSONB webhook_payload
        TIMESTAMPTZ purchased_at
        TIMESTAMPTZ created_at
        TIMESTAMPTZ updated_at
    }

    lps {
        UUID id PK
        UUID event_id FK_UK
        lp_status status
        TEXT html_content
        TEXT hero_image_url
        TEXT ogp_image_url
        VARCHAR meta_title
        TEXT meta_description
        VARCHAR association_ref_code UK
        TEXT association_pia_url
        JSONB editor_data
        VARCHAR generated_by_model
        TIMESTAMPTZ published_at
        UUID published_by FK
        TIMESTAMPTZ unpublished_at
        TIMESTAMPTZ created_at
        TIMESTAMPTZ updated_at
    }

    sponsors {
        UUID id PK
        UUID player_id FK
        VARCHAR company_name
        VARCHAR industry
        VARCHAR contact_name
        TEXT contact_phone_encrypted
        TEXT contact_email_encrypted
        TEXT logo_url
        TEXT website_url
        INTEGER contract_amount
        DATE contract_start_date
        DATE contract_end_date
        concept_status concept_sales_status
        TEXT concept_memo
        BOOLEAN is_public_to_association
        BOOLEAN is_approved_for_display
        TEXT memo
        TIMESTAMPTZ created_at
        TIMESTAMPTZ updated_at
        TIMESTAMPTZ deleted_at
    }

    kickbacks {
        UUID id PK
        UUID sponsor_id FK
        UUID player_id FK
        INTEGER contract_amount
        DECIMAL kickback_rate
        INTEGER kickback_amount "GENERATED"
        DATE payment_due_date
        BOOLEAN is_paid
        DATE paid_date
        TEXT transfer_info_encrypted
        TEXT memo
        TIMESTAMPTZ created_at
        TIMESTAMPTZ updated_at
    }

    player_rewards {
        UUID id PK
        UUID event_id FK
        UUID player_id FK
        INTEGER lp_sales_amount
        DECIMAL reward_rate
        INTEGER reward_amount "GENERATED"
        VARCHAR status
        DATE payment_due_date
        DATE paid_date
        TIMESTAMPTZ confirmed_at
        UUID confirmed_by FK
        TEXT memo
        TIMESTAMPTZ created_at
        TIMESTAMPTZ updated_at
    }

    notifications {
        UUID id PK
        user_role recipient_type
        UUID recipient_id
        notification_type type
        notification_method method
        VARCHAR title
        TEXT body
        UUID related_event_id FK
        UUID related_player_id FK
        BOOLEAN is_read
        BOOLEAN is_sent
        TIMESTAMPTZ sent_at
        VARCHAR sms_sid
        TEXT error_message
        TIMESTAMPTZ created_at
    }

    audit_logs {
        UUID id PK
        VARCHAR actor_type
        UUID actor_id
        VARCHAR action
        VARCHAR resource_type
        UUID resource_id
        JSONB changes
        INET ip_address
        TEXT user_agent
        TIMESTAMPTZ created_at
    }

    page_views {
        UUID id PK
        VARCHAR page_type
        UUID player_id FK
        UUID event_id FK
        DATE viewed_at
        INTEGER count
        TIMESTAMPTZ created_at
    }

    sms_verification_codes {
        UUID id PK
        VARCHAR user_type
        UUID user_id
        VARCHAR code_hash
        INTEGER attempts
        TIMESTAMPTZ expires_at
        BOOLEAN verified
        TIMESTAMPTZ created_at
    }

ENUM型定義

user_role(ユーザーロール)

説明
admin協会管理者
player選手
staff協会スタッフ
conceptconcept管理者(スポンサー営業)

ticket_route(チケット販売経路)

説明
player_pia選手QR/URL経由(ぴあ)
player_eplus選手URL経由(イープラス)
association_lp協会LP経由(ぴあ)
player_cash選手手売り(現金)
player_transfer選手手売り(振込)
day_ticket当日券

lp_status(LP公開ステータス)

説明
draft下書き
reviewingレビュー中
published公開中
unpublished非公開

concept_status(スポンサー営業ステータス)

説明
untouched未接触
approachedアプローチ済
negotiating交渉中
closed成約
passed見送り

notification_type(通知種別)

説明
purchaseチケット購入通知
deadline締切通知
lp_publishedLP公開通知
reward_confirmed還元額確定通知
reward_paid還元支払完了通知

notification_method(通知方法)

説明
smsSMS
pushWeb Push
bothSMS + Web Push

payment_method(支払方法)

説明
cash現金
transfer振込
otherその他

テーブル一覧

#テーブル名説明論理削除RLS
1players選手deleted_at有効
2admins管理者(admin/staff/concept)deleted_at有効
3events試合・イベントdeleted_at有効
4event_players試合 x 選手 中間テーブル-有効
5fansファン(購入者DB)deleted_at有効
6ticketsチケット販売実績-有効
7lps試合LP(ランディングページ)-有効
8sponsorsスポンサー企業deleted_at有効
9kickbacksキックバック(concept紹介料)-有効
10player_rewards選手還元(LP経由売上の還元管理)-有効
11notifications通知-有効
12audit_logs操作ログ-有効
13page_viewsページ閲覧数(ランキング用)-有効
14sms_verification_codesSMS認証コード(一時テーブル)-有効

テーブル詳細

1. players(選手)

カラムNOT NULLデフォルト説明
idUUIDPKgen_random_uuid()主キー
login_idVARCHAR(50)UK, NOT NULL-ログインID
password_hashVARCHAR(255)NOT NULL-bcryptハッシュ
nameVARCHAR(100)NOT NULL-表示名
name_kanaVARCHAR(100)--カナ名
gymVARCHAR(100)--ジム・所属
weight_classVARCHAR(50)--階級
profile_textTEXT--プロフィール文
profile_image_urlTEXT--プロフィール画像URL
phone_encryptedTEXT--電話番号(AES-256暗号化)
email_encryptedTEXT--メールアドレス(AES-256暗号化)
sns_linksJSONB-'{}'SNSリンク(JSON)
slugVARCHAR(100)UK, NOT NULL-公開ページURL用スラッグ
roleuser_role-'player'ロール
is_lockedBOOLEAN-FALSEアカウントロック状態
failed_attemptsINTEGER-0連続失敗回数
locked_atTIMESTAMPTZ--ロック日時
last_password_hashVARCHAR(255)--前回パスワードハッシュ(再使用防止)
must_change_passwordBOOLEAN-TRUE初回ログイン時パスワード変更要否
password_changed_atTIMESTAMPTZ--パスワード変更日時
phone_verifiedBOOLEAN-FALSESMS認証済フラグ
push_subscriptionJSONB--Web Push購読情報
created_atTIMESTAMPTZ-NOW()作成日時
updated_atTIMESTAMPTZ-NOW()更新日時
deleted_atTIMESTAMPTZ--論理削除日時

セキュリティ: phone_encrypted, email_encrypted はAES-256で暗号化して保存。5回連続ログイン失敗でアカウントロック。

2. admins(管理者)

カラムNOT NULLデフォルト説明
idUUIDPKgen_random_uuid()主キー
login_idVARCHAR(50)UK, NOT NULL-ログインID
password_hashVARCHAR(255)NOT NULL-bcryptハッシュ
nameVARCHAR(100)NOT NULL-名前
phone_encryptedTEXT--電話番号(AES-256暗号化)
roleuser_roleNOT NULL-ロール(admin/staff/concept)
is_lockedBOOLEAN-FALSEロック状態
failed_attemptsINTEGER-0連続失敗回数
locked_atTIMESTAMPTZ--ロック日時
last_password_hashVARCHAR(255)--前回パスワードハッシュ
must_change_passwordBOOLEAN-TRUE初回パスワード変更要否
phone_verifiedBOOLEAN-FALSESMS認証済フラグ
push_subscriptionJSONB--Web Push購読情報
created_atTIMESTAMPTZ-NOW()作成日時
updated_atTIMESTAMPTZ-NOW()更新日時
deleted_atTIMESTAMPTZ--論理削除日時

CHECK制約: role IN ('admin', 'staff', 'concept')

3. events(試合・イベント)

カラムNOT NULLデフォルト説明
idUUIDPKgen_random_uuid()主キー
nameVARCHAR(100)NOT NULL-試合名
event_dateDATENOT NULL-開催日
venueVARCHAR(100)NOT NULL-会場
door_open_timeTIME--開場時刻
start_timeTIMENOT NULL-開始時刻
ticket_sale_dateDATENOT NULL-チケット発売日
ticket_deadline_dateDATENOT NULL-チケット販売締切日
seat_typesJSONBNOT NULL-席種情報 [{name, price}]
sales_channelsTEXT[]NOT NULL-販売チャネル ['pia', 'eplus', 'cash']
pia_base_urlTEXT--ぴあベースURL
descriptionTEXT--試合紹介テキスト
promo_imagesTEXT[]-'{}'宣材画像URL配列
statusVARCHAR(20)-'upcoming'ステータス
created_byUUIDFK→admins-作成者(管理者)
created_atTIMESTAMPTZ-NOW()作成日時
updated_atTIMESTAMPTZ-NOW()更新日時
deleted_atTIMESTAMPTZ--論理削除日時

ステータス値: upcoming, ongoing, completed, cancelled

4. event_players(試合 x 選手 中間テーブル)

カラムNOT NULLデフォルト説明
idUUIDPKgen_random_uuid()主キー
event_idUUIDFK→events, NOT NULL-試合ID
player_idUUIDFK→players, NOT NULL-選手ID
quotaINTEGER-0ノルマ枚数
ref_codeVARCHAR(100)UK-リファレンスコード sb_player007_event001
pia_urlTEXT--ぴあ参照付き完全URL
eplus_urlTEXT--イープラスURL
promo_imagesTEXT[]--宣材写真URL(複数)
reward_rateDECIMAL(5,2)-0LP経由売上の還元率(%)
created_atTIMESTAMPTZ-NOW()作成日時

UNIQUE制約: (event_id, player_id) -- 同一試合に同一選手を二重登録防止 外部キー動作: ON DELETE CASCADE(試合/選手削除時に自動削除)

5. fans(ファン・購入者DB)

カラムNOT NULLデフォルト説明
idUUIDPKgen_random_uuid()主キー
player_idUUIDFK→players, NOT NULL-紐づく選手
nameVARCHAR(100)--ファン名
phone_encryptedTEXT--電話番号(AES-256暗号化)
email_encryptedTEXT--メール(AES-256暗号化)
memoTEXT--メモ
last_purchase_event_idUUIDFK→events-最終購入イベント
last_purchase_atTIMESTAMPTZ--最終購入日時
total_purchasesINTEGER-0累計購入枚数
is_dormantBOOLEAN-FALSE掘り起こし対象フラグ
created_atTIMESTAMPTZ-NOW()作成日時
updated_atTIMESTAMPTZ-NOW()更新日時
deleted_atTIMESTAMPTZ--論理削除日時

ドメインロジック: 直近2試合で購入のないファンは is_dormant = TRUE に更新(掘り起こし対象)

6. tickets(チケット販売実績)

カラムNOT NULLデフォルト説明
idUUIDPKgen_random_uuid()主キー
event_idUUIDFK→events, NOT NULL-試合ID
player_idUUIDFK→players-販売選手(NULL=協会枠/当日券)
fan_idUUIDFK→fans-購入ファン
seat_typeVARCHAR(50)NOT NULL-席種
quantityINTEGERNOT NULL-枚数
unit_priceINTEGERNOT NULL-単価(円)
total_amountINTEGERNOT NULL-合計金額
routeticket_routeNOT NULL-販売経路
buyer_nameVARCHAR(100)--購入者名(手入力)
payment_methodpayment_method--支払方法
is_paidBOOLEAN-FALSE支払済フラグ
paid_atTIMESTAMPTZ--支払日時
memoVARCHAR(200)--メモ
webhook_payloadJSONB--ぴあWebhook生データ
purchased_atTIMESTAMPTZ-NOW()購入日時
created_atTIMESTAMPTZ-NOW()作成日時
updated_atTIMESTAMPTZ-NOW()更新日時

CHECK制約: quantity > 0

7. lps(試合LP - ランディングページ)

カラムNOT NULLデフォルト説明
idUUIDPKgen_random_uuid()主キー
event_idUUIDFK→events, UK, NOT NULL-試合ID(1:1)
statuslp_status-'draft'公開ステータス
html_contentTEXT--生成HTML
hero_image_urlTEXT--ヒーロー画像URL
ogp_image_urlTEXT--OGP画像URL(1200x630)
meta_titleVARCHAR(200)--メタタイトル
meta_descriptionTEXT--メタディスクリプション
association_ref_codeVARCHAR(100)UK-協会枠リファレンスコード
association_pia_urlTEXT--協会枠ぴあURL
editor_dataJSONB--ビジュアルエディタ保存データ
generated_by_modelVARCHAR(100)--生成AIモデル名
published_atTIMESTAMPTZ--公開日時
published_byUUIDFK→admins-公開承認者
unpublished_atTIMESTAMPTZ--非公開日時
created_atTIMESTAMPTZ-NOW()作成日時
updated_atTIMESTAMPTZ-NOW()更新日時

UNIQUE制約: event_id -- イベントとLP は 1:1 関係

8. sponsors(スポンサー企業)

カラムNOT NULLデフォルト説明
idUUIDPKgen_random_uuid()主キー
player_idUUIDFK→players, NOT NULL-紐づく選手
company_nameVARCHAR(200)NOT NULL-企業名
industryVARCHAR(100)--業種
contact_nameVARCHAR(100)NOT NULL-担当者名
contact_phone_encryptedTEXTNOT NULL-連絡先電話番号(AES-256暗号化)
contact_email_encryptedTEXT--連絡先メール(AES-256暗号化)
logo_urlTEXT--ロゴURL
website_urlTEXT--WebサイトURL
contract_amountINTEGER--契約金額(円)
contract_start_dateDATE--契約開始日
contract_end_dateDATE--契約終了日
concept_sales_statusconcept_status-'untouched'concept営業ステータス
concept_memoTEXT--concept営業メモ
is_public_to_associationBOOLEAN-FALSE協会への公開許可
is_approved_for_displayBOOLEAN-FALSE公開ページ掲載承認
memoTEXT--メモ
created_atTIMESTAMPTZ-NOW()作成日時
updated_atTIMESTAMPTZ-NOW()更新日時
deleted_atTIMESTAMPTZ--論理削除日時

9. kickbacks(キックバック)

カラムNOT NULLデフォルト説明
idUUIDPKgen_random_uuid()主キー
sponsor_idUUIDFK→sponsors, NOT NULL-スポンサーID
player_idUUIDFK→players-選手ID
contract_amountINTEGERNOT NULL-concept AGI受注金額(円)
kickback_rateDECIMAL(5,2)NOT NULL-キックバック割合(%)
kickback_amountINTEGERGENERATED STORED-キックバック金額(自動計算)
payment_due_dateDATE--支払期日
is_paidBOOLEAN-FALSE支払済フラグ
paid_dateDATE--支払日
transfer_info_encryptedTEXT--振込先情報(AES-256暗号化)
memoTEXT--メモ
created_atTIMESTAMPTZ-NOW()作成日時
updated_atTIMESTAMPTZ-NOW()更新日時

GENERATED列: kickback_amount = FLOOR(contract_amount * kickback_rate / 100)

10. player_rewards(選手還元)

カラムNOT NULLデフォルト説明
idUUIDPKgen_random_uuid()主キー
event_idUUIDFK→events, NOT NULL-試合ID
player_idUUIDFK→players, NOT NULL-選手ID
lp_sales_amountINTEGER-0LP経由売上金額
reward_rateDECIMAL(5,2)NOT NULL-還元率(%)
reward_amountINTEGERGENERATED STORED-還元金額(自動計算)
statusVARCHAR(20)-'pending'ステータス
payment_due_dateDATE--支払期日
paid_dateDATE--支払日
confirmed_atTIMESTAMPTZ--確定日時
confirmed_byUUIDFK→admins-確定承認者
memoTEXT--メモ
created_atTIMESTAMPTZ-NOW()作成日時
updated_atTIMESTAMPTZ-NOW()更新日時

GENERATED列: reward_amount = FLOOR(lp_sales_amount * reward_rate / 100) UNIQUE制約: (event_id, player_id) -- 試合ごとに選手1レコード ステータス値: pending, confirmed, paid

11. notifications(通知)

カラムNOT NULLデフォルト説明
idUUIDPKgen_random_uuid()主キー
recipient_typeuser_roleNOT NULL-受信者タイプ
recipient_idUUIDNOT NULL-受信者ID
typenotification_typeNOT NULL-通知種別
methodnotification_methodNOT NULL-通知方法
titleVARCHAR(200)NOT NULL-タイトル
bodyTEXTNOT NULL-本文
related_event_idUUIDFK→events-関連イベント
related_player_idUUIDFK→players-関連選手
is_readBOOLEAN-FALSE既読フラグ
is_sentBOOLEAN-FALSE送信済フラグ
sent_atTIMESTAMPTZ--送信日時
sms_sidVARCHAR(100)--Twilio SMS SID
error_messageTEXT--エラーメッセージ
created_atTIMESTAMPTZ-NOW()作成日時

12. audit_logs(操作ログ)

カラムNOT NULLデフォルト説明
idUUIDPKgen_random_uuid()主キー
actor_typeVARCHAR(10)NOT NULL-操作者タイプ (player/admin/system)
actor_idUUID--操作者ID
actionVARCHAR(50)NOT NULL-アクション名
resource_typeVARCHAR(50)NOT NULL-対象テーブル名
resource_idUUID--対象リソースID
changesJSONB--変更内容 {before, after}
ip_addressINET--クライアントIPアドレス
user_agentTEXT--User-Agent
created_atTIMESTAMPTZ-NOW()作成日時

13. page_views(ページ閲覧数)

カラムNOT NULLデフォルト説明
idUUIDPKgen_random_uuid()主キー
page_typeVARCHAR(20)NOT NULL-ページ種別 (player_page/lp)
player_idUUIDFK→players-選手ID
event_idUUIDFK→events-イベントID
viewed_atDATENOT NULL-閲覧日
countINTEGER-1日次集計カウント
created_atTIMESTAMPTZ-NOW()作成日時

UNIQUE制約: (page_type, player_id, event_id, viewed_at) -- 日次集計の重複防止

14. sms_verification_codes(SMS認証コード)

カラムNOT NULLデフォルト説明
idUUIDPKgen_random_uuid()主キー
user_typeVARCHAR(10)NOT NULL-ユーザー種別 (player/admin)
user_idUUIDNOT NULL-ユーザーID
code_hashVARCHAR(255)NOT NULL-6桁コードのハッシュ
attemptsINTEGER-0試行回数
expires_atTIMESTAMPTZNOT NULL-有効期限(5分後)
verifiedBOOLEAN-FALSE認証済フラグ
created_atTIMESTAMPTZ-NOW()作成日時

アクセス制限: RLSポリシーなし(service_roleキーのみアクセス可)


ビュー

purchases(購入ビュー - ファンCRM用)

CREATE VIEW purchases AS
SELECT
  t.id AS purchase_id,
  t.fan_id,
  t.player_id,
  t.event_id,
  t.seat_type,
  t.quantity,
  t.unit_price,
  t.total_amount,
  t.route,
  t.is_paid,
  t.purchased_at
FROM tickets t
WHERE t.fan_id IS NOT NULL;

fan_id が紐づくチケットのみを抽出し、ファンCRM画面での購入履歴表示に使用。


インデックス一覧

テーブルインデックス名カラム用途
playersidx_players_login_idlogin_idログイン検索
playersidx_players_slugslug公開ページURL解決
playersidx_players_created_atcreated_at時系列ソート
adminsidx_admins_login_idlogin_idログイン検索
adminsidx_admins_roleroleロール別検索
eventsidx_events_dateevent_date日付検索
eventsidx_events_statusstatusステータス検索
eventsidx_events_created_atcreated_at時系列ソート
event_playersidx_ep_eventevent_id試合別検索
event_playersidx_ep_playerplayer_id選手別検索
event_playersidx_ep_ref_coderef_codeリファレンスコード検索
fansidx_fans_playerplayer_id選手別ファン検索
fansidx_fans_dormantis_dormant, player_id掘り起こし対象検索
fansidx_fans_created_atcreated_at時系列ソート
ticketsidx_tickets_eventevent_id試合別チケット検索
ticketsidx_tickets_playerplayer_id選手別チケット検索
ticketsidx_tickets_fanfan_idファン別購入検索
ticketsidx_tickets_routeroute販売経路別検索
ticketsidx_tickets_purchased_atpurchased_at購入日時ソート
lpsidx_lps_eventevent_id試合別LP検索
lpsidx_lps_statusstatusステータス検索
sponsorsidx_sponsors_playerplayer_id選手別スポンサー検索
sponsorsidx_sponsors_concept_statusconcept_sales_status営業ステータス検索
kickbacksidx_kickbacks_sponsorsponsor_idスポンサー別検索
kickbacksidx_kickbacks_paidis_paid支払状態検索
player_rewardsidx_rewards_eventevent_id試合別還元検索
player_rewardsidx_rewards_playerplayer_id選手別還元検索
player_rewardsidx_rewards_statusstatusステータス検索
notificationsidx_notifications_recipientrecipient_type, recipient_id受信者別検索
notificationsidx_notifications_readis_read未読検索
notificationsidx_notifications_created_atcreated_at時系列ソート
audit_logsidx_audit_actoractor_type, actor_id操作者別検索
audit_logsidx_audit_resourceresource_type, resource_idリソース別検索
audit_logsidx_audit_actionactionアクション別検索
audit_logsidx_audit_createdcreated_at時系列ソート
page_viewsidx_pv_dateviewed_at日付検索
page_viewsidx_pv_playerplayer_id選手別PV検索
page_viewsidx_pv_eventevent_idイベント別PV検索
sms_verification_codesidx_sms_useruser_type, user_idユーザー別検索
sms_verification_codesidx_sms_expiresexpires_at有効期限検索

RLSポリシー

全14テーブルでRow Level Securityが有効化されている。

ヘルパー関数

関数戻り値説明
auth.get_user_role()user_roleJWTのapp_metadata.roleからロールを取得
auth.get_user_id()UUIDJWTのapp_metadata.user_idからIDを取得
auth.is_admin()BOOLEANadmin/staffロールか判定
auth.is_concept()BOOLEANconceptロールか判定

ポリシー一覧

players

ポリシー名操作条件
players_select_ownSELECTrole='player' AND id=自分
players_select_adminSELECTadmin/staff
players_select_conceptSELECTconcept
players_update_ownUPDATErole='player' AND id=自分
players_update_adminUPDATEadmin
players_insert_adminINSERTadmin

admins

ポリシー名操作条件
admins_select_adminSELECTadmin
admins_select_ownSELECTid=自分
admins_insert_adminINSERTadmin
admins_update_adminUPDATEadmin
admins_update_ownUPDATEid=自分

events

ポリシー名操作条件
events_select_authenticatedSELECT全認証ユーザー
events_insert_adminINSERTadmin
events_update_adminUPDATEadmin
events_delete_adminDELETEadmin

event_players

ポリシー名操作条件
ep_select_ownSELECTrole='player' AND player_id=自分
ep_select_adminSELECTadmin/staff
ep_insert_adminINSERTadmin
ep_update_adminUPDATEadmin
ep_delete_adminDELETEadmin

tickets

ポリシー名操作条件
tickets_select_ownSELECTrole='player' AND player_id=自分
tickets_select_adminSELECTadmin/staff
tickets_insert_playerINSERTrole='player' AND player_id=自分
tickets_insert_adminINSERTadmin/staff
tickets_update_adminUPDATEadmin/staff

fans

ポリシー名操作条件
fans_select_ownSELECTrole='player' AND player_id=自分
fans_insert_ownINSERTrole='player' AND player_id=自分
fans_update_ownUPDATErole='player' AND player_id=自分
fans_delete_ownDELETErole='player' AND player_id=自分
fans_select_adminSELECTadmin/staff

lps

ポリシー名操作条件
lps_select_publishedSELECTstatus='published'(匿名含む)
lps_select_authenticatedSELECT全認証ユーザー
lps_insert_adminINSERTadmin
lps_update_adminUPDATEadmin
lps_delete_adminDELETEadmin

sponsors

ポリシー名操作条件
sponsors_select_ownSELECTrole='player' AND player_id=自分
sponsors_select_adminSELECTadmin/staff
sponsors_select_conceptSELECTconcept
sponsors_insert_ownINSERTrole='player' AND player_id=自分
sponsors_update_ownUPDATErole='player' AND player_id=自分
sponsors_update_conceptUPDATEconcept
sponsors_update_adminUPDATEadmin

kickbacks

ポリシー名操作条件
kickbacks_select_adminSELECTadmin/concept
kickbacks_insert_adminINSERTadmin/concept
kickbacks_update_adminUPDATEadmin/concept
kickbacks_delete_adminDELETEadmin

選手はアクセス不可。

player_rewards

ポリシー名操作条件
rewards_select_ownSELECTrole='player' AND player_id=自分
rewards_select_adminSELECTadmin/staff
rewards_insert_adminINSERTadmin/staff
rewards_update_adminUPDATEadmin/staff

notifications

ポリシー名操作条件
notifications_select_ownSELECTrecipient_id=自分
notifications_update_ownUPDATErecipient_id=自分
notifications_insert_adminINSERTadmin/concept

audit_logs

ポリシー名操作条件
audit_select_adminSELECTadmin/concept
audit_insert_authenticatedINSERT全認証ユーザー

page_views

ポリシー名操作条件
pv_insert_anonINSERT誰でも(匿名含む)
pv_select_adminSELECTadmin/staff
pv_select_ownSELECTrole='player' AND player_id=自分

sms_verification_codes

RLSポリシーなし。service_roleキー経由のサーバーサイド処理のみアクセス可能。


ストアド関数

1. calculate_achievement_rate

選手のチケット販売達成率を計算する。

項目内容
引数p_event_id UUID, p_player_id UUID
戻り値JSONB ({event_id, player_id, quota, sold, achievement_rate, remaining})
言語plpgsql
揮発性STABLE
ロジックevent_playersからノルマ取得、ticketsから販売枚数集計、達成率 = sold / quota * 100

2. get_dig_out_fans

直近2試合で購入のないファンを取得する(掘り起こし対象)。

項目内容
引数p_player_id UUID
戻り値TABLE (fan_id, fan_name, last_purchase_at, last_event_name, total_purchases, memo)
言語plpgsql
揮発性STABLE
ロジック直近2試合のIDを取得し、そのいずれでも購入がないファン、または一度も購入していないファンを返す

3. get_dashboard_stats

管理者ダッシュボード用KPIを取得する。

項目内容
引数p_event_id UUID
戻り値JSONB
言語plpgsql
揮発性STABLE
ロジック総チケット数、総売上、販売経路別集計、席種別集計、選手別集計(達成率込み)、LP状態を返す

戻り値構造:

{
  "event": { "id", "name", "event_date", "venue", "status" },
  "summary": { "total_tickets", "total_revenue", "lp_status" },
  "by_route": [{ "route", "quantity", "revenue" }],
  "by_seat_type": [{ "seat_type", "quantity", "revenue" }],
  "by_player": [{ "player_id", "player_name", "quota", "sold", "revenue", "achievement_rate" }]
}

4. update_dig_out_flags

掘り起こし対象フラグの一括更新。直近2試合で購入のないファンに is_dormant = TRUE を設定する。

項目内容
引数なし
戻り値JSONB ({flagged_as_dormant, cleared_dormant, executed_at})
言語plpgsql
揮発性VOLATILE
ロジック全選手のファンについて、直近2試合の購入有無でフラグを更新/解除

5. calculate_rewards

試合ごとのLP経由売上から選手還元を一括計算する。

項目内容
引数p_event_id UUID
戻り値JSONB ({event_id, players_calculated, details, calculated_at})
言語plpgsql
揮発性VOLATILE
ロジックevent_playersの各選手について、LP経由(association_lpルート)の売上を集計し、還元率を掛けてplayer_rewardsにUPSERT

トリガー

update_updated_at_column

BEFORE UPDATEトリガーにより、更新時にupdated_atカラムを自動的に現在日時に設定する。

対象テーブル: players, admins, events, tickets, fans, lps, sponsors, kickbacks, player_rewards

CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
  NEW.updated_at = NOW();
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

暗号化方針

以下のカラムはAES-256で暗号化して保存される。アプリケーション層で暗号化/復号を行う。

テーブルカラム対象データ
playersphone_encrypted電話番号
playersemail_encryptedメールアドレス
fansphone_encrypted電話番号
fansemail_encryptedメールアドレス
sponsorscontact_phone_encrypted担当者電話番号
sponsorscontact_email_encrypted担当者メールアドレス
kickbackstransfer_info_encrypted振込先情報

論理削除

以下のテーブルはdeleted_atカラムによる論理削除を採用。物理削除は行わない。