データモデル Data model

管理者ポータルが触る主要エンティティ・フィールド・リレーションをドメイン別に整理しました。 全 schema (public / admin / marketing / analytics / extensions) の俯瞰は 共通データモデルを、TS 型は parky/web/portal/admin/src/lib/api.ts を参照してください。 スキーマと TS 型がずれた場合はマイグレーション側を正とし、TS 型をフォローする運用です。

Core admin-portal entities, fields, and relationships grouped by domain. For the cross-product schema overview (public / admin / marketing / analytics / extensions) see the shared data model; TS interfaces live in parky/web/portal/admin/src/lib/api.ts. When schema and TS types diverge, migrations win — TS follows.

Schema 配置: Schema placement: admins / roles / role_permissions / admin_tasks / admin_notifications / admin_activity_logs / blocked_email_hashes / app_config(+history) / notification_failuresadmin schema 配下で service_role only。アクセスは Cloudflare Workers BFF(/v1/admin/*)経由のみ(Supabase Edge Functions は使用しない)。public 側のテーブルとはアクセス権限が異なる。 admins / roles / role_permissions / admin_tasks / admin_notifications / admin_activity_logs / blocked_email_hashes / app_config(+history) / notification_failures live in the admin schema and are service_role only. Reach them only through the Cloudflare Workers BFF (/v1/admin/*) — Supabase Edge Functions are not used.

ドメイン俯瞰図 Domain map

flowchart LR
  subgraph Parking["🅿️ Parking domain"]
    PL[parking_lots]
    PLI[parking_lot_images]
    PLT[parking_lot_tags]
    PLO[parking_lot_owners]
    PLH[parking_lot_hours]
    PLDO[parking_lot_date_overrides]
    %% parking_lot_attributes (028) と parking_lot_payment_methods (057) は tags に統合済み
    PR[parking_reviews]
    PS[parking_sessions]
  end
  subgraph Users["👤 Users"]
    AU[app_users]
    USP[user_saved_parkings]
    UAL[user_activity_logs]
    US[user_subscriptions]
  end
  subgraph Owner["🏢 Owners"]
    OW[owners]
    OA[owner_applications]
    OC[owner_credits]
    CT[credit_transactions]
    B[boosts]
  end
  subgraph Game["🏆 Gamification"]
    BD[badge_definitions]
    UBP[user_badge_progress]
    UE[user_exp]
    LD[level_definitions]
    AER[activity_exp_rules]
  end
  subgraph Theme["🎨 Customization"]
    CTh[customization_themes]
    CTI[customization_theme_items]
    CTP[customization_theme_parts]
    TG[theme_gifts]
    UT[user_themes]
  end
  subgraph Ops["🛠 Operations"]
    ST[support_tickets]
    ER[error_reports]
    AT[admin_tasks]
    UN[user_notifications]
    AN[admin_notifications]
  end
  subgraph Content["📝 Content"]
    ART[articles]
    ADS[ads]
    TAG[tags]
  end
  subgraph Rev["💰 Revenue"]
    SP[subscription_plans]
    RT[revenue_transactions]
    RMS[(revenue_monthly_summary
view)] end subgraph Sys["⚙️ System"] AD[admins] RL[roles] RP[role_permissions] AS[assets] CD[codes] end PL --> PLI PL --> PLT PL --> PLH PL --> PLDO PL --> PLO PL --> PR PL --> PS PLO --> OW AU --> PS AU --> PR AU --> USP AU --> UAL AU --> US AU --> UE AU --> UBP OW --> OA OW --> OC OW --> CT OW --> B B --> PL BD --> UBP LD --> UE CTh --> CTI CTI --> CTP CTh --> TG TG --> UT AT -.-> ST AT -.-> ER AT -.-> OA AT -.-> PL SP --> US RT --> SP AD --> RL RL --> RP

主要テーブル定義 Core table definitions

parking_lots core

駐車場の中核テーブル。座標 SoT は PostGIS の location (geography 4326)、lat/lng はトリガー派生。shape_type で point/line/area を切替、code (citext) は自然キー。周辺検索は nearby_parking_lots() RPC。

The core lot table. Coordinate SoT is PostGIS location (geography 4326); lat/lng are trigger-derived. shape_type switches point/line/area, code (citext) is the natural key. Nearby search via nearby_parking_lots().

FieldTypeNotes
iduuidPK
codecitext NOT NULL自然キー (URL / 連携 ID)Natural key
nametext NOT NULL
addresstext
locationgeography(Point, 4326)座標 SoTCoordinate SoT
lat / lngdouble precision表示用 (派生)Display-only (derived)
areageometryshape_type='area' 用ポリゴンPolygon for shape_type='area'
shape_typetext NOT NULL (default 'point')point / line / area
parent_id / variant_labeluuid / textバリアント分割用 (子駐車場の親参照)Parent / variant label for sub-lots
total_spacesint
operating_hourstext表示用文字列。詳細は parking_lot_hoursDisplay string; detail in parking_lot_hours
structure / entry_method / entry_difficultytextcodes 値で保持Stored as code values
max_height_m / max_width_m / max_length_m / max_weight_tnumeric車両制限Vehicle limits
min_clearance_cm / max_tire_width_mmint最低地上高 / タイヤ幅制限Min clearance / max tire width
max_parking_duration_minint連続駐車可能時間 (分)Max continuous parking (minutes)
receipt_availableboolean領収書発行可否Receipt available
operator_code / place_idtext運営会社コード / Google Places IDOperator code / Google Places ID
statustext NOT NULL (default 'active')codes.parking_lot_status: pending / active / on_hold / withdrawn
sourcetext (default 'manual')データ取得元Data origin
raw_texttext取込元の生データ (デバッグ用)Raw ingest text (debug)
rulesjsonb (default '[]')料金ルールの旧 jsonb 列。2026-04-24 階層化以降は parking_lot_pricing_rules + parking_lot_pricing_groups が正本。互換のため残置Legacy jsonb of pricing rules. Authoritative source is now parking_lot_pricing_rules + parking_lot_pricing_groups; this column is kept for backwards compatibility only.
created_at / updated_at / deleted_attimestamptz

parking_lot_pricing_groups / parking_spots core 2026-04-24 new

2026-04-24 に lot → pricing_group → spot の 3 階層を導入。parking_lot_pricing_rulespricing_group_id NOT NULL、parking_sessionsparking_spot_id NOT NULL になり、新規セッション作成時には spot を必ず確定させる必要がある。

2026-04-24 introduced the lot → pricing_group → spot hierarchy. parking_lot_pricing_rules.pricing_group_id is NOT NULL and parking_sessions.parking_spot_id is NOT NULL — every new session must pin to a specific spot.

FieldTypeNotes
parking_lot_pricing_groups.iduuidPK
parking_lot_pricing_groups.parking_lot_iduuid NOT NULLFK
parking_lot_pricing_groups.codecitext NOT NULLlot 内一意Unique within a lot
parking_lot_pricing_groups.name / is_default / display_ordertext / boolean / int
parking_spots
iduuidPK
parking_lot_id / pricing_group_iduuid NOT NULLFK / FK
codecitext NOT NULL区画コード (例: A-12)Spot code (e.g. A-12)
vehicle_type_max / is_ev_charger / ev_connector_type / accessibilitytext / boolean / text / text
width_mm / length_mm / height_mmint区画寸法Spot dimensions
floorplan_x / floorplan_y / locationnumeric / numeric / geography(Point)場内座標 + GPSFloor-plan XY + GPS
is_reservable / is_active / metaboolean / boolean / jsonb
created_at / updated_at / deleted_attimestamptz

app_users core

auth_user_idauth.users と 1:1 紐付け。退会は status='withdrawn' + PII 匿名化で運用 (deleted_at 列なし)。

Linked 1:1 to auth.users via auth_user_id. Withdrawals use status='withdrawn' + PII anonymization (no deleted_at).

FieldTypeNotes
iduuidPK
auth_user_iduuidFK → auth.users (RLS 主軸)
display_nametext NOT NULL
handletext@ハンドル (UNIQUE)Public handle (UNIQUE)
emailtext NOT NULL
vehicle_typetext (default 'sedan')codes.vehicle_type: sedan / kei / large / bike / minivan / high_roof
premiumboolean (default false)
statustext NOT NULL (default 'active')codes.user_status: active / withdrawn / suspended / blocked
profile_visibilitytext NOT NULL (default 'friends')public / friends / private
device_fingerprintstext[]既知デバイス指紋 (referral 不正検知)Known device fingerprints (referral abuse detection)
signup_ipinet
referral_code_usedtext
last_active_at / created_attimestamptz

通知設定 / 同意履歴 / OS パーミッション / 検索プリセット / ストリーク等は user_notification_prefs / user_consents / user_device_permissions / user_search_presets / user_streaks に分離。

Notification prefs, consents, OS permissions, search presets and streaks live in their own tables (user_notification_prefs, user_consents, user_device_permissions, user_search_presets, user_streaks).

parking_sessions

ユーザーの駐車履歴。parking_spot_id NOT NULL で必ず特定 spot に紐付く。金額は total_amount_minor (bigint, minor unit)。geo_verified / geo_distance_m で位置検証、client_request_id で冪等性。updated_at / deleted_at は無く、cancel は status='cancelled' + cancelled_reason

Per-user parking history. parking_spot_id is NOT NULL — sessions always pin to a specific spot. Amount is bigint in minor units. geo_verified / geo_distance_m validate location; client_request_id enforces idempotency. No updated_at / deleted_at — cancellation uses status='cancelled' + cancelled_reason.

FieldTypeNotes
iduuidPK
user_id / parking_lot_id / parking_spot_iduuid NOT NULL × 3FK / FK / FK (parking_spot_id は 2026-04-24 必須化)
started_at / ended_attimestamptzstarted_at NOT NULL、ended_at NULL=駐車中
planned_end_attimestamptz予定終了時刻 (session_notifications trigger)Planned end (drives session notifications)
statustext NOT NULL (default 'parking')codes.session_status: parking / completed / cancelled
cancelled_reasontext
vehicle_typetextcodes.vehicle_type
total_amount_minorbigint最終確定額 (minor unit)Final billed amount (minor units)
user_entered_fee_minor / fee_mismatch_flagint / booleanユーザー手入力額 + 計算額との突合User-entered fee + calc-vs-entered mismatch flag
start_lat / start_lng / end_lat / end_lngdouble precision
geo_verified / geo_distance_mboolean / numeric入庫時位置検証 (gamification cap で参照)Start-location verification (gamification gate)
client_request_iduuid冪等性キーIdempotency key
memo / bad_reasontext
created_attimestamptz

parking_lot_hours core

駐車場の時間窓。1 行 = 1 種別の時間窓 (business / entry / exit / after_hours_exit)。 曜日ロジックは day_of_week / day_type、UNIQUE は部分 UNIQUE INDEX uq_parking_lot_hours_window_keywindow_type 含む)で担保。

Time windows for a parking lot. Each row represents a single window kind (business / entry / exit / after_hours_exit). Day-of-week logic uses day_of_week / day_type; uniqueness is enforced via the partial UNIQUE index uq_parking_lot_hours_window_key (which includes window_type).

FieldTypeNotes
id, parking_lot_iduuidPK, FK
window_typetext NOT NULLbusiness / entry / exit / after_hours_exit (codes.parking_hours_window_type)
day_typetextcodes.day_type: weekday / saturday / sunday / holiday / holiday_eve / all (NULL 可)
day_of_weeksmallint (0-6)0=Sun 〜 6=Sat
is_24h, is_closedboolean
open_time, close_timetime / text24:00 表記可
effective_from, effective_todate期間限定ルール用

parking_lot_date_overrides core 2026-04-24 new

特定日の営業時間・料金オーバーライド。花火大会や元旦特別営業など、通常ルールを 1 日だけ上書きする例外テーブル。 1 駐車場 × 1 日で 1 行 (UNIQUE parking_lot_id + override_date)。hours jsonb で business / entry / exit / after_hours_exit の任意キーを上書き、pricing jsonb 配列で料金を置換 (NULL=通常通り / []=当日無料)。

Per-day hour / pricing overrides for a parking lot (fireworks, New-Year specials, …). One row per lot × date; hours jsonb overrides any subset of business / entry / exit / after_hours_exit, pricing jsonb array replaces rules (NULL = normal, [] = free that day).

FieldTypeNotes
iduuidPK
parking_lot_iduuidFK (ON DELETE CASCADE)
override_datedate対象日 (JST)
labeltext NOT NULL例外の名称 (例: 隅田川花火大会)
hoursjsonbキー: business / entry / exit / after_hours_exit。省略キーは通常ルールへフォールバック
pricingjsonb (array)NULL=通常 / []=無料 / 配列=置換
notetext社内メモ
RLSSELECT 公開 / 書込は admin または当該 lot の owner (current_owner_id())

helper: get_parking_lot_date_override(lot_id, date)。 優先順位 (時刻判定 RPC が参照): 日付オーバーライド → 祝日行 → 曜日行 → day_type → all フォールバック。詳細は 2026-04-24 ADR

jp_holidays master 2026-04-24 new

日本の祝日マスター。date PK / kind∈(national / substitute / national_holiday_bridge)。2026・2027 年の 34 件を seed 済 (振替休日含む)。 helper is_jp_holiday(date) → boolean で参照。parking_lot_hours.day_type='holiday' の判定に使われる。

Japanese holiday master with 34 rows seeded for 2026 / 2027. Used by the window-active RPC to resolve day_type='holiday' rows.

owners, owner_applications, owner_credits, credit_transactions

badge_definitions, user_badge_progress, user_exp, level_definitions, activity_exp_rules gamification

TablePurpose
badge_definitionsバッジ定義 (名前、アイコン、条件、閾値)badge definitions (name, icon, conditions, threshold)
user_badge_progressユーザー × バッジの進捗カウントprogress count per user × badge
user_exp総 EXP と現在レベルtotal EXP and current level
level_definitionsレベル毎の必要 EXPrequired EXP per level
activity_exp_rulesアクティビティ型 → 付与 EXPactivity type → EXP granted
user_activity_logs全ての行動イベント。metadata (JSONB) にイベント詳細all activity events; detail in metadata (JSONB)
user_activity_log_targetsログの参照先 (parking / review / user 等) のマルチ参照multi-entity references for each log

customization_themes, ..._items, ..._parts, theme_gifts, user_themes theming

support_tickets, error_reports, admin_tasks ops

admin_tasks が 4 種類のソース (support / misinformation_report / owner_application / parking_new_registration) を task_kind + ref_id で指す設計。ポリモーフィックなリレーション。

admin_tasks polymorphically references four source tables via task_kind + ref_id, which is what enables the unified task inbox.

revenue_transactions & revenue_monthly_summary revenue

admins, roles, role_permissions system

管理者は Supabase Auth ユーザー (auth.users) と admin.admins の 2 層で管理。結合キーは admin.admins.user_id(旧名 auth_user_id ではなく user_id)。emailuq_admins_email で UNIQUE、statusactive / inactive 等。notification_prefs JSONB に通知購読フラグ (new_owner / new_parking / sales_daily / sales_monthly / system_alert) を持つ。ロール定義は admin.roles、付与権限キーは admin.role_permissions (role_id + permission_key UNIQUE)。

Admins are tracked in both Supabase Auth (auth.users) and admin.admins. The join column is admin.admins.user_id (not auth_user_id). email is UNIQUE (uq_admins_email); status uses values such as active / inactive. notification_prefs JSONB stores per-admin subscription flags (new_owner / new_parking / sales_daily / sales_monthly / system_alert). Role definitions live in admin.roles; granted permission keys in admin.role_permissions (role_id + permission_key UNIQUE).

TableFieldNotes
admin.adminsiduuid PK
user_iduuid → auth.users.id(NULL 可:Auth 紐付け前の招待状態を許容)
name / emailtext NOT NULL(email は UNIQUE)
role_iduuid NOT NULL → admin.roles.id
statustext default 'active'
notification_prefsjsonb — 通知購読フラグ
admin.rolesiduuid PK
nametext UNIQUE
color / is_systemUI 表示色 / システム作成ロール保護
admin.role_permissionsrole_iduuid → admin.roles.id
permission_keytext — 例: parking.edit(role_id, permission_key) UNIQUE)

admin_tasks, admin_notifications, admin_activity_logs system

codes master

カテゴリ付きラベル辞書 (user_status / vehicle_type / ...)。起動時に一括ロードされ、UI のドロップダウンとラベル表示に使われます。

A category-keyed label dictionary (user_status / vehicle_type / ...) that's loaded once at boot and powers every dropdown and label in the UI.

TypeScript 型との対応 TypeScript type mapping

各テーブルは src/lib/api.ts に対応する TS インターフェースを持ちます。 代表的なものを以下に示しますが、新しい機能を追加するときは必ずこのファイルを一次情報として確認してください。

Every table has a matching TS interface in src/lib/api.ts. Use that file as the source of truth when adding new features.

TableInterface
parking_lotsParkingLot (+ ParkingRule, RuleTier)
app_usersAppUser
parking_sessionsParkingSession
parking_reviewsParkingReview
ownersOwner
owner_applicationsOwnerApplication
owner_creditsOwnerCredit
credit_transactionsCreditTransaction
boostsBoost
badge_definitionsBadgeDefinition (+ BadgeCondition)
user_badge_progressUserBadgeProgress
user_expUserEXP
level_definitionsLevelDefinition
activity_exp_rulesActivityExpRule
user_activity_logsUserActivityLog
customization_themesCustomizationTheme
customization_theme_partsCustomizationThemePart
articles / adsArticle / Ad
user_notifications / admin_notificationsUserNotification / AdminNotification
admin_tasksAdminTask
admins / rolesAdmin / Role
assetsAsset
subscription_plansSubscriptionPlan
tagsTag
注意Caveat: フィールド定義はシード SQL / マイグレーション (parky/infra/supabase/migrations/) と TS 型の両方で進化します。齟齬があった場合はマイグレーション側を正とし、TS 型をフォローしてください。 Schema lives in both the migrations under parky/infra/supabase/migrations/ and in TypeScript. If they disagree, migrations win — update the TS types to follow.