🌐 Hệ Thống Đặt Món Online - Web Development
1. Giới Thiệu Về Web Development
Trong thời đại số, nhà hàng cần có hệ thống đặt món online. Go cung cấp package net/http mạnh mẽ để xây dựng web server - giống như thiết lập cửa sổ nhận đơn online cho nhà hàng.
Khái niệm chính:
- HTTP Server: Cửa sổ nhận đơn online
- Routes: Đường dẫn đến từng quầy dịch vụ
- Handlers: Nhân viên tiếp nhận yêu cầu
- Middleware: Bộ phận kiểm tra đơn hàng
- JSON: Phiếu đặt món điện tử
2. HTTP Server Cơ Bản
Tạo cửa sổ nhận đơn online đơn giản:
package main
import (
"fmt"
"net/http"
)
// Nhân viên chào đón khách hàng online
func welcomeHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "🍽️ Chào mừng đến Nhà Hàng Code!\n")
fmt.Fprintf(w, "Phương thức: %s\n", r.Method)
fmt.Fprintf(w, "Đường dẫn: %s\n", r.URL.Path)
}
func main() {
// Thiết lập cửa sổ nhận đơn
http.HandleFunc("/", welcomeHandler)
fmt.Println("🌐 Cửa sổ nhận đơn online đang mở tại http://localhost:8080")
// Mở cửa nhận đơn
http.ListenAndServe(":8080", nil)
}
Giải thích:
http.HandleFunc: Gán nhân viên cho từng quầyhttp.ResponseWriter: Công cụ để trả lời khách hànghttp.Request: Đơn hàng từ khách hàngListenAndServe: Mở cửa và bắt đầu nhận đơn
3. Routes - Đường Dẫn Đến Các Quầy
Thiết lập nhiều quầy phục vụ khác nhau:
package main
import (
"fmt"
"net/http"
)
// Quầy xem thực đơn
func menuHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "📋 THỰC ĐƠN\n")
fmt.Fprintf(w, "1. Phở - 50k\n")
fmt.Fprintf(w, "2. Bún chả - 45k\n")
fmt.Fprintf(w, "3. Cơm tấm - 40k\n")
}
// Quầy nhận đơn hàng
func orderHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
fmt.Fprintf(w, "✅ Đã nhận đơn hàng của quý khách!\n")
} else {
fmt.Fprintf(w, "📝 Vui lòng gửi đơn hàng qua phương thức POST\n")
}
}
// Quầy kiểm tra trạng thái
func statusHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "🟢 Nhà hàng đang hoạt động bình thường\n")
}
func main() {
// Thiết lập các quầy phục vụ
http.HandleFunc("/menu", menuHandler)
http.HandleFunc("/order", orderHandler)
http.HandleFunc("/status", statusHandler)
fmt.Println("🌐 Hệ thống đặt món online đang hoạt động...")
fmt.Println("📋 Menu: http://localhost:8080/menu")
fmt.Println("📝 Order: http://localhost:8080/order")
fmt.Println("🟢 Status: http://localhost:8080/status")
http.ListenAndServe(":8080", nil)
}
4. Handlers - Nhân Viên Tiếp Nhận
Tạo handlers chuyên nghiệp hơn:
package main
import (
"fmt"
"net/http"
"strings"
)
// Cấu trúc nhân viên quầy menu
type MenuHandler struct {
dishes map[string]int
}
// Phương thức phục vụ của nhân viên
func (h *MenuHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "🍽️ THỰC ĐƠN NHÀ HÀNG CODE\n\n")
for dish, price := range h.dishes {
fmt.Fprintf(w, "- %s: %dk\n", dish, price)
}
}
// Nhân viên xử lý đơn hàng
type OrderHandler struct {
orders []string
}
func (h *OrderHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
w.WriteHeader(http.StatusMethodNotAllowed)
fmt.Fprintf(w, "❌ Chỉ chấp nhận phương thức POST\n")
return
}
// Đọc món từ URL query
dish := r.URL.Query().Get("dish")
if dish == "" {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "❌ Vui lòng chọn món\n")
return
}
h.orders = append(h.orders, dish)
fmt.Fprintf(w, "✅ Đã nhận đơn: %s\n", dish)
fmt.Fprintf(w, "📊 Tổng đơn hôm nay: %d\n", len(h.orders))
}
func main() {
// Khởi tạo nhân viên
menuHandler := &MenuHandler{
dishes: map[string]int{
"Phở": 50,
"Bún chả": 45,
"Cơm tấm": 40,
"Bánh mì": 25,
},
}
orderHandler := &OrderHandler{
orders: make([]string, 0),
}
// Gán nhân viên vào từng quầy
http.Handle("/menu", menuHandler)
http.Handle("/order", orderHandler)
fmt.Println("🌐 Server đang chạy tại http://localhost:8080")
http.ListenAndServe(":8080", nil)
}
5. JSON - Phiếu Đặt Món Điện Tử
Làm việc với JSON để trao đổi dữ liệu:
package main
import (
"encoding/json"
"fmt"
"net/http"
)
// Cấu trúc món ăn
type Dish struct {
ID int `json:"id"`
Name string `json:"name"`
Price int `json:"price"`
Description string `json:"description"`
}
// Cấu trúc đơn hàng
type Order struct {
OrderID string `json:"order_id"`
Customer string `json:"customer"`
Dishes []string `json:"dishes"`
Total int `json:"total"`
}
// Database giả lập
var menu = []Dish{
{1, "Phở Bò", 50, "Phở bò truyền thống Hà Nội"},
{2, "Bún Chả", 45, "Bún chả nướng thơm ngon"},
{3, "Cơm Tấm", 40, "Cơm tấm sườn bì chả"},
}
// API trả về menu dạng JSON
func getMenuJSON(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(menu)
}
// API nhận đơn hàng JSON
func createOrderJSON(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
var order Order
// Đọc phiếu đặt món JSON
err := json.NewDecoder(r.Body).Decode(&order)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]string{
"error": "Phiếu đặt món không hợp lệ",
})
return
}
// Tạo mã đơn hàng
order.OrderID = fmt.Sprintf("ORD-%d", len(order.Dishes)*1000)
// Trả về xác nhận
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(map[string]interface{}{
"message": "✅ Đơn hàng đã được tiếp nhận",
"order": order,
})
}
func main() {
http.HandleFunc("/api/menu", getMenuJSON)
http.HandleFunc("/api/order", createOrderJSON)
fmt.Println("🌐 API Server đang chạy tại http://localhost:8080")
fmt.Println("📋 GET /api/menu - Xem thực đơn")
fmt.Println("📝 POST /api/order - Đặt món")
http.ListenAndServe(":8080", nil)
}
Test API với curl:
# Xem menu
curl http://localhost:8080/api/menu
# Đặt món
curl -X POST http://localhost:8080/api/order \
-H "Content-Type: application/json" \
-d '{
"customer": "Nguyễn Văn A",
"dishes": ["Phở Bò", "Bún Chả"],
"total": 95
}'
6. Middleware - Kiểm Tra Đơn Hàng
Middleware giống như bộ phận kiểm tra chất lượng đơn hàng:
package main
import (
"fmt"
"log"
"net/http"
"time"
)
// Middleware ghi log thời gian xử lý
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
fmt.Printf("⏰ [%s] %s %s - Bắt đầu xử lý\n",
start.Format("15:04:05"), r.Method, r.URL.Path)
next.ServeHTTP(w, r)
duration := time.Since(start)
fmt.Printf("✅ [%s] Hoàn thành trong %v\n",
r.URL.Path, duration)
})
}
// Middleware kiểm tra xác thực
func authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token == "" {
w.WriteHeader(http.StatusUnauthorized)
fmt.Fprintf(w, "❌ Vui lòng đăng nhập\n")
return
}
if token != "Bearer secret-token" {
w.WriteHeader(http.StatusForbidden)
fmt.Fprintf(w, "❌ Token không hợp lệ\n")
return
}
fmt.Println("✅ Xác thực thành công")
next.ServeHTTP(w, r)
})
}
// Handler đặt món (cần xác thực)
func orderHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "✅ Đơn hàng đã được tiếp nhận!\n")
}
// Handler xem menu (không cần xác thực)
func menuHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "📋 Thực đơn: Phở, Bún chả, Cơm tấm\n")
}
func main() {
// Menu không cần xác thực, chỉ có logging
http.Handle("/menu", loggingMiddleware(http.HandlerFunc(menuHandler)))
// Order cần xác thực và logging
http.Handle("/order", loggingMiddleware(authMiddleware(http.HandlerFunc(orderHandler))))
fmt.Println("🌐 Server với middleware đang chạy...")
log.Fatal(http.ListenAndServe(":8080", nil))
}
7. Router Nâng Cao với Gorilla Mux
Sử dụng router chuyên nghiệp hơn:
package main
import (
"encoding/json"
"fmt"
"net/http"
"github.com/gorilla/mux"
)
type Dish struct {
ID string `json:"id"`
Name string `json:"name"`
Price int `json:"price"`
}
var dishes = map[string]Dish{
"1": {ID: "1", Name: "Phở Bò", Price: 50},
"2": {ID: "2", Name: "Bún Chả", Price: 45},
"3": {ID: "3", Name: "Cơm Tấm", Price: 40},
}
// Lấy tất cả món
func getDishes(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(dishes)
}
// Lấy món theo ID
func getDish(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
dish, exists := dishes[id]
if !exists {
w.WriteHeader(http.StatusNotFound)
json.NewEncoder(w).Encode(map[string]string{
"error": "Không tìm thấy món này",
})
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(dish)
}
// Tạo món mới
func createDish(w http.ResponseWriter, r *http.Request) {
var dish Dish
json.NewDecoder(r.Body).Decode(&dish)
dishes[dish.ID] = dish
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(dish)
}
// Xóa món
func deleteDish(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
delete(dishes, id)
w.WriteHeader(http.StatusNoContent)
}
func main() {
r := mux.NewRouter()
// Định nghĩa các routes
r.HandleFunc("/api/dishes", getDishes).Methods("GET")
r.HandleFunc("/api/dishes/{id}", getDish).Methods("GET")
r.HandleFunc("/api/dishes", createDish).Methods("POST")
r.HandleFunc("/api/dishes/{id}", deleteDish).Methods("DELETE")
fmt.Println("🌐 RESTful API đang chạy tại http://localhost:8080")
http.ListenAndServe(":8080", r)
}
Cài đặt Gorilla Mux:
go get -u github.com/gorilla/mux
8. Xử Lý Form và File Upload
Nhận đơn hàng qua form và upload hình ảnh:
package main
import (
"fmt"
"io"
"net/http"
"os"
"path/filepath"
)
// Form đặt món
func orderFormHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
// Hiển thị form
html := `
<!DOCTYPE html>
<html>
<head>
<title>Đặt Món Online</title>
<meta charset="UTF-8">
</head>
<body>
<h1>🍽️ Đặt Món Online</h1>
<form method="POST" enctype="multipart/form-data">
<label>Tên khách hàng:</label><br>
<input type="text" name="customer" required><br><br>
<label>Món ăn:</label><br>
<select name="dish">
<option>Phở Bò</option>
<option>Bún Chả</option>
<option>Cơm Tấm</option>
</select><br><br>
<label>Số lượng:</label><br>
<input type="number" name="quantity" value="1"><br><br>
<label>Ghi chú (upload ảnh nếu cần):</label><br>
<input type="file" name="image"><br><br>
<button type="submit">Đặt Món</button>
</form>
</body>
</html>
`
w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprint(w, html)
return
}
// Xử lý POST
r.ParseMultipartForm(10 << 20) // 10 MB max
customer := r.FormValue("customer")
dish := r.FormValue("dish")
quantity := r.FormValue("quantity")
// Xử lý file upload
file, handler, err := r.FormFile("image")
if err == nil {
defer file.Close()
// Lưu file
dst, _ := os.Create(filepath.Join("uploads", handler.Filename))
defer dst.Close()
io.Copy(dst, file)
fmt.Fprintf(w, "✅ Đơn hàng từ %s\n", customer)
fmt.Fprintf(w, "📝 Món: %s x %s\n", dish, quantity)
fmt.Fprintf(w, "🖼️ Đã upload: %s\n", handler.Filename)
} else {
fmt.Fprintf(w, "✅ Đơn hàng từ %s\n", customer)
fmt.Fprintf(w, "📝 Món: %s x %s\n", dish, quantity)
}
}
func main() {
// Tạo thư mục uploads
os.Mkdir("uploads", 0755)
http.HandleFunc("/order", orderFormHandler)
fmt.Println("🌐 Server đang chạy tại http://localhost:8080/order")
http.ListenAndServe(":8080", nil)
}
9. Hệ Thống Đặt Món Hoàn Chỉnh
Kết hợp tất cả kiến thức:
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"sync"
"time"
)
// Models
type MenuItem struct {
ID int `json:"id"`
Name string `json:"name"`
Price int `json:"price"`
Category string `json:"category"`
Available bool `json:"available"`
}
type Order struct {
ID string `json:"id"`
Customer string `json:"customer"`
Items []int `json:"items"`
Total int `json:"total"`
Status string `json:"status"`
CreatedAt time.Time `json:"created_at"`
}
// Restaurant System
type Restaurant struct {
menu map[int]MenuItem
orders map[string]Order
mu sync.RWMutex
}
func NewRestaurant() *Restaurant {
return &Restaurant{
menu: map[int]MenuItem{
1: {1, "Phở Bò", 50, "Món chính", true},
2: {2, "Bún Chả", 45, "Món chính", true},
3: {3, "Cơm Tấm", 40, "Món chính", true},
4: {4, "Trà Đá", 5, "Đồ uống", true},
},
orders: make(map[string]Order),
}
}
// Handlers
func (r *Restaurant) GetMenu(w http.ResponseWriter, req *http.Request) {
r.mu.RLock()
defer r.mu.RUnlock()
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(r.menu)
}
func (r *Restaurant) CreateOrder(w http.ResponseWriter, req *http.Request) {
if req.Method != "POST" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var order Order
if err := json.NewDecoder(req.Body).Decode(&order); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Tính tổng tiền
r.mu.RLock()
total := 0
for _, itemID := range order.Items {
if item, exists := r.menu[itemID]; exists && item.Available {
total += item.Price
}
}
r.mu.RUnlock()
// Tạo đơn hàng
order.ID = fmt.Sprintf("ORD-%d", time.Now().Unix())
order.Total = total
order.Status = "Đang xử lý"
order.CreatedAt = time.Now()
r.mu.Lock()
r.orders[order.ID] = order
r.mu.Unlock()
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(order)
}
func (r *Restaurant) GetOrders(w http.ResponseWriter, req *http.Request) {
r.mu.RLock()
defer r.mu.RUnlock()
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(r.orders)
}
// Middleware
func loggingMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
log.Printf("⏰ %s %s - Bắt đầu", r.Method, r.URL.Path)
next(w, r)
log.Printf("✅ %s - Hoàn thành trong %v", r.URL.Path, time.Since(start))
}
}
func main() {
restaurant := NewRestaurant()
// Routes
http.HandleFunc("/api/menu", loggingMiddleware(restaurant.GetMenu))
http.HandleFunc("/api/orders", loggingMiddleware(restaurant.CreateOrder))
http.HandleFunc("/api/orders/list", loggingMiddleware(restaurant.GetOrders))
// Static homepage
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, `
🍽️ HỆ THỐNG ĐẶT MÓN ONLINE - NHÀ HÀNG CODE
API Endpoints:
- GET /api/menu - Xem thực đơn
- POST /api/orders - Đặt món mới
- GET /api/orders/list - Xem danh sách đơn hàng
Example Order:
POST /api/orders
{
"customer": "Nguyễn Văn A",
"items": [1, 2, 4]
}
`)
})
fmt.Println("🌐 Hệ thống đặt món đang hoạt động tại http://localhost:8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
Tổng Kết:
- HTTP Server = Cửa sổ nhận đơn online
- Routes = Đường dẫn đến các quầy phục vụ
- Handlers = Nhân viên tiếp nhận và xử lý
- Middleware = Bộ phận kiểm tra chất lượng
- JSON = Phiếu đặt món điện tử chuẩn hóa
Bài tiếp theo: Database - Kho Dữ Liệu