作品集 ·
電子報發佈系統(epaper)
電子報發佈的管理後台:建立內容、維護訂閱名單,一鍵產生既有發報系統吃的批次 JSON,再由排程分時推送到 RabbitMQ;另附開信/點擊追蹤與給 AI 用的 MCP 端點。

背景與動機
公司原本已經有一套跑很久的發報程式,缺的是前面那一段:內容怎麼進來、名單誰在維護、這次到底發給了誰。這個專案只做「發報前的管理與處理中心」,不負責實際巨量發送——它產出既有系統認得的批次 JSON,發信仍由那套系統接手,兩邊的契約就只有兩種檔案。
做了什麼
- 電子報與名單管理:上傳 HTML 即時預覽,名單支援 CSV/TXT 匯入匯出,產生發報檔時跨名單以 email 去重後切批
- 公開訂閱頁:外界從
/subscribe自行訂閱/退訂,走兩段式 email 確認——點信裡的連結只顯示確認頁、按下按鈕才生效,避開郵件安全掃描預抓連結造成的誤退訂;退訂紀錄保留不刪 - RabbitMQ 分時推送:排程每隔 N 分鐘推一個批次檔,數萬人的名單不會一次灌爆寄送端,推送結果、則數、耗時與失敗原因全數留存
- 開信/點擊追蹤:每位收件人一組唯一 token,推送當下才注入追蹤像素與轉址連結,統計掛在發報紀錄上,點擊追蹤可逐份電子報關閉
- MCP 端點:讓 Claude 這類 AI 工具直接查電子報、改名單、產生發報檔,支援 OAuth 2.1 一鍵授權或 API Token
- 操作紀錄:後台與 AI 的每一次寫入、登入登出與授權都有稽核紀錄,可篩選、下載與設定保留天數
技術組成
前端是 Vue 3 + Vite + Element Plus,後端 Express 搭 Node 22 內建的 node:sqlite(刻意不用 better-sqlite3,省掉原生編譯這件麻煩事)。MCP 走 @modelcontextprotocol/sdk 的 Streamable HTTP,並自己實作了一套 OAuth 2.1 授權伺服器(動態註冊 + 強制 PKCE),因為 Claude 的連接器 UI 只讓使用者填一個網址、沒有地方帶自訂 header。推送不走 AMQP,而是打 RabbitMQ 的 Management HTTP API,零額外依賴。部署為 Docker 多階段建置。
亮點與難點
- 業務邏輯集中在 service 層,REST 路由與 MCP tool 都只是薄薄一層。兩邊各寫一份 SQL 的那段時期,欄位與驗證很快就分岔了
- 失敗處理的分界線是「收件人會不會收到重複信」:一則都沒推出去就原地重試,推到一半失敗則搬進
failed/停下來等人工,絕不自動重推;單則重試也只針對確定沒送達 broker 的錯誤 - 時間一律存 UTC、顯示才轉時區:舊版寫進資料庫的是不帶時區的裸字串,容器跑在 UTC 時畫面就少 8 小時,啟動時會自動推斷偏移並轉換一次
- 以 5 萬人 × 5 次發報的假資料實測過:資料庫與統計側在這個量級沒有瓶頸,真正被追蹤放大的是推送——一人一則訊息,所以排程節流一定要慢於寄信端的速度
