Chuyển tới nội dung chính

🌐 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