LINE × Google Sheets で作る受付順番管理システム作った

next.js

※プロモーションページが含まれる場合があります

LINE × Google Sheets で作る受付順番管理システム作った

この記事でわかること

  • LINE LIFF + Next.js + Google Sheets でサーバーレスな受付システムを構築する方法
  • 段階通知(あと3人→2人→1人)を自動化する仕組み
  • GAS(Google Apps Script)を Webhook トリガーとして活用するパターン
  • 実際に躓いたポイントと解決策

このシステムで何ができるか

病院や店舗で使える受付順番管理システムを作りました。

主な機能

  1. LINE から受付登録
    LIFFアプリで名前を入力 → 自動で番号発行
  2. 「順番確認」で待ち人数を返答
    トークにメッセージ送信 → 即座に返信
  3. 段階的な自動通知
    スタッフがスプレッドシートで「完了」に変更
    → 次の人に「あと3人です」→「あと2人です」→「まもなくです」と自動通知

ポイント: サーバー管理不要、月額ほぼゼロ円で運用可能


なぜこのシステムを作ったのか

きっかけは自分の体験

ある日、病院の待合室で2時間待たされたときのことです。

  • 狭い待合室でずっと座っている苦痛
  • いつ呼ばれるかわからない不安
  • 外に出たいけど、順番を逃すかもしれない…

同じ空間に2時間はキツすぎる。他の人も同じ気持ちなんじゃないか?

そう思ったのが、このシステムを作るきっかけでした。

解決したかった課題

患者側:

  • 待合室で2時間も密になるストレス
  • 順番がわからず、外出もできない
  • 「あと何人?」と何度も聞きづらい

スタッフ側:

  • 患者からの問い合わせ対応に追われる
  • 手動で電話やLINEで呼び出し → 手間がかかる

このシステムの可能性

病院だけじゃない。

飲食店の行列役所の窓口
「順番待ち」がある場所なら、どこでも応用できるはず。

待つ側も、対応する側も、みんなが楽になる。そんなシステムを目指しました。

目指したこと

  • 患者が自分で状況を確認できる
  • 待ち時間を有効活用(カフェ、車内、散歩など)
  • スタッフの手動作業をゼロに
  • 初期コスト・運用コストを最小化

技術選定の理由

システム構成

患者(LINE) ← → Vercel (Next.js) ← → Google Sheets
                      ↕
                  GAS (トリガー)
技術スタック理由
LINE LIFFユーザーは公式LINEから利用。アプリ開発不要
Next.js (Vercel)サーバーレス、デプロイが簡単、無料枠が大きい
Google Sheetsスタッフが使い慣れている。専用管理画面を作る必要なし
GASスプレッドシート編集をトリガーに Webhook 発火
TypeScript型安全性で実装ミスを防ぐ

なぜ RDB を使わなかったか

  • 受付データは日次で完結(履歴管理は不要)
  • スタッフがシートを直接編集したい
  • Supabase や Firestore を使うとスタッフ用の管理画面が必要になる

→ Google Sheets で十分


実装のポイント

1. LIFF アプリで受付登録

app/page.tsx でフォームを実装。

病院で「〇〇さん〜」と受付で名前が呼ばれていると思い。

患者自身が自分の名前をフォームに入力することでLINEIDと名前を紐付けするようにしました。

// LIFF初期化
const liffModule = await import("@line/liff");
const liff = liffModule.default;
await liff.init({ liffId: LIFF_ID });

// ユーザー情報を取得
const profile = await liff.getProfile();
const lineId = profile.userId;
const name = `${lastName}${firstName}`;

// Vercel API Route に POST
await fetch("/api/submit", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ lineId, name }),
});

ポイント:

  • LIFF は LINE トーク内で開くため、ユーザーはログイン不要
  • profile.userId で LINE ユーザーを一意に識別

2. Google Sheets をデータベースとして使う

lib/sheets.ts で Google Sheets API をラップ。

import { google } from 'googleapis';

const auth = new google.auth.GoogleAuth({
  credentials: JSON.parse(process.env.GOOGLE_CREDENTIALS!),
  scopes: ['https://www.googleapis.com/auth/spreadsheets'],
});

const sheets = google.sheets({ version: 'v4', auth });

// 今日のシートにデータを追加
export async function appendRow(
  spreadsheetId: string,
  sheetName: string,
  row: (string | number)[]
) {
  await sheets.spreadsheets.values.append({
    spreadsheetId,
    range: `${sheetName}!A:G`,
    valueInputOption: 'USER_ENTERED',
    requestBody: { values: [row] },
  });
}

工夫した点:

  • 日付ごとにシートを分けて管理(例: 2026-02-06
  • シートが存在しない場合は自動作成

3. 段階通知の自動化

スタッフがステータスを「完了」に変更 → 次の待機者に通知

3-1. GAS でスプレッドシート編集を検知

code.gs

// onEdit トリガー
function onEdit(e) {
  if (!e || !e.range) return;

  const sheet = e.range.getSheet();
  const col = e.range.getColumn();
  const row = e.range.getRow();

  // D列(ステータス)が変更された場合
  if (col === 4 && row > 1) {
    const status = sheet.getRange(row, col).getValue();
    if (status === '完了') {
      // Vercel の Webhook を呼ぶ
      callSheetEditedWebhook(sheet.getName());
    }
  }
}

ポイント:

  • GAS の onEdit トリガーはリアルタイムでスプレッドシート編集を検知
  • Webhook を叩いて Vercel 側の処理に移譲

3-2. Vercel で段階通知ロジックを実行

lib/notify.ts

const NOTIFY_THRESHOLDS = [3, 2, 1, 0];
const THRESHOLD_MESSAGES: Record<number, string> = {
  3: 'あなたの前にはあと3人です。少しお待ちください。',
  2: 'あなたの前にはあと2人です。準備をお願いいたします。',
  1: '次の順番が近づいています。あなたの前にはあと1人です。',
  0: 'まもなくご案内です。受付付近でお待ちください。',
};

export async function notifyNearTurn() {
  const values = await getTodaySheetData(spreadsheetId, today);
  const lastDoneNumber = getLastDoneNumber(values); // 最後に完了した番号

  for (const row of values) {
    const remaining = row.number - lastDoneNumber - 1; // 前に何人いるか

    // 通知段階を判定
    for (let i = 0; i < NOTIFY_THRESHOLDS.length; i++) {
      if (remaining <= NOTIFY_THRESHOLDS[i] && row.notifyStage < i) {
        await pushToUser(row.lineId, THRESHOLD_MESSAGES[i]);
        await updateNotifyStage(row.number, i);
        break;
      }
    }
  }
}

工夫した点:

  • notifyStage でどこまで通知したかを記録
  • 二重通知を防ぐ仕組み

4. 「順番確認」メッセージに返答

app/api/webhook/route.ts

export async function POST(request: NextRequest) {
  const body = await request.json();

  for (const event of body.events) {
    if (event.type === 'message' && event.message.type === 'text') {
      const text = event.message.text;

      if (text.includes('順番') || text.includes('確認')) {
        const waiting = await getWaitingCount(event.source.userId);
        await replyMessage(
          event.replyToken,
          `現在、あなたの前には ${waiting} 人待っています。`
        );
      }
    }
  }

  return NextResponse.json({ status: 'ok' });
}

ポイント:

  • LINE Messaging API の replyMessage を使用
  • キーワードに反応する簡易的な bot

データ構造

Google Sheets の各シート(例: 2026-02-06

番号LINE_ID氏名ステータス通知段階登録日時更新日時
1U123…山田太郎完了32026-02-06T09:00:002026-02-06T09:30:00
2U456…鈴木花子待機22026-02-06T09:05:002026-02-06T09:15:00

通知段階:

  • 0: 未通知
  • 1: あと3人 通知済
  • 2: あと2人 通知済
  • 3: あと1人 通知済
  • 4: まもなく 通知済

セキュリティ対策

1. Webhook 署名検証

GAS と Vercel 間の Webhook に共有シークレットを設定。

const secret = process.env.WEBHOOK_SECRET;
const signature = request.headers.get('x-webhook-signature');

if (signature !== secret) {
  return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}

2. LINE Webhook の署名検証

LINE からの Webhook が本物か検証。

import crypto from 'crypto';

const channelSecret = process.env.LINE_CHANNEL_SECRET!;
const signature = request.headers.get('x-line-signature');
const body = await request.text();

const hash = crypto
  .createHmac('SHA256', channelSecret)
  .update(body)
  .digest('base64');

if (hash !== signature) {
  return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
}

運用コスト

サービス用途月額コスト
VercelNext.js ホスティング無料 (Hobby プラン)
Google CloudSheets API無料 (制限内)
LINE Messaging APIPush 通知無料 (月5000通まで)

合計: ほぼ 0円 で運用可能


まとめ

できるようになったこと

✅ スタッフの手動通知作業がゼロになる
✅ 患者が LINE で受付・順番確認できる
✅ 段階的な自動通知で待合室の混雑を緩和
✅ 初期コスト・運用コストがほぼゼロ

使った技術

  • LINE LIFF でユーザー体験を統一
  • Next.js (Vercel) でサーバーレスな API 構築
  • Google Sheets をシンプルなデータベースとして活用
  • GAS をリアルタイムトリガーとして利用

コメント

タイトルとURLをコピーしました