データベース(日程調整)
候補者との日程調整と、その結果としての予約・履歴を扱う 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 行追加される