機能
機能一覧
| 機能名 | 概要 | 対応ロール | ステータス |
|---|---|---|---|
| Google OAuth ログイン | Google アカウントで管理画面にログインし、JWT を取得する | 全ユーザー | 実装済み |
| プロジェクト管理 | インタビュー企画の作成・更新・削除・一覧・ダッシュボード | 参照: viewer / 更新: member | 実装済み |
| アレンジフロー(STEP1〜3) | 日程条件・面接官・仮枠 → セグメントと候補者選定 → 追加質問 | member | 実装済み |
| 面接官管理 | プロジェクトへの面接官の追加・削除 | 参照: viewer / 更新: member | 実装済み |
| 空き枠算出 | 面接官の Google Calendar から予約可能枠を算出する | viewer | 実装済み |
| 仮枠押さえ | 算出した枠をプロジェクトカレンダーに暫定イベントとして一括作成/削除 | member | 実装済み |
| 候補者インポート(TSV) | TSV ファイルをアップロードし、列マッピングを指定して取り込む | member | 実装済み |
| 候補者インポート(アンケート) | Databricks のアンケート回答から、マッピングとフィルタを指定して取り込む | member | 実装済み |
| 候補者一覧・絞り込み | ステータス・選定区分・セグメント・スコア・回答内容で絞り込む | viewer | 実装済み |
| スコアリング | プロジェクトのスコアリングルールで候補者を自動採点・再計算する | member | 実装済み |
| 候補者選定 | 本命/補欠の選定区分とセグメントを一括更新する | member | 実装済み |
| ステータス管理 | 候補者ステータスの遷移と履歴記録 | member | 実装済み |
| メールテンプレート管理 | システム共通/プロジェクト個別のテンプレート CRUD | 参照: viewer / 更新: member | 実装済み |
| メール送信 | テンプレートを差し込んで候補者へ一括送信、プレビュー、送信ログ | member | 実装済み |
| 日程調整(候補者向け公開ページ) | トークン URL から案件確認・空き枠取得・希望日程送信 | 認証なし | 実装済み |
| 日程確定 | 候補者の希望日程から予約を作成し、仮枠を確定イベントに変換する | member | 実装済み |
| 予約管理 | 予約の一覧・詳細・キャンセル | 参照: viewer / 更新: member | 実装済み |
| アンケートデータ閲覧 | アンケート・設問・回答者(パネル)・回答のブラウズ | viewer | 実装済み |
| 個人情報クリーンアップ | 保持期間を超えた個人情報をバッチで匿名化する | バッチ | 実装済み |
機能詳細
アレンジフロー(STEP1〜STEP3)
プロジェクト詳細画面の中心となる段階的ウィザード。状態は projects.arrange_settings(JSON カラム 1 個)にまとめて保存され、フロントエンドのロジックは use-project-arrange.ts に集約されている。
対応ロール: member
ユースケース:
-
STEP1 — 日程条件と面接官
- 調整期間(開始日・終了日)、時間帯(開始時刻・終了時刻)、曜日条件を指定する
- メイン面接官・サブ面接官を選択する(複数可)、目標人数を入力する
- 「空き枠を算出」で
GET /projects/{id}/interviewers/availabilityを呼び、面接官の Google Calendar の空き時間から枠を算出する - 算出結果を確認し、「仮枠を押さえる」で
POST /projects/{id}/interviewers/tentative-slotsを実行してプロジェクトカレンダーに暫定イベントを作成する - 作成したイベント情報は
arrange_settings.step1.tentativeEventIdsに保存される
枠の並び方
- 営業時間は平日 9:00〜18:00(JST)。土日は対象外
- 開始時刻は所要時間の倍数。90 分なら
9:00-10:30 / 10:30-12:00 / … / 16:30-18:00の 6 枠で、枠同士は重ならない - 営業時間をはみ出す枠は作らない(90 分なら 16:30 開始が最後)
- 昼休みは除外していない。面接官のカレンダーに予定が入っていれば空き時間の照会で自動的に外れる
- 所要時間は画面で選んだ値が API に渡る(
durationMinutes)。プロジェクトへの保存を待たずに算出結果へ反映される - STEP2 — セグメント分けと候補者選定
- アンケートの設問と選択肢を条件にセグメントを定義する(
arrange_settings.step2.segments) - セグメントごとに候補者を一覧し、本命(primary)/補欠(reserve)を選ぶ
PUT /projects/{id}/candidates/selectionで選定区分とセグメント ID を一括保存する- STEP3 — 追加質問
- セグメント別・候補者別に、面接で聞きたい追加質問を登録する
arrange_settings.step3.questionsに保存される
型の二重定義
ArrangeSettings の型は backend の src/db/schema.ts と frontend の src/types/arrange-settings.ts に二重定義されている(OpenAPI では JSON カラムの内部構造まで表現されないため)。片方を変えたらもう片方も直すこと。
候補者インポート
対応ロール: member
TSV とアンケートの 2 経路がある。いずれも import_logs に履歴が残る(import_type: manual_tsv / manual_survey / auto_survey)。
TSV インポートのユースケース:
POST /projects/{id}/candidates/import/uploadに TSV(UTF-16LE、タブ区切り)をアップロードする- レスポンスの列情報・サンプル値・推奨マッピングを見て、画面上でマッピングを組み立てる
POST /projects/{id}/candidates/import/{fileId}/executeにマッピングを渡して実行する(非同期)GET /projects/{id}/candidates/import/{importId}/statusで進捗と結果を確認する
アンケートインポートのユースケース:
POST /projects/{id}/candidates/import/survey/previewでマッピング・フィルタを指定し、取り込み結果を試算する- 問題なければ
POST /projects/{id}/candidates/import/surveyで実行する GET /projects/{id}/candidates/import/historyで履歴を確認する
スコアリング
対応ロール: member
projects.scoring_rules に定義したルール群を、候補者の attributes と survey_responses に適用して合計点を算出する。
- ルールは
field(参照先フィールド)、condition、values、scoreの 4 項目 conditionはequals/not_equals/in/not_in/contains/between/greater_than/less_thanfieldはattributes.xxx/survey_responses.xxxのようにプレフィックスで参照先を指定できる。プレフィックスがない場合はattributesを先に探し、なければsurvey_responsesを探すmax_scoreを指定すると合計点の上限になるPOST /projects/{id}/candidates/scoreでプロジェクト内の全候補者を再計算する
候補者ステータス管理
対応ロール: member
project_candidates.status は 10 種類の enum で、遷移可能な組み合わせが VALID_STATUS_TRANSITIONS に定義されている。不正な遷移は InvalidStatusTransitionError(400 / INVALID_STATUS_TRANSITION)になる。
stateDiagram-v2
[*] --> not_contacted
not_contacted --> contacted
not_contacted --> declined
not_contacted --> cancelled
contacted --> waiting_response
contacted --> bounced
contacted --> declined
contacted --> cancelled
waiting_response --> scheduling
waiting_response --> declined
waiting_response --> cancelled
scheduling --> scheduled
scheduling --> declined
scheduling --> cancelled
scheduled --> interviewed
scheduled --> cancelled
interviewed --> completed
interviewed --> cancelled
bounced --> contacted
cancelled --> not_contacted
completed --> [*]
declined --> [*]
| ステータス | 意味 |
|---|---|
not_contacted |
未連絡 |
contacted |
連絡済み(案内メール送信済み) |
waiting_response |
返答待ち |
scheduling |
日程調整中(候補者が希望日程を送信した状態) |
scheduled |
日程確定(予約が作成された状態) |
interviewed |
面接実施済み |
completed |
完了 |
declined |
辞退 |
bounced |
メール不達 |
cancelled |
キャンセル |
遷移のたびに status_histories にレコードが追加され、old_status / new_status / changed_by / note が記録される。
日程調整
対応ロール: 候補者(認証なし)+ member
sequenceDiagram
participant C as 候補者
participant FE as 公開ページ<br/>/scheduling/{token}
participant API as Backend API
participant DB as PostgreSQL
participant GC as Google Calendar
participant M as 管理者(member)
M->>API: 案内メール送信
API->>DB: scheduling_tokens 発行
API-->>C: トークン URL 付きメール
C->>FE: トークン URL を開く
FE->>API: GET /scheduling/{token}
API-->>FE: 案件情報・期限・予約状況
FE->>API: GET /scheduling/{token}/slots
API-->>FE: 予約可能枠一覧
C->>FE: 希望枠を選択(1〜20 件)
FE->>API: POST /scheduling/{token}/submit-availability
API->>DB: candidate_available_dates を置き換え
API->>DB: status を scheduling に更新
API-->>C: 受付完了メール
M->>API: POST /projects/{id}/candidates/{cid}/confirm-schedule
API->>DB: reservations を作成
API->>GC: 仮枠を確定イベントへ変換
API-->>C: 確定通知メール
- 調整トークンの有効期限は
SCHEDULING_TOKEN_TTL_DAYS(既定 14 日) - 希望日程は送信のたびに全置換される(追記ではない)
- 既に確定予約がある場合の送信は
409 SLOT_NOT_AVAILABLE PROTOTYPE_MODE=true(dev の既定)の間はメール送信とカレンダー書き込みがスキップされる。stg はfalseなので実際に反映される
Google カレンダー連携
プロジェクトカレンダー
プロジェクトを作成すると [インタビュー] {プロジェクト名} という専用カレンダーが作られ、projects.google_calendar_id に保存される。所有者はプロジェクト作成者で、サービスアカウントはその人になりすまして読み書きする。作成時に失敗していた場合や、カレンダーがない古いプロジェクトの場合は、仮枠を押さえる時点で作成される。
書き込み先と参加者
仮枠から確定枠まで、すべてこのプロジェクトカレンダーに入る。面接官個人のカレンダーには直接書き込まない。
| 操作 | カレンダー | attendee | Google からの通知 |
|---|---|---|---|
| 空き時間の照会 | 面接官本人(freebusy のみ) | - | - |
| 仮枠の作成・削除 | プロジェクトカレンダー | 担当の面接官 | 送らない |
| 日程確定 | プロジェクトカレンダー | メイン面接官+サブ面接官 | 送る |
| 予約のキャンセル | プロジェクトカレンダー | 同上 | 送る |
- 面接官は仮枠の段階から attendee に入れる。 そうしないと押さえた枠が本人の空き時間照会に反映されず、同じ時間に別の枠を作ってしまう
- 仮枠は一度に数十件作るため、その通知は送らない。確定とキャンセルだけ通知する
- 候補者は attendee に入れない。 候補者への連絡はアプリからのメールで行う
イベント名
| 状態 | 書式 | 例 |
|---|---|---|
| 仮枠 | [仮] {プロジェクト名} インタビュー枠 |
[仮] 新商品調査 インタビュー枠 |
| 確定 | {候補者名}様 インタビュー(担当:{面接官名})/ {プロジェクト名} |
山田太郎様 インタビュー(担当:渡辺)/ 新商品調査 |
仮枠の時点では候補者が決まっていないため、確定時に候補者名と担当者名を入れて書き換える。
メール送信
対応ロール: member
- テンプレートは「システム共通」(
project_idが NULL)と「プロジェクト個別」の 2 種類 - 種別(
type)はinvitation/reminder/confirmation/cancellation - 件名・本文には
{{candidate_name}}{{project_name}}のようなプレースホルダを埋め込める POST /projects/{id}/emails/previewで 1 候補者分の差し込み結果を確認できるPOST /projects/{id}/emails/sendで複数候補者へ一括送信し、結果はsent_count/failed_count/errorsで返る- 送信内容は
email_logsに本文ごと保存される
アンケートデータ連携(Creative Survey / Ask One)
対応ロール: viewer(参照のみ)
回答データは Databricks の Unity Catalog から直接読む。Postgres にキャッシュするのは調査一覧に使うメタデータだけ。
| 参照先 | 用途 |
|---|---|
cs.cs_dm.dm_answers_<survey_id> |
調査ごとの回答。回答とパネルが結合済み |
databricks_surveys(Postgres) |
調査一覧・詳細。日次バッチで更新する |
- メタの集計は約 6,800 万行のスキャンで 15 秒前後かかるため、リクエストごとには実行しない
- 更新は
src/batch/databricks-meta-sync.tsを ECS Fargate タスクとして走らせる - 詳細は Databricks 連携 を参照
以前は S3 の CSV をキャッシュしていた
Creative Survey が S3 に出力する answers.csv.gz / panels.csv.gz を自前で取り込んでいたが、同じファイルを Databricks 側のパイプラインも取り込んでいたため二重管理になっていた。同期機能は廃止した。
個人情報クリーンアップ
対応ロール: バッチ
保持期間(既定 3 ヶ月)を超えた個人情報を pii-cleanup バッチで匿名化する。集計のため関連レコード自体は残す。