データベース(コア)
プロジェクトと候補者を中心とした 5 テーブル。
users
概要
管理画面のユーザー。Google アカウントに 1 対 1 で対応する。面接官もこのテーブルのレコードとして表現される。
テーブル定義
| 論理名 | 物理名 | カラム名 | データ型 | 主キー | リレーション | ユニーク | NULL許可 | デフォルト値 | 備考 |
|---|---|---|---|---|---|---|---|---|---|
| ユーザー | users |
id |
integer | ◯ | auto-increment | ||||
google_id |
varchar(255) | ◯ | Google アカウントの一意 ID | ||||||
email |
varchar(255) | ◯ | |||||||
name |
varchar(255) | 表示名 | |||||||
avatar_url |
varchar(500) | ◯ | Google プロフィール画像 | ||||||
role |
user_role | 'member' |
admin / member / viewer |
||||||
created_at |
timestamp | now() |
|||||||
updated_at |
timestamp | now() |
インデックス
- 主キー (
id) - UNIQUE:
google_id - UNIQUE:
email - INDEX:
idx_users_email(email)
注記
- ユーザーの新規作成は Google OAuth コールバック時に自動で行われる(初回ログインで登録)
roleの既定はmember。viewerへの降格やadminへの昇格は現状 DB 直接更新reservations.interviewer_idがonDelete: restrictで参照するため、予約が残るユーザーは削除できない
projects
概要
1 回のインタビュー企画。面接条件・スコアリングルール・アンケートインポート設定・アレンジフローの状態をすべて保持する。
テーブル定義
| 論理名 | 物理名 | カラム名 | データ型 | 主キー | リレーション | ユニーク | NULL許可 | デフォルト値 | 備考 |
|---|---|---|---|---|---|---|---|---|---|
| プロジェクト | projects |
id |
integer | ◯ | auto-increment | ||||
name |
varchar(255) | プロジェクト名 | |||||||
description |
text | ◯ | |||||||
status |
project_status | 'draft' |
draft / active / completed / archived |
||||||
interview_duration_minutes |
integer | 60 |
面接 1 件の所要時間(分) | ||||||
scheduling_range_start_days |
integer | 1 |
調整可能期間の開始(今日から N 日後) | ||||||
scheduling_range_end_days |
integer | 14 |
調整可能期間の終了(今日から N 日後) | ||||||
scoring_rules |
json | ◯ | ScoringRules 型 |
||||||
interview_type |
interview_type | 'any' |
online_meet / online_zoom / offline / any |
||||||
google_calendar_id |
varchar(255) | ◯ | プロジェクト専用カレンダー | ||||||
survey_auto_import_enabled |
boolean | false |
自動インポートの有効化 | ||||||
survey_id |
varchar(50) | ◯ | 紐付けるアンケート ID(databricks_surveys.survey_id を論理参照) |
||||||
survey_import_mapping |
json | ◯ | ImportMapping 型 |
||||||
survey_import_filter |
json | ◯ | ImportFilter 型 |
||||||
survey_last_imported_at |
timestamp | ◯ | 最終インポート日時 | ||||||
arrange_settings |
json | ◯ | ArrangeSettings 型(STEP1〜3) |
||||||
created_by |
integer | users:id(論理) | 作成者 | ||||||
created_at |
timestamp | now() |
|||||||
updated_at |
timestamp | now() |
リレーション
created_by→users.id:Drizzle のrelations()上でのみ定義されており、DB レベルの外部キー制約は張られていない- 子テーブル(
onDelete: cascade):project_candidates/project_interviewers/email_templates/import_logs
インデックス
- 主キー (
id) - INDEX:
idx_projects_status(status) - INDEX:
idx_projects_created_by(created_by)
注記
arrange_settingsはアレンジフローの状態を JSON カラム 1 個に丸ごと 保存する設計。STEP ごとにテーブルを分けていないscoring_rules.max_scoreが指定されている場合、算出スコアの上限になる- 調整可能期間は絶対日付ではなく「今日からの相対日数」で保持する
candidates
概要
個人としての候補者。プロジェクトをまたいで一意であり、同じ人物が複数プロジェクトに参加しても 1 レコード。
テーブル定義
| 論理名 | 物理名 | カラム名 | データ型 | 主キー | リレーション | ユニーク | NULL許可 | デフォルト値 | 備考 |
|---|---|---|---|---|---|---|---|---|---|
| 候補者 | candidates |
id |
integer | ◯ | auto-increment | ||||
external_user_id |
varchar(255) | ◯ | 外部システム(アンケート基盤)の回答者 ID | ||||||
email |
varchar(255) | 個人情報 | |||||||
phone |
varchar(50) | ◯ | 個人情報 | ||||||
name |
varchar(255) | 個人情報 | |||||||
created_at |
timestamp | now() |
|||||||
updated_at |
timestamp | now() |
インデックス
- 主キー (
id) - UNIQUE:
external_user_id - INDEX:
idx_candidates_email(email)
注記
emailは UNIQUE ではない。同一メールアドレスの別回答者を許容するため、一意キーはexternal_user_idemail/phone/nameは個人情報であり、保持期間(既定 3 ヶ月)超過後にpii-cleanupバッチで匿名化される- 匿名化後も
project_candidatesなどの関連レコードは集計のため残る
project_candidates
概要
候補者のプロジェクトへの参加。ステータス・スコア・選定区分・回答内容といった「プロジェクト文脈の情報」をすべて持つ、システムの中心テーブル。
テーブル定義
| 論理名 | 物理名 | カラム名 | データ型 | 主キー | リレーション | ユニーク | NULL許可 | デフォルト値 | 備考 |
|---|---|---|---|---|---|---|---|---|---|
| プロジェクト候補者 | project_candidates |
id |
integer | ◯ | auto-increment | ||||
project_id |
integer | projects:id (cascade) | |||||||
candidate_id |
integer | candidates:id (cascade) | |||||||
attributes |
jsonb | ◯ | 属性(都道府県・年齢など) | ||||||
survey_responses |
jsonb | ◯ | アンケート回答(横持ち) | ||||||
score |
decimal(10,2) | ◯ | スコアリング結果 | ||||||
selection_type |
selection_type | ◯ | primary(本命)/ reserve(補欠) |
||||||
segment_id |
varchar(255) | ◯ | arrange_settings.step2.segments[].id を参照する論理キー |
||||||
memo |
text | ◯ | 自由記述メモ | ||||||
priority |
varchar(1) | ◯ | 優先度(A / B / C) |
||||||
status |
pc_status | 'not_contacted' |
10 値の enum | ||||||
created_at |
timestamp | now() |
|||||||
updated_at |
timestamp | now() |
リレーション
project_id→projects.id(onDelete: cascade)candidate_id→candidates.id(onDelete: cascade)- 子テーブル(onDelete: cascade):
email_logs/scheduling_tokens/candidate_available_dates/reservations/status_histories
インデックス
- 主キー (
id) - UNIQUE INDEX:
uk_project_candidate(project_id,candidate_id) - INDEX:
idx_pc_project_status(project_id,status) - INDEX:
idx_pc_project_score(project_id,score)
注記
uk_project_candidateにより、同一プロジェクトへの二重登録は不可statusの遷移はVALID_STATUS_TRANSITIONSに定義された組み合わせのみ許可される(機能 の遷移図を参照)。不正な遷移はInvalidStatusTransitionErrorsegment_idは他テーブルへの外部キーではなく、projects.arrange_settings内のセグメント定義を指す文字列attributesとsurvey_responsesはどちらもjsonb。候補者一覧のresponseFilterクエリはこの 2 つを対象に絞り込む
project_interviewers
概要
プロジェクトと面接官(ユーザー)の中間テーブル。
テーブル定義
| 論理名 | 物理名 | カラム名 | データ型 | 主キー | リレーション | ユニーク | NULL許可 | デフォルト値 | 備考 |
|---|---|---|---|---|---|---|---|---|---|
| プロジェクト面接官 | project_interviewers |
id |
integer | ◯ | auto-increment | ||||
project_id |
integer | projects:id (cascade) | |||||||
user_id |
integer | users:id (cascade) | |||||||
created_at |
timestamp | now() |
リレーション
project_id→projects.id(onDelete: cascade)user_id→users.id(onDelete: cascade)
インデックス
- 主キー (
id) - UNIQUE INDEX:
uk_project_user(project_id,user_id)
注記
- メイン/サブの区別はこのテーブルではなく
projects.arrange_settings.step1.mainInterviewerIds/subInterviewerIdsに持つ - 空き枠算出はこのテーブルに登録された面接官の Google Calendar を参照する