カレンダーを見て「え、今週もう終わったの?」って思ったことありませんか?…私もです。
私はかつて友人と小さな清掃業をやっていました。初めはホワイトボード、次にスプレッドシート…
でも、手作業では限界があり、ミスやダブルブッキングが多発。そこでソフトウェアに助けを求めました。
目次
- 自動化のメリット
- 基本アーキテクチャ
- バックエンド:REST API
- フロントエンド:React コンポーネント
- データベース設計
- 外部連携:Google Calendar API
- 自動リマインダー:Twilio で SMS
- デプロイと Docker
- セキュリティと ベストプラクティス
- まとめ
1. 自動化のメリット
- ダブルブッキング防止:予約ロジックで時間衝突を検知。
- 効率化:スタッフへの自動リマインダーでミス激減。
- スケーラビリティ:ビジネス拡大時も設定の調整だけで対応可能。
- 顧客満足度向上:ウェブポータルで手軽に再予約やキャンセル操作が可能。
2. 基本アーキテクチャ
[Browser] ←→ [React SPA] ←→ [Node.js/Express API] ←→ [PostgreSQL]
↓
[Twilio API]
↓
[Google Calendar API]
3. バックエンド:REST API
Node.js + Express サンプル:
// server.js
import express from 'express';
import bodyParser from 'body-parser';
import { Pool } from 'pg';
import dotenv from 'dotenv';
dotenv.config();
const app = express();
app.use(bodyParser.json());
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
// 予約作成エンドポイント
app.post('/api/bookings', async (req, res) => {
const { clientName, startTime, duration } = req.body;
const endTime = new Date(new Date(startTime).getTime() + duration * 3600000);
const conflict = await pool.query(
'SELECT * FROM bookings WHERE NOT($1 >= end_time OR $2 <= start_time)',
[endTime.toISOString(), new Date(startTime).toISOString()]
);
if (conflict.rows.length) {
return res.status(409).json({ error: '時間が重複しています。' });
}
const result = await pool.query(
'INSERT INTO bookings(client_name, start_time, end_time) VALUES($1, $2, $3) RETURNING *',
[clientName, startTime, endTime]
);
res.status(201).json(result.rows[0]);
});
// ポート起動
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
4. フロントエンド:React コンポーネント
// BookingForm.jsx
import React, { useState } from 'react';
import axios from 'axios';
export default function BookingForm() {
const [date, setDate] = useState('');
const [duration, setDuration] = useState(2);
const handleSubmit = async (e) => {
e.preventDefault();
try {
const res = await axios.post('/api/bookings', {
clientName: 'User',
startTime: date,
duration
});
alert(`予約完了: ${res.data.start_time} から ${res.data.end_time}`);
} catch (err) {
alert(err.response.data.error);
}
};
return (
<form onSubmit={handleSubmit}>
<label>日付・時刻:</label>
<input type="datetime-local" value={date} onChange={e => setDate(e.target.value)} required />
<label>所要時間 (h):</label>
<input type="number" value={duration} onChange={e => setDuration(+e.target.value)} min="1" />
<button type="submit">予約</button>
</form>
);
}
5. データベース設計 (PostgreSQL)
CREATE TABLE bookings (
id SERIAL PRIMARY KEY,
client_name VARCHAR(100) NOT NULL,
start_time TIMESTAMP NOT NULL,
end_time TIMESTAMP NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
6. 外部連携:Google Calendar API
import { google } from 'googleapis';
const calendar = google.calendar('v3');
async function addEvent(auth, booking) {
const event = {
summary: '清掃サービス',
start: { dateTime: booking.start_time },
end: { dateTime: booking.end_time },
};
await calendar.events.insert({
auth,
calendarId: 'primary',
resource: event,
});
}
7. 自動リマインダー:Twilio で SMS
import Twilio from 'twilio';
const client = new Twilio(process.env.TWILIO_SID, process.env.TWILIO_TOKEN);
function sendReminder(phone, message) {
return client.messages.create({
body: message,
from: process.env.TWILIO_FROM,
to: phone,
});
}
8. デプロイと Docker
# Dockerfile
FROM node:16
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .
CMD ["node", "server.js"]
9. セキュリティと ベストプラクティス
- 環境変数で機密情報を管理(
.env
ファイルは.gitignore
に追加)。 - SQL インジェクション防止のため、パラメータライズドクエリを使用。
- HTTPS 対応(Let’s Encrypt などで SSL 化)。
- ユーザー認証・認可(JWT など)。
10. まとめ
清掃業のスケジューリング自動化は、技術を活用することで
- 作業効率
- 顧客満足度
- ビジネスの拡張性
を飛躍的に向上させます。
ぜひこのテンプレートを参考に、自分だけの最適なスケジューラを構築してください!
エピソードひとつ。聞いてください。
高齢のお客様「Doris」さんがいて、「掃除後にバスルームが変な匂いする」と言われました。
調べたら、スタッフが漂白剤を使っていて、実は彼女アレルギーだったんです…。
でもその情報、どこにも記録してなかったんですよね。今は、顧客ごとの好みや注意点をアプリにタグ付けしています。
Dorisさんの健康のために、これは大事な一歩でした。