第十章:自動化你的工作 - 在 FastAPI 實作 Cron Jobs 定時任務
當你的打卡系統上線運作了一個月,老闆把你叫進辦公室,開心地對你說:「系統很棒!但... 那個... 我每天晚上下班前,都要自己登入後台去點『匯出遲到名單』,有點麻煩。這個系統可以每天晚上 6 點,『自動』把今天遲到的人傳 Line 給我嗎?」
如果你不知道怎麼做「定時任務」,你可能會想到一個非常悲催的畫面:你設定了一個每天 5:55 的手機鬧鐘,時間一到,你就手動去敲擊鍵盤觸發 API。這聽起來一點都不像個工程師。
在現代化程式開發中,這件事有一個專屬的高級名詞:Cron Jobs (排程任務)。 它就像是軟體世界裡的「隱形鬧鐘」,時間一到,它就會在背景自動幫你做苦工。
🎯 本章目標
- 認識 Python 界最強的非同步排程套件
APScheduler。 - 了解 FastAPI 的生命週期 (
lifespan) 與背景任務的整合。 - 寫一個能自動 Query 資料庫,並透過 Line 主動發送通知的定時任務。
⏰ 第一步:安裝排程套件 (APScheduler)
Python 生態系有很多排程套件 (例如簡單的 schedule 或是龐大的 Celery),但在 FastAPI 這種主打「非同步 (Async)」的現代化框架中,業界最推薦使用的是輕量且支援非同步的 APScheduler。
請開啟終端機 (記得啟動你的 venv!),輸入以下指令安裝:
pip install apscheduler
🚀 第二步:請 AI 寫出「每日遲到報表」腳本
在 FastAPI 裡面設定定時任務,最容易踩坑的地方在於「如何讓它跟著 FastAPI 的伺服器一起啟動,而不會互相卡死」。我們可以直接請 AI 用最新的 lifespan 寫法來實作。
🔥【Vibe Prompt 實戰咒語】
我正在開發一個 FastAPI 的 Line Bot。我需要加上一個定時排程任務 (Cron Job)。需求如下:1. 匯入 apscheduler 的 AsyncIOScheduler。2. 寫一個非同步函數 daily_late_report(),設定在每天的傍晚 18:00 自動執行。3. 在這個函數內部,請寫一段假邏輯:去資料庫撈出今天遲到的員工名單陣列,並組合成一段中文的匯報字串。4. 使用 line-bot-sdk v3 的 api.push_message 功能,將這串匯報文字,主動推播給老闆 (請留一個 BOSS_LINE_ID 的變數給我填寫)。5. (關鍵) 請將這個 Scheduler 正確地綁定在 FastAPI 最新版推薦的 lifespan (生命週期) 事件中啟動與關閉。
AI 生成的程式碼會非常優雅地整合在你的主程式中:
import os
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from fastapi import FastAPI
from contextlib import asynccontextmanager
from linebot.v3.messaging import Configuration, ApiClient, MessagingApi, PushMessageRequest, TextMessage
# 設定 Line 的金鑰與老闆的 ID
CHANNEL_ACCESS_TOKEN = os.getenv("LINE_CHANNEL_ACCESS_TOKEN")
BOSS_LINE_ID = os.getenv("BOSS_LINE_ID") # 老闆的 Line ID (通常以 U 開頭)
configuration = Configuration(access_token=CHANNEL_ACCESS_TOKEN)
# 💼 定義要定時執行的核心任務
async def daily_late_report():
print("⏰ [CronJob] 開始產生每日遲到報表...")
# TODO: 這裡未來換成去 Supabase 資料庫撈取真實資料的語法
late_users = ["王小明 (遲到 15 分鐘)", "李小華 (未打卡)"]
if not late_users:
report_text = "老闆您好,今日全體員工皆準時出勤,無人遲到!🎉"
else:
report_text = "老闆您好,今日出勤異常名單如下:\n\n" + "\n".join(late_users)
# 透過 Messaging API 主動推播訊息給老闆
try:
with ApiClient(configuration) as api_client:
line_bot_api = MessagingApi(api_client)
line_bot_api.push_message(
PushMessageRequest(
to=BOSS_LINE_ID,
messages=[TextMessage(text=report_text)]
)
)
print("✅ [CronJob] 報表推播成功!")
except Exception as e:
print(f"❌ [CronJob] 推播失敗:{e}")
# 🔄 設定 FastAPI 的生命週期 (Lifespan)
@asynccontextmanager
async def lifespan(app: FastAPI):
# 伺服器啟動時:初始化並啟動鬧鐘
scheduler = AsyncIOScheduler()
# 每天 18 點 0 分執行 (可改成 "* * * * *" 來測試每分鐘執行)
scheduler.add_job(daily_late_report, 'cron', hour=18, minute=0)
scheduler.start()
print("🕒 Scheduler 已啟動!排程任務待命中。")
yield # 這裡代表 FastAPI 伺服器正在執行中...
# 伺服器關閉時:優雅地關閉鬧鐘,釋放資源
scheduler.shutdown()
print("🛑 Scheduler 已安全關閉。")
# 啟動 FastAPI 應用程式
app = FastAPI(lifespan=lifespan)
有了這段程式,當你在伺服器上執行 uvicorn main:app 時,FastAPI 就會在背景默默地看著手錶倒數計時。
一到晚上六點整,不用任何人去點擊,老闆的手機就會叮咚一聲,收到熱騰騰的遲到名單。老闆會覺得你是一個無比專業、甚至會通靈的軟體供應商。
💼 [商業應用場景] 萬物皆可自動化
有了 Cron Job 這個「時間魔法」,你不只能做打卡系統的報表,你還可以開發出無數種可以收「月租費 (SaaS)」的自動化服務:
- 財務催繳機器人:每個月的 5 號,自動去資料庫把還沒繳月租費的客戶名單撈出來,發送 Line 的圖文卡片提醒他們繳款,甚至附上綠界的專屬繳款連結。
- 電商挽回購物車:如果使用者把商品加入購物車超過 24 小時未結帳,自動發送一組「限時 9 折優惠券」給他,提升轉換率。
- 每日天氣與運勢訂閱服務:每天早上 7:30,自動去中央氣象署抓取天氣 API,發送給所有訂閱的會員。
讓系統在半夜為你工作,這才是軟體工程師最浪漫的事情。下一章,我們將帶你進入伺服器部署的最後一塊拼圖,解決雲端平台對於 Cronjob 支援度不一的問題!