コンテンツにスキップ

データベース(日程調整)

候補者との日程調整と、その結果としての予約・履歴を扱う 4 テーブル。


scheduling_tokens

概要

候補者向け公開ページ(/scheduling/{token})へのアクセストークン。認証の代わりにこのトークンが本人性を担保する。

テーブル定義

論理名 物理名 カラム名 データ型 主キー リレーション ユニーク NULL許可 デフォルト値 備考
調整トークン scheduling_tokens id integer ◯ auto-increment
project_candidate_id integer project_candidates:id (cascade)
token varchar(255) ◯ URL に埋め込まれるトークン
expires_at timestamp 有効期限
created_at timestamp now()

リレーション

  • project_candidate_id → project_candidates.id(onDelete: cascade)

インデックス

  • 主キー (id)
  • UNIQUE: token
  • INDEX: idx_st_project_candidate (project_candidate_id)
  • INDEX: idx_st_expires_at (expires_at)

注記

  • 有効期限は発行時に SCHEDULING_TOKEN_TTL_DAYS(既定 14 日)から算出される
  • 1 人の候補者に複数トークンを発行できる(再送のたびに追加される)
  • 期限切れトークンでのアクセスは SchedulingTokenError(400 / SCHEDULING_TOKEN_ERROR)

candidate_available_dates

概要

候補者が公開ページから送信した希望日程。確定前の「候補」を保持する。

テーブル定義

論理名 物理名 カラム名 データ型 主キー リレーション ユニーク NULL許可 デフォルト値 備考
候補者希望日程 candidate_available_dates id integer ◯ auto-increment
project_candidate_id integer project_candidates:id (cascade)
interviewer_id integer users:id (cascade) その枠を担当する面接官
slot_id varchar(255) 空き枠算出時に払い出された枠 ID
start_at timestamp 開始日時
end_at timestamp 終了日時
created_at timestamp now()

リレーション

  • project_candidate_id → project_candidates.id(onDelete: cascade)
  • interviewer_id → users.id(onDelete: cascade)

インデックス

  • 主キー (id)
  • INDEX: idx_cad_project_candidate (project_candidate_id)

注記

  • 希望日程の送信(POST /scheduling/{token}/submit-availability)は全置換。既存レコードを削除してから新しい枠を作成する
  • 1 回の送信で 1〜20 件の枠を受け付ける
  • 送信時、候補者ステータスが contacted / waiting_response / scheduling のいずれかであれば scheduling に更新される
  • 管理者はここから 1 件を選んで confirm-schedule を実行し、reservations を作成する

reservations

概要

確定した面接。プロジェクト候補者につき最大 1 件。

テーブル定義

論理名 物理名 カラム名 データ型 主キー リレーション ユニーク NULL許可 デフォルト値 備考
予約 reservations id integer ◯ auto-increment
project_candidate_id integer project_candidates:id (cascade) ◯ 1 候補者につき 1 件
interviewer_id integer users:id (restrict) 担当面接官
scheduled_at timestamp 面接開始日時
duration_minutes integer 所要時間(分)
interview_type reservation_interview_type online_meet / online_zoom / offline
meeting_url varchar(500) ◯ Google Meet / Zoom の URL
calendar_event_id varchar(255) ◯ Google Calendar のイベント ID
zoom_meeting_id varchar(255) ◯ Zoom ミーティング ID
status reservation_status 'confirmed' confirmed / cancelled
created_at timestamp now()
updated_at timestamp now()

リレーション

  • project_candidate_id → project_candidates.id(onDelete: cascade、UNIQUE)
  • interviewer_id → users.id(onDelete: restrict)

インデックス

  • 主キー (id)
  • UNIQUE: project_candidate_id
  • INDEX: idx_res_interviewer_scheduled (interviewer_id, scheduled_at)
  • INDEX: idx_res_scheduled_at (scheduled_at)

注記

  • キャンセルはレコード削除ではなく status = 'cancelled' への更新
  • interview_type はプロジェクトの interview_type と別 enum で、any を持たない(確定時には形式が決まっているため)
  • 確定時、押さえていた仮枠(Google Calendar の暫定イベント)を確定イベントに変換し、calendar_event_id を保存する
  • イベントはプロジェクトカレンダー(projects.google_calendar_id)にあるため、calendar_event_id を Calendar API で扱うにはそのカレンダー ID と併せて使う
  • PROTOTYPE_MODE=true の間はカレンダー操作がスキップされるため、calendar_event_id は NULL のままになる

status_histories

概要

候補者ステータスの遷移記録。監査ログを兼ねる。

テーブル定義

論理名 物理名 カラム名 データ型 主キー リレーション ユニーク NULL許可 デフォルト値 備考
ステータス履歴 status_histories id integer ◯ auto-increment
project_candidate_id integer project_candidates:id (cascade)
old_status varchar(50) ◯ 遷移前。初期登録時は NULL
new_status varchar(50) 遷移後
changed_by integer users:id(論理) ◯ 変更者。システム変更時は NULL
changed_at timestamp now()
note text ◯ 備考

リレーション

  • project_candidate_id → project_candidates.id(onDelete: cascade)
  • changed_by → users.id:Drizzle の relations() 上でのみ定義されており、DB レベルの外部キー制約は張られていない

インデックス

  • 主キー (id)
  • INDEX: idx_sh_pc_changed (project_candidate_id, changed_at)

注記

  • old_status / new_status は enum ではなく varchar(50)。過去に存在した値が enum から削除されても履歴が壊れないようにするため
  • ステータス更新 API(PATCH /projects/{id}/candidates/{cid}/status)が成功するたびに 1 行追加される