2025-07-11 Line 訂單系統-C 階段 LINE 訂單系統的三大核心路由

2025-07-11 Line 訂單系統-C 階段 LINE 訂單系統的三大核心路由

現在我們要補齊整個 LINE 訂單系統的三大核心路由:

📘 /routes/productRoutes.js
📘 /routes/userRoutes.js
📘 /routes/lineWebhook.js

這三個檔案完成後,你的 Node.js 專案就能:

  • 管理商品與使用者
  • 接收 LINE webhook 事件
  • 與訂單 API 串接完成「可互動的 LINE 訂單系統」🎯

🛍️ 一、productRoutes.js

📁 路徑:/backend/routes/productRoutes.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// ===============================
// 🛍️ productRoutes.js
// 商品管理 API
// ===============================
import express from "express";
import { query } from "../db.js";

const router = express.Router();

// ✅ 取得所有商品
router.get("/", async (req, res) => {
try {
const products = await query("SELECT * FROM products ORDER BY id DESC");
res.json(products);
} catch (err) {
console.error("Error fetching products:", err);
res.status(500).json({ error: "Server error while fetching products" });
}
});

// ✅ 新增商品
router.post("/", async (req, res) => {
try {
const { name, description, price, stock, category } = req.body;
await query(
"INSERT INTO products (name, description, price, stock, category) VALUES (?, ?, ?, ?, ?)",
[name, description, price, stock, category]
);
res.status(201).json({ message: "Product created successfully" });
} catch (err) {
console.error("Error adding product:", err);
res.status(500).json({ error: "Server error while adding product" });
}
});

// ✅ 更新商品
router.patch("/:id", async (req, res) => {
try {
const id = req.params.id;
const { name, description, price, stock, category } = req.body;
await query(
"UPDATE products SET name=?, description=?, price=?, stock=?, category=? WHERE id=?",
[name, description, price, stock, category, id]
);
res.json({ message: "Product updated successfully" });
} catch (err) {
console.error("Error updating product:", err);
res.status(500).json({ error: "Failed to update product" });
}
});

// ✅ 刪除商品
router.delete("/:id", async (req, res) => {
try {
const id = req.params.id;
await query("DELETE FROM products WHERE id=?", [id]);
res.json({ message: "Product deleted successfully" });
} catch (err) {
console.error("Error deleting product:", err);
res.status(500).json({ error: "Failed to delete product" });
}
});

export default router;

👤 二、userRoutes.js

📁 路徑:/backend/routes/userRoutes.js

這裡的使用者主要是 LINE 使用者
我們讓他能夠查詢、註冊與更新個人資料。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// ===============================
// 👤 userRoutes.js
// 使用者管理 API
// ===============================
import express from "express";
import { query } from "../db.js";

const router = express.Router();

// ✅ 查詢所有使用者
router.get("/", async (req, res) => {
try {
const users = await query("SELECT * FROM users ORDER BY id DESC");
res.json(users);
} catch (err) {
console.error("Error fetching users:", err);
res.status(500).json({ error: "Server error while fetching users" });
}
});

// ✅ 新增或更新 LINE 使用者
router.post("/register", async (req, res) => {
try {
const { line_user_id, display_name, phone, email } = req.body;

const existing = await query(
"SELECT id FROM users WHERE line_user_id = ?",
[line_user_id]
);

if (existing.length > 0) {
// 更新資料
await query(
"UPDATE users SET display_name=?, phone=?, email=? WHERE line_user_id=?",
[display_name, phone, email, line_user_id]
);
res.json({ message: "User updated" });
} else {
// 新增使用者
await query(
"INSERT INTO users (line_user_id, display_name, phone, email) VALUES (?, ?, ?, ?)",
[line_user_id, display_name, phone, email]
);
res.status(201).json({ message: "User registered" });
}
} catch (err) {
console.error("Error registering user:", err);
res.status(500).json({ error: "Failed to register user" });
}
});

export default router;

💬 三、lineWebhook.js

📁 路徑:/backend/routes/lineWebhook.js

這是整個 LINE Bot 的核心入口:

  • 接收使用者訊息事件
  • 儲存訊息到 DB
  • 可回覆訊息或觸發訂單流程

請先安裝 LINE SDK:

1
npm install @line/bot-sdk

然後建立以下內容👇

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// ===============================
// 💬 lineWebhook.js
// LINE Bot Webhook API
// ===============================
import express from "express";
import line from "@line/bot-sdk";
import { query } from "../db.js";
import config from "../config.js";

const router = express.Router();

// LINE SDK 設定
const lineConfig = {
channelSecret: config.line.channelSecret,
channelAccessToken: config.line.channelAccessToken,
};

const client = new line.Client(lineConfig);

// ✅ 接收 LINE webhook
router.post(config.line.webhookPath, line.middleware(lineConfig), async (req, res) => {
Promise.all(req.body.events.map(handleEvent))
.then((result) => res.json(result))
.catch((err) => {
console.error("❌ Webhook Error:", err);
res.status(500).end();
});
});

// 處理各類事件
async function handleEvent(event) {
console.log("🔹 Received Event:", event);

// 只處理 message 事件
if (event.type !== "message" || event.message.type !== "text") {
return Promise.resolve(null);
}

const userId = event.source.userId;
const messageText = event.message.text;

// 儲存訊息到 DB
await saveMessage(userId, messageText);

// 範例:當使用者輸入「菜單」時,列出商品
if (messageText === "菜單" || messageText === "menu") {
const products = await query("SELECT name, price FROM products");
const replyText = products
.map((p) => `${p.name} - $${p.price}`)
.join("\n");

return client.replyMessage(event.replyToken, {
type: "text",
text: `📋 今日菜單:\n${replyText}`,
});
}

// 範例:回覆收到訊息
return client.replyMessage(event.replyToken, {
type: "text",
text: `你說了:「${messageText}」`,
});
}

// 儲存訊息到資料庫
async function saveMessage(line_user_id, text) {
try {
// 確保使用者存在
const user = await query("SELECT id FROM users WHERE line_user_id=?", [line_user_id]);
let userId;
if (user.length === 0) {
const res = await query(
"INSERT INTO users (line_user_id, display_name) VALUES (?, ?)",
[line_user_id, "LINE_USER"]
);
userId = res.insertId;
} else {
userId = user[0].id;
}

// 儲存訊息
await query("INSERT INTO message_log (user_id, message_text, message_type) VALUES (?, ?, 'text')", [
userId,
text,
]);
} catch (err) {
console.error("Error saving message:", err);
}
}

export default router;

⚙️ 四、整合到主伺服器 server.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import express from "express";
import cors from "cors";
import config from "./config.js";
import { initDB } from "./db.js";

import productRoutes from "./routes/productRoutes.js";
import orderRoutes from "./routes/orderRoutes.js";
import userRoutes from "./routes/userRoutes.js";
import lineWebhook from "./routes/lineWebhook.js";

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

// 註冊路由
app.use("/api/products", productRoutes);
app.use("/api/orders", orderRoutes);
app.use("/api/users", userRoutes);
app.use("/", lineWebhook); // webhook 路由必須放在根目錄

// 啟動伺服器
initDB().then(() => {
app.listen(config.port, () => {
console.log(`🚀 Server running on http://localhost:${config.port}`);
});
});

✅ 測試重點整理

測試項目 方法 說明
測試 LINE webhook LINE Developer → Webhook Test 檢查伺服器能收到事件
查詢商品 GET /api/products 應回傳商品列表
新增商品 POST /api/products 應建立新商品
查詢訂單 GET /api/orders 應回傳訂單列表
建立訂單 POST /api/orders 建立新訂單與明細
註冊使用者 POST /api/users/register 新增或更新 LINE user

如果你願意,我可以幫你接著建立:

📗 frontend (Vue3) 對應的管理介面模組
包含:商品管理頁、訂單列表、LINE Bot 訊息模擬器(方便開發時測試)

是否要我接著幫你生成 Vue3 前端部分?