Guide Operations · Admin Console × LINE LIFF
ガイドの名簿・在籍状態・稼働状態を一元管理し、ツアーの手配と募集、個別連絡と全体アナウンス、 経費の申請から承認・精算までを 1 つの画面で回す——その「管理者ができること」「ガイドができること」を、 画面操作・API・データモデルまで含めてまとめた機能リファレンス。管理は 管理コンソール、ガイド本人は LINE 内の LIFF ページから操作します。
ガイド統合管理は、予約台帳(Booking)と地続きの「ガイド運用」レイヤです。誰が在籍していて、いま稼働できるのは誰か、どのツアーを誰に手配したか、その経費をいくら払うか——を、管理者とガイドの両側から扱います。
ブラウザの管理コンソール(app/templates/admin/index.html のガイド管理タブ)から操作。
認証は既存の管理画面と同じプレーン運用。名簿・状態管理・手配と募集・連絡・経費の承認/却下/精算・実績の確認まで担当します。
LINE 内に開く LIFF ページ(docs/guide.html)から操作。
LINE IDトークンで本人を特定するため、ログイン=認証。稼働状態の変更・担当ツアーの確認・経費申請ができます。
ガイド管理 API はすべて FastAPI ルータ app/api/guide_admin.py(prefix="/api/guides")に集約されます。管理コンソール用は /api/guides/profile/*・/api/guides/expenses*・/api/guides/message(s) のように静的セグメントで構成し、既存の app/api/guides.py(募集系:"" / /recruitments / /recruit/{booking_id})と衝突しないようにしています。ガイド本人向けは別系統の /api/guide-liff/* で、各リクエストの先頭で IDトークンを検証して line_user_id から本人を解決します。
status(在籍/離脱:active / inactive)と availability(稼働状態:available / busy / off)の別々の軸があります。
在籍していても「今日は休止(off)」はあり得るし、離脱したガイドはそもそも募集対象から外れる——役割が違うので両方を独立に持ちます。
| 用語 | 意味 | 取りうる値 |
|---|---|---|
在籍状態 status | 名簿上の在籍/離脱。離脱者は募集・一斉連絡の対象外 | active 在籍 / inactive 離脱 |
稼働状態 availability | いま仕事を受けられるか。ガイド本人が LIFF で切り替え可能 | available 稼働可 / busy 多忙 / off 休止 |
| 手配 | 特定の予約に対してガイドを直接指名して割り当てる(管理者) | — |
| 募集 | 予約に対し全在籍ガイドへ一斉送信し、先着 1 名で確定する(既存 /recruit) | open / filled / cancelled |
経費 GuideExpense | ガイドが立て替えた費用。申請→承認/却下→精算で状態遷移 | submitted / approved / rejected / reimbursed |
連絡 GuideMessage | 管理者からの個別連絡・全体アナウンスの送信ログ | — |
管理コンソールのガイド管理タブから扱える機能。名簿・状態・手配と募集・連絡・経費・実績・給与・CSV移行の領域をカバーします。
登録済みガイドの一覧と詳細を確認。氏名・電話・メール・対応言語・管理メモを編集できます。LINE セルフ登録(「登録 名前」)で入った人を、後から肉付けする運用。
在籍状態(active / inactive)と稼働状態(available / busy / off)を切り替え。離脱処理や、ガイドに代わって稼働状態を直すこともできます。
手配=予約にガイドを直接指名(既存 /api/admin/guides/assign)。募集=全在籍ガイドへ一斉送信して先着確定(既存 /api/guides/recruit/{booking_id})。詳細画面から担当中ツアーも一覧できます。
特定ガイドへの個別連絡、または全在籍ガイドへの一斉アナウンスを LINE push で送信。送信結果(成功/失敗件数)と全文を GuideMessage に記録します。
ガイドが申請した経費を一覧・集計し、承認/却下(理由つき)/精算済みへ遷移。管理者による代理登録も可能。操作は監査ログ(AuditLog)にも残ります。
ガイド詳細の勤務実績タブで、担当した全ツアーの履歴・累計案内人数・ツアー別の担当回数を表示。予約台帳(Booking.guide_name)から自動集計します。
ガイドにランクとツアー単価(pay_rate)を設定し、詳細の給与・支払タブで対象期間を指定すると、期間内の担当ツアー報酬(単価×件数)+承認済み経費の精算+調整額を自動集計。支払通知書を発行し、印刷/PDF 用の整形ページを別タブで開けます。「支払済」にすると紐づく経費も精算済みへ進みます。
GAS 管理のスプレッドシートを CSV / タブ区切りで書き出し、ヘッダごと貼り付けて一括取り込み。氏名・電話・ランク・単価・銀行口座・対応言語などの列を自動認識し、氏名(または LINE ID)で既存ガイドと突合して upsert します。取り込み前に必ずプレビューで確認。
経費は申請(submitted)を起点に、レビュー API(action)で状態が変わります。承認後にのみ精算へ進めるのがルールです。
reject_reason を保存)
submitted → approved。レビュー担当(reviewer)と日時(reviewed_at)を記録。submitted → rejected。reason を reject_reason に保存し、ガイド側にも理由が見えます。approved → reimbursed。支払い済みとして締める。承認前の経費はこの遷移ができません。AuditLog に action="expense_<action>"・対象 booking_id・金額/カテゴリを detail として追記。誰がいつ何をしたかを後から追えます。POST /api/guides/expenses で経費を代理登録できます。guide_id から氏名を解決し、状態は submitted で起票されるので、その後は通常どおり承認フローに乗ります。
ガイド本人は LINE 内の LIFF ページから操作。LINE IDトークンで本人確認するため、別途ログインや ID 入力は不要です。できることは 3 つ。
「稼働可 / 多忙 / 休止」をワンタップで切り替え。ここで off にすると、管理者側の名簿でも休止として見え、稼働可能なガイドの把握に反映されます。
自分が割り当てられている担当中ツアーの一覧を表示(予約台帳 Booking.guide_name が自分の名前のもの)。日付・時刻・人数・ツアー名を確認できます。
立て替えた費用を申請。カテゴリ(交通/食事/チケット/備品/その他)・金額・発生日・メモ・領収書 URL を入力。状態は submitted で起票され、自分の申請履歴も /me で確認できます。
liff.init → liff.getIDToken() で取得した id_token を毎リクエストの body に載せます。サーバは app/api/liff.py の _verify_id_token() を再利用して検証し、sub(= line_user_id)から Guide を引き当てます。経費申請時の guide_id / guide_name はトークンから解決するので、ガイドが他人になりすまして申請することはできません。
ガイドが POST /api/guide-liff/me を最初に叩くと、本人情報(id / 名前 / 稼働状態)・担当中ツアー・自分の経費が 1 回でまとめて返ります。LIFF ページはこの結果で画面を組み立てます。
管理コンソールのガイド管理タブと、ガイド向け LIFF ページ、それぞれの代表的な操作の流れ。
GET /api/guides/profile/{gid} で詳細を取得。プロフィール・担当中ツアー一覧・経費(件数/未精算額)・統計が出ます。PATCH /api/guides/profile/{gid})。/api/admin/guides/assign)、または募集を一斉送信(/api/guides/recruit/{booking_id}、先着確定)。POST /api/guides/message)。送信ログは GET /api/guides/messages で確認。GET /api/guides/expenses、状態/ガイドで絞り込み)から、承認・却下・精算(POST /api/guides/expenses/{eid}/review)。サマリーは /expenses/summary。docs/guide.html)を開く。liff.init 後、未ログインなら liff.login()。liff.getIDToken() で取得した id_token を POST /api/guide-liff/me に送り、本人情報・担当中ツアー・自分の経費を取得して表示。POST /api/guide-liff/availability)。即時に反映。POST /api/guide-liff/expense)。submitted として申請され、履歴に追加されます。{ok:false, error})。ガイドが未登録の line_user_id でアクセスした場合などは、エラー応答で画面側が案内を出せるようになっています。
ガイド管理は app/api/guide_admin.py に新設。管理コンソール用は /api/guides/*(プレーン認証)、ガイド本人用は /api/guide-liff/*(IDトークン検証)。既存の募集系 API も参照のため併記します。web=管理画面 / token=LINE IDトークン必須。
app/api/guide_admin.py — 管理コンソール用| Method | パス | 用途 | 認証 |
|---|---|---|---|
| GET | /api/guides/profile/{gid} | ガイド詳細+サマリー(担当中ツアー一覧/経費の件数・未精算額/統計) | web |
| PATCH | /api/guides/profile/{gid} | プロフィール/状態更新(name? / phone? / email? / languages? / availability? / status? / note?) | web |
| GET | /api/guides/expenses | 経費一覧(query: status? / guide_id?)。新しい順 | web |
| POST | /api/guides/expenses | 経費を新規登録(管理者の代理登録。guide_id / booking_id? / category / amount / incurred_on? / memo? / receipt_url?) | web |
| POST | /api/guides/expenses/{eid}/review | 状態遷移(action: approve / reject / reimburse、reason? / reviewer?)。AuditLog へも記録 | web |
| GET | /api/guides/expenses/summary | 状態別サマリー(submitted / approved / reimbursed の件数・合計、rejected 件数) | web |
| POST | /api/guides/message | 個別/全体連絡(guide_id?(空=全在籍)/ text)。LINE push 送信+GuideMessage 記録。{sent, failed} を返す | web |
| GET | /api/guides/messages | 連絡の送信ログ(最新 50 件) | web |
app/api/guide_admin.py — ガイド本人用(LIFF)| Method | パス | 用途 | 認証 |
|---|---|---|---|
| POST | /api/guide-liff/me | 本人ダッシュボード。body:{id_token} → {ok, guide:{id,name,availability}, assigned:[担当中ツアー], expenses:[自分の経費]} | token |
| POST | /api/guide-liff/expense | 経費申請。body:{id_token, booking_id?, category, amount, incurred_on?, memo?, receipt_url?}(guide_id/name はトークンから解決、status="submitted") | token |
| POST | /api/guide-liff/availability | 稼働状態を更新。body:{id_token, availability}(available / busy / off) | token |
app/api/guides.py / app/api/admin.py(本機能で新設しない)| Method | パス | 用途 | 認証 |
|---|---|---|---|
| GET | /api/guides | ガイド一覧(id / name / line_user_id / status / created_at) | web |
| GET | /api/guides/recruitments | 募集一覧(最新 50 件、確定ガイド名つき) | web |
| POST | /api/guides/recruit/{booking_id} | 募集を出す(全在籍ガイドへ一斉送信、先着 1 名で確定) | web |
| GET | /api/admin/guides/stats | ガイド統計 | web |
| POST | /api/admin/guides/assign | ガイドを予約に手動割り当て(指名手配) | web |
app/api/guides.py の "" / /recruitments / /recruit/{booking_id} と衝突しないよう、動的 /{id} を使わず profile/{gid}・expenses・message(s) のような静的セグメント先頭で定義しています。ガイド本人向けは prefix 自体を /api/guide-liff に分けて完全に分離。
SQLAlchemy 2.0 の宣言的マッピング(Mapped / mapped_column)。app/db/models.py に追記。新規テーブルは起動時 Base.metadata.create_all で自動生成され、Guide の追加列は SQLite のため NULL 許容で足します。
__tablename__ = "guides"既存列(id / name / line_user_id / status / created_at)は維持し、連絡先・稼働状態・実績・メモを追加します。
| カラム | 型 | 説明 |
|---|---|---|
id | int PK | 主キー(既存) |
name | str | 氏名(既存) |
line_user_id | str unique | LINE ユーザー ID。本人特定の鍵(既存) |
status | str = "active" | 在籍状態:active 在籍 / inactive 離脱(既存) |
phone | str | None (40) | 電話番号(追加) |
email | str | None (200) | メールアドレス(追加) |
languages | str | None (200) | 対応言語。カンマ区切り 例 "日本語,英語"(追加) |
availability | str = "available" | 稼働状態:available 稼働可 / busy 多忙 / off 休止(追加) |
rating | float | None | 5 段階の平均評価(追加) |
tours_done | int = 0 | 担当完了数(追加) |
note | Text | None | 管理メモ(追加) |
created_at | datetime | 登録日時(既存) |
updated_at | datetime | 更新日時。default / onupdate = utcnow(追加) |
status は名簿上の在籍/離脱、availability は「いま受けられるか」。離脱者は募集対象から外れ、稼働状態は在籍中の本人が LIFF で随時切り替えます。役割が違うので両方を保持します。__tablename__ = "guide_expenses"ガイドが立て替えた費用。申請(submitted)→ 承認/却下 → 精算(reimbursed)の状態を持つ。
| カラム | 型 | 説明 |
|---|---|---|
id | int PK | 主キー |
guide_id | int (index) | 申請ガイドの ID |
guide_name | str (100) | 申請時のガイド名(スナップショット) |
booking_id | str | None (100) | 関連する予約番号(任意) |
tour_name | str | None (100) | 関連ツアー名(任意) |
category | str (30) | transport / meal / ticket / supply / other(交通/食事/チケット/備品/その他) |
amount | int | 金額(円) |
incurred_on | str | None (20) | 発生日 "M/D" または ISO |
memo | Text | None | メモ |
receipt_url | str | None (500) | 領収書 URL |
status | str = "submitted" | submitted / approved / rejected / reimbursed |
reject_reason | Text | None | 却下理由 |
reviewer | str | None (120) | レビュー担当 |
reviewed_at | datetime | None | レビュー日時 |
created_at | datetime | 申請日時。default = utcnow |
__tablename__ = "guide_messages"管理者からの個別連絡・全体アナウンスの送信ログ。guide_id が None なら全体アナウンス。
| カラム | 型 | 説明 |
|---|---|---|
id | int PK | 主キー |
guide_id | int | None (index) | 宛先ガイド ID。None=全体アナウンス |
guide_name | str | None (100) | 宛先ガイド名(個別時) |
body | Text | 本文 |
channel | str = "line" | 送信チャネル |
sent_by | str = "web" | 送信元(管理画面など) |
ok | bool = True | 送信成否 |
created_at | datetime | 送信日時。default = utcnow |
ガイド管理は以下の既存テーブルと連携します(本機能では変更しません)。
| テーブル | 役割 | 連携ポイント |
|---|---|---|
bookings | 受付台帳 | guide_name が「担当中ツアー」の引き当てキー。手配/募集確定で更新される |
tour_recruitments | ガイド募集 | 予約への一斉募集と先着確定(open → filled)。確定で bookings.guide_name に反映 |
audit_logs | 操作監査ログ | 経費レビューを action="expense_<action>"・booking_id・detail(金額/カテゴリ)で記録 |
guide_expenses / guide_messages)は app/db/database.py の起動時 init_db(Base.metadata.create_all)で作成されます。Guide への追加列は SQLite のため NULL 許容で足し、既存データへの影響なく拡張できます。