iT邦幫忙

2025 iThome 鐵人賽

DAY 11
0
Modern Web

用 LINE OA 打造中小企業訂單系統:從零開始的 30 天實作紀錄系列 第 11

讓訂單真正落地:Express API 與 MongoDB 寫入

  • 分享至 

  • xImage
  •  

昨天我們完成了使用者在 LINE 上的完整下單流程,但資料僅存在伺服器記憶體,一旦重啟就會消失。今天,我們要設計後端 API,將訂單資料寫入 MongoDB,讓系統能「記住」每一筆交易。這是從 Prototype 邁向實用系統的重要一步。


為什麼需要 API 層?

  • 分離責任:將 LINE Webhook 事件與資料存取拆開,程式結構更清晰。

  • 擴充性:API 作為前端(LINE Bot、未來 LIFF 表單)與後端(DB)的橋樑。

  • 可維護性:日後要接入後台或其他服務,只要呼叫相同的 API 即可。


API 設計思路

  • 設計一個 POST /orders API。

  • 請求 Payload

    • lineUserId: string(來自 LINE 的使用者 ID)

    • items: Array<{ productName: string; quantity: number; price: number }>

    • status?: 'Pending' | 'In Progress' | 'Completed'(預設 Pending

  • 資料模型對齊:完全符合 Day 8 的 OrderSchemauserId:ObjectIditems: orderItemSchema[]status enum)。


資料流示意

https://ithelp.ithome.com.tw/upload/images/20250925/20178868stzycjLM8o.png

今天完成後的效果如下~:

https://ithelp.ithome.com.tw/upload/images/20250925/20178868BWdlXcbfpM.png


程式碼範例

建立 API 路由

// routes/orders.js
const express = require("express");
const router = express.Router();
const mongoose = require("mongoose");
const Order = require("../models/Order");
const User = require("../models/User");

/**
 * Helper:以 LINE userId (string) 取得/建立 User,回傳其 ObjectId
 */
async function findOrCreateUserByLineId(lineUserId) {
  let user = await User.findOne({ lineUserId });
  if (!user) {
    user = await User.create({ lineUserId });
  }
  return user._id;
}

// 建立新訂單(符合 Day 8 定義的 OrderSchema)
router.post("/", async (req, res) => {
  try {
    const { lineUserId, items, status } = req.body;

    if (!lineUserId || !Array.isArray(items) || items.length === 0) {
      return res.status(400).json({ success: false, message: "lineUserId 與 items 為必填" });
    }

    // 基本驗證與正規化
    const normalizedItems = items
      .map((it) => ({
        productName: String(it.productName),
        quantity: Number(it.quantity),
        price: Number(it.price),
      }))
      .filter(
        (it) =>
          it.productName && Number.isFinite(it.quantity) && it.quantity > 0 &&
          Number.isFinite(it.price) && it.price >= 0
      );

    if (normalizedItems.length !== items.length) {
      return res.status(400).json({ success: false, message: "items 格式不正確" });
    }

    const userId = await findOrCreateUserByLineId(lineUserId);

    const order = await Order.create({
      userId, // ObjectId 參照 User
      items: normalizedItems, // [{ productName, quantity, price }]
      status: status || "Pending", // enum: Pending/In Progress/Completed
    });

    res.json({ success: true, orderId: order._id });
  } catch (err) {
    console.error(err);
    res.status(500).json({ success: false, message: "訂單寫入失敗" });
  }
});

module.exports = router;

在主程式引入 API

// index.js
const express = require("express");
const mongoose = require("mongoose");

const app = express();
app.use(express.json());

// 引入 routes
const orderRoutes = require("./routes/orders");
app.use("/orders", orderRoutes);

// MongoDB 連線
mongoose.connect("mongodb://localhost:27017/line-order-system")
  .then(() => console.log("MongoDB Connected"))
  .catch(err => console.error(err));

app.listen(3000, () => console.log("Server running on port 3000"));

Webhook 呼叫 API

在 Day 10 的 handleEvent 中,使用者輸入「確認」時,除了回覆 LINE 訊息,也要呼叫 POST /orders 把資料寫進 DB(記得在檔案頂部 const axios = require("axios");)。

if (event.message.text === "確認") {
  await axios.post("http://localhost:3000/orders", {
    lineUserId: event.source.userId, // 由 LINE 事件而來
    items: [
      {
        productName: state.item,
        quantity: state.quantity,
        price: state.price, // ⚠️ 實務上請以後端商品表為準,不要信任前端價格
      },
    ],
  });

  delete userState[event.source.userId];
  return client.replyMessage(event.replyToken, {
    type: "text",
    text: "訂單已送出,並成功寫入資料庫!",
  });
}

等正確執行完就可以去 MongoDB Compass 即時查看資料有沒有寫進去啦!

https://ithelp.ithome.com.tw/upload/images/20250925/20178868TRJGpoD3gL.png


錯誤處理與回應設計

  • 成功:回傳 { success: true, orderId }

  • 失敗:回傳 { success: false, message }

  • 加上 try/catch,避免 DB 錯誤導致伺服器崩潰。


重點回顧

  • 學會如何把 LINE Bot 的訂單流程與 MongoDB 串接。

  • API 設計是未來擴充的基礎,任何前端(LINE、LIFF、Web 後台)都能共用同一套介面。

  • Prototype 與實務系統的差異就在於 資料必須持久化,今天完成了最關鍵的一步!

Day 12:將會告訴大家,收到訂單然後呢?該如何通知店家訂單來了!


上一篇
打造完整訂單流程:商品 → 數量 → 確認 → 送出
下一篇
不再漏單!自動推播新訂單到管理者群組
系列文
用 LINE OA 打造中小企業訂單系統:從零開始的 30 天實作紀錄12
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言