🌐 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ầy
- http.ResponseWriter: Công cụ để trả lời khách hàng
- http.Request: Đơn hàng từ khách hàng
- ListenAndServe: 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