diff --git a/chapters/chapter-6-web-api.md b/chapters/chapter-6-web-api.md
new file mode 100644
index 0000000..f5fa374
--- /dev/null
+++ b/chapters/chapter-6-web-api.md
@@ -0,0 +1,738 @@
+# 第六章:实战项目 —— 构建一个简易 Web API 服务(深度图解版)
+
+> **本章目标**:综合运用前五章知识,构建一个**生产级别**的 Go Web API 服务(任务管理系统),涵盖路由、中间件、数据库、并发、配置、错误处理、部署等核心技能。
+> **项目功能**:
+> 1. 创建、读取、更新、删除(CRUD)任务(Task)。
+> 2. 支持任务状态(待办、进行中、已完成)。
+> 3. 支持按状态、优先级筛选。
+> 4. 异步处理任务通知(模拟)。
+> 5. 优雅关闭、健康检查、监控指标。
+
+---
+
+## 6.1 项目架构设计
+
+### 6.1.1 目录结构
+
+```
+task-api/
+├── cmd/
+│ └── server/
+│ └── main.go # 入口文件
+├── internal/
+│ ├── handler/ # HTTP 处理层(Handler)
+│ │ └── task_handler.go
+│ ├── service/ # 业务逻辑层(Service)
+│ │ └── task_service.go
+│ ├── repository/ # 数据访问层(Repository)
+│ │ └── task_repo.go
+│ ├── middleware/ # 中间件
+│ │ ├── logger.go
+│ │ ├── auth.go
+│ │ └── recovery.go
+│ ├── config/ # 配置管理
+│ │ └── config.go
+│ └── model/ # 数据模型
+│ └── task.go
+├── pkg/
+│ └── logger/ # 公共工具
+│ └── logger.go
+├── migrations/ # 数据库迁移
+│ └── 001_init.sql
+├── .env # 环境变量
+├── Dockerfile
+├── docker-compose.yml
+└── go.mod
+```
+
+**📖 深度解析**:
+- **cmd/**:应用入口,负责初始化配置、数据库连接、启动服务器。
+- **internal/**:私有代码,外部无法导入,保证封装性。
+ - **handler**:处理 HTTP 请求/响应,参数校验,调用 Service。
+ - **service**:核心业务逻辑,调用 Repository,处理事务。
+ - **repository**:数据库操作,SQL 查询,映射到 Model。
+ - **middleware**:横切关注点(日志、认证、限流)。
+- **pkg/**:公共工具库,可被外部导入。
+- **migrations/**:数据库结构变更脚本。
+
+---
+
+## 6.2 核心代码实现
+
+### 6.2.1 数据模型 (Model)
+
+```go
+// internal/model/task.go
+package model
+
+import "time"
+
+type Status string
+
+const (
+ StatusTodo Status = "todo"
+ StatusInProgress Status = "in_progress"
+ StatusDone Status = "done"
+)
+
+type Task struct {
+ ID int64 `json:"id"`
+ Title string `json:"title"`
+ Description string `json:"description"`
+ Status Status `json:"status"`
+ Priority int `json:"priority"` // 1-5
+ CreatedAt time.Time `json:"created_at"`
+ UpdatedAt time.Time `json:"updated_at"`
+}
+```
+
+**📖 深度解析**:
+- **Status**:使用自定义类型(基于 string),增强类型安全,避免魔法字符串。
+- **JSON 标签**:明确字段映射,支持 `omitempty` 等选项。
+- **时间字段**:`time.Time` 自动映射为 ISO 8601 格式。
+
+---
+
+### 6.2.2 配置管理 (Config)
+
+```go
+// internal/config/config.go
+package config
+
+import (
+ "os"
+ "strconv"
+)
+
+type Config struct {
+ Port string
+ DBHost string
+ DBPort string
+ DBUser string
+ DBPassword string
+ DBName string
+ LogLevel string
+}
+
+func Load() *Config {
+ return &Config{
+ Port: getEnv("PORT", "8080"),
+ DBHost: getEnv("DB_HOST", "localhost"),
+ DBPort: getEnv("DB_PORT", "5432"),
+ DBUser: getEnv("DB_USER", "postgres"),
+ DBPassword: getEnv("DB_PASSWORD", "password"),
+ DBName: getEnv("DB_NAME", "taskdb"),
+ LogLevel: getEnv("LOG_LEVEL", "info"),
+ }
+}
+
+func getEnv(key, defaultVal string) string {
+ if val := os.Getenv(key); val != "" {
+ return val
+ }
+ return defaultVal
+}
+```
+
+**📖 深度解析**:
+- **环境变量**:优先从环境变量读取,支持容器化部署。
+- **默认值**:提供安全默认值,避免配置缺失导致崩溃。
+- **集中管理**:所有配置集中加载,便于传递和测试。
+
+---
+
+### 6.2.3 数据库连接池 (Repository)
+
+```go
+// internal/repository/task_repo.go
+package repository
+
+import (
+ "context"
+ "database/sql"
+ "fmt"
+ "task-api/internal/model"
+)
+
+type TaskRepo struct {
+ db *sql.DB
+}
+
+func NewTaskRepo(db *sql.DB) *TaskRepo {
+ return &TaskRepo{db: db}
+}
+
+// 创建任务
+func (r *TaskRepo) Create(ctx context.Context, task *model.Task) error {
+ query := `INSERT INTO tasks (title, description, status, priority, created_at, updated_at)
+ VALUES ($1, $2, $3, $4, NOW(), NOW()) RETURNING id`
+
+ err := r.db.QueryRowContext(ctx, query,
+ task.Title, task.Description, task.Status, task.Priority).Scan(&task.ID)
+
+ if err != nil {
+ return fmt.Errorf("create task failed: %w", err)
+ }
+ return nil
+}
+
+// 获取所有任务
+func (r *TaskRepo) List(ctx context.Context, status *model.Status) ([]model.Task, error) {
+ query := `SELECT id, title, description, status, priority, created_at, updated_at FROM tasks`
+ if status != nil {
+ query += " WHERE status = $1"
+ rows, err := r.db.QueryContext(ctx, query, *status)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ // ... 解析 rows
+ } else {
+ rows, err := r.db.QueryContext(ctx, query)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ // ... 解析 rows
+ }
+ return tasks, nil
+}
+
+// 更新任务
+func (r *TaskRepo) Update(ctx context.Context, task *model.Task) error {
+ query := `UPDATE tasks SET title=$1, description=$2, status=$3, priority=$4, updated_at=NOW()
+ WHERE id=$5`
+ res, err := r.db.ExecContext(ctx, query,
+ task.Title, task.Description, task.Status, task.Priority, task.ID)
+ if err != nil {
+ return err
+ }
+ rows, _ := res.RowsAffected()
+ if rows == 0 {
+ return fmt.Errorf("task not found")
+ }
+ return nil
+}
+
+// 删除任务
+func (r *TaskRepo) Delete(ctx context.Context, id int64) error {
+ query := `DELETE FROM tasks WHERE id = $1`
+ res, err := r.db.ExecContext(ctx, query, id)
+ if err != nil {
+ return err
+ }
+ rows, _ := res.RowsAffected()
+ if rows == 0 {
+ return fmt.Errorf("task not found")
+ }
+ return nil
+}
+```
+
+**📖 深度解析**:
+- **Context 传递**:所有 DB 操作都接收 `context.Context`,支持超时控制和取消。
+- **预编译语句**:使用 `$1, $2` 防止 SQL 注入。
+- **连接池**:`sql.DB` 内部维护连接池,自动管理连接生命周期。
+- **错误包装**:使用 `fmt.Errorf("%w")` 包装错误,保留原始错误信息。
+- **资源管理**:`defer rows.Close()` 确保资源释放。
+
+---
+
+### 6.2.4 业务逻辑层 (Service)
+
+```go
+// internal/service/task_service.go
+package service
+
+import (
+ "context"
+ "errors"
+ "task-api/internal/model"
+ "task-api/internal/repository"
+)
+
+var ErrNotFound = errors.New("task not found")
+
+type TaskService struct {
+ repo *repository.TaskRepo
+}
+
+func NewTaskService(repo *repository.TaskRepo) *TaskService {
+ return &TaskService{repo: repo}
+}
+
+func (s *TaskService) CreateTask(ctx context.Context, task *model.Task) error {
+ if task.Title == "" {
+ return errors.New("title is required")
+ }
+ if task.Priority < 1 || task.Priority > 5 {
+ return errors.New("priority must be between 1 and 5")
+ }
+ return s.repo.Create(ctx, task)
+}
+
+func (s *TaskService) GetTask(ctx context.Context, id int64) (*model.Task, error) {
+ // 模拟从 repo 获取
+ task, err := s.repo.GetByID(ctx, id)
+ if err != nil {
+ return nil, ErrNotFound
+ }
+ return task, nil
+}
+
+// 异步通知示例
+func (s *TaskService) NotifyTask(ctx context.Context, taskID int64) {
+ go func() {
+ // 模拟发送通知
+ // 注意:这里没有接收 context,实际项目中应传递 context 并处理超时
+ // 使用 context.WithTimeout 创建子 context
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+
+ // 发送通知逻辑...
+ select {
+ case <-ctx.Done():
+ // 超时
+ default:
+ // 成功
+ }
+ }()
+}
+```
+
+**📖 深度解析**:
+- **参数校验**:在 Service 层进行业务规则校验。
+- **错误定义**:定义自定义错误(`ErrNotFound`),便于 Handler 层统一处理。
+- **异步任务**:使用 `go func()` 启动 Goroutine,注意处理 Context 和超时。
+- **依赖注入**:Service 依赖 Repository,便于测试和替换。
+
+---
+
+### 6.2.5 HTTP 处理层 (Handler)
+
+```go
+// internal/handler/task_handler.go
+package handler
+
+import (
+ "encoding/json"
+ "net/http"
+ "strconv"
+ "task-api/internal/model"
+ "task-api/internal/service"
+)
+
+type TaskHandler struct {
+ service *service.TaskService
+}
+
+func NewTaskHandler(svc *service.TaskService) *TaskHandler {
+ return &TaskHandler{service: svc}
+}
+
+type Response struct {
+ Code int `json:"code"`
+ Message string `json:"message"`
+ Data interface{} `json:"data,omitempty"`
+}
+
+func (h *TaskHandler) CreateTask(w http.ResponseWriter, r *http.Request) {
+ var task model.Task
+ if err := json.NewDecoder(r.Body).Decode(&task); err != nil {
+ h.respondError(w, http.StatusBadRequest, "invalid request body")
+ return
+ }
+
+ if err := h.service.CreateTask(r.Context(), &task); err != nil {
+ h.respondError(w, http.StatusInternalServerError, err.Error())
+ return
+ }
+
+ h.respondJSON(w, http.StatusCreated, "created", task)
+}
+
+func (h *TaskHandler) GetTask(w http.ResponseWriter, r *http.Request) {
+ idStr := r.URL.Query().Get("id")
+ id, err := strconv.ParseInt(idStr, 10, 64)
+ if err != nil {
+ h.respondError(w, http.StatusBadRequest, "invalid id")
+ return
+ }
+
+ task, err := h.service.GetTask(r.Context(), id)
+ if err != nil {
+ h.respondError(w, http.StatusNotFound, "task not found")
+ return
+ }
+
+ h.respondJSON(w, http.StatusOK, "success", task)
+}
+
+func (h *TaskHandler) respondJSON(w http.ResponseWriter, code int, msg string, data interface{}) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(code)
+ json.NewEncoder(w).Encode(Response{Code: code, Message: msg, Data: data})
+}
+
+func (h *TaskHandler) respondError(w http.ResponseWriter, code int, msg string) {
+ h.respondJSON(w, code, msg, nil)
+}
+```
+
+**📖 深度解析**:
+- **统一响应格式**:`Response` 结构体,包含 Code、Message、Data。
+- **错误处理**:统一调用 `respondError`,避免重复代码。
+- **Context 传递**:`r.Context()` 自动包含超时和取消信号。
+- **JSON 编解码**:使用标准库 `encoding/json`。
+
+---
+
+### 6.2.6 中间件 (Middleware)
+
+```go
+// internal/middleware/logger.go
+package middleware
+
+import (
+ "log"
+ "net/http"
+ "time"
+)
+
+func Logger(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ start := time.Now()
+ log.Printf("Started %s %s", r.Method, r.URL.Path)
+
+ next.ServeHTTP(w, r)
+
+ log.Printf("Completed %s %s in %v", r.Method, r.URL.Path, time.Since(start))
+ })
+}
+
+// internal/middleware/recovery.go
+func Recovery(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ defer func() {
+ if err := recover(); err != nil {
+ log.Printf("Panic recovered: %v", err)
+ http.Error(w, "Internal Server Error", http.StatusInternalServerError)
+ }
+ }()
+ next.ServeHTTP(w, r)
+ })
+}
+
+// internal/middleware/auth.go
+func Auth(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ token := r.Header.Get("Authorization")
+ if token != "secret-token" {
+ http.Error(w, "Unauthorized", http.StatusUnauthorized)
+ return
+ }
+ next.ServeHTTP(w, r)
+ })
+}
+```
+
+**📖 深度解析**:
+- **链式调用**:中间件是 `http.Handler` 的装饰器,层层包裹。
+- **Logger**:记录请求开始和结束时间,便于性能分析。
+- **Recovery**:捕获 Panic,防止服务器崩溃,返回 500 错误。
+- **Auth**:简单的 Token 验证,实际项目应使用 JWT。
+
+---
+
+### 6.2.7 主函数与优雅关闭 (Main)
+
+```go
+// cmd/server/main.go
+package main
+
+import (
+ "context"
+ "log"
+ "net/http"
+ "os"
+ "os/signal"
+ "syscall"
+ "time"
+
+ "task-api/internal/config"
+ "task-api/internal/handler"
+ "task-api/internal/middleware"
+ "task-api/internal/repository"
+ "task-api/internal/service"
+
+ "github.com/gorilla/mux"
+ _ "github.com/lib/pq"
+ "database/sql"
+)
+
+func main() {
+ cfg := config.Load()
+
+ // 连接数据库
+ dsn := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable",
+ cfg.DBHost, cfg.DBPort, cfg.DBUser, cfg.DBPassword, cfg.DBName)
+ db, err := sql.Open("postgres", dsn)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer db.Close()
+
+ // 初始化依赖
+ taskRepo := repository.NewTaskRepo(db)
+ taskSvc := service.NewTaskService(taskRepo)
+ taskHandler := handler.NewTaskHandler(taskSvc)
+
+ // 路由
+ r := mux.NewRouter()
+ r.Use(middleware.Logger)
+ r.Use(middleware.Recovery)
+ r.Use(middleware.Auth)
+
+ r.HandleFunc("/tasks", taskHandler.CreateTask).Methods("POST")
+ r.HandleFunc("/tasks", taskHandler.GetTask).Methods("GET")
+
+ srv := &http.Server{
+ Addr: ":" + cfg.Port,
+ Handler: r,
+ }
+
+ // 启动服务器
+ go func() {
+ log.Printf("Server starting on port %s", cfg.Port)
+ if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
+ log.Fatal(err)
+ }
+ }()
+
+ // 优雅关闭
+ quit := make(chan os.Signal, 1)
+ signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
+ <-quit
+ log.Println("Shutting down server...")
+
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+
+ if err := srv.Shutdown(ctx); err != nil {
+ log.Fatal("Server forced to shutdown:", err)
+ }
+
+ log.Println("Server exited")
+}
+```
+
+**📖 深度解析**:
+- **依赖注入**:从 Config -> Repo -> Service -> Handler,层层构建。
+- **中间件链**:`r.Use()` 按顺序添加中间件(Logger -> Recovery -> Auth)。
+- **优雅关闭**:
+ - 监听 `SIGINT` (Ctrl+C) 和 `SIGTERM` (Docker 停止) 信号。
+ - 收到信号后,调用 `srv.Shutdown(ctx)`,等待当前请求完成(最多 5 秒)。
+ - 超时后强制关闭。
+- **并发启动**:`go func()` 启动服务器,主进程等待信号。
+
+---
+
+## 6.3 深度图解:请求处理流程
+
+```mermaid
+sequenceDiagram
+ participant Client
+ participant Router
+ participant Logger
+ participant Auth
+ participant Recovery
+ participant Handler
+ participant Service
+ participant Repo
+ participant DB
+
+ Client->>Router: POST /tasks
+ Router->>Logger: 调用 Logger 中间件
+ Logger->>Auth: 记录开始时间
+ Auth->>Recovery: 验证 Token
+ Recovery->>Handler: 捕获 Panic
+ Handler->>Service: CreateTask
+ Service->>Repo: Create
+ Repo->>DB: INSERT INTO tasks
+ DB-->>Repo: 返回 ID
+ Repo-->>Service: Task with ID
+ Service-->>Handler: nil
+ Handler-->>Client: 201 Created + JSON
+ Logger->>Client: 记录结束时间
+```
+
+**📖 深度解析**:
+1. **Client**:发起 HTTP 请求。
+2. **Router**:匹配路由,调用中间件链。
+3. **Logger**:记录请求开始时间。
+4. **Auth**:验证 Token,失败直接返回 401。
+5. **Recovery**:包裹整个处理链,捕获 Panic。
+6. **Handler**:解析请求,调用 Service。
+7. **Service**:业务逻辑,调用 Repo。
+8. **Repo**:执行 SQL,映射结果。
+9. **DB**:执行数据库操作。
+10. **返回**:沿调用链返回,Logger 记录结束时间。
+
+---
+
+## 6.4 深度图解:数据库连接池
+
+```mermaid
+graph TB
+ subgraph "应用层"
+ App[Go 应用]
+ Pool[sql.DB 连接池]
+ end
+
+ subgraph "数据库层"
+ DB[(PostgreSQL)]
+ Conn1[连接 1]
+ Conn2[连接 2]
+ Conn3[连接 3]
+ end
+
+ App <--> Pool
+ Pool <--> Conn1
+ Pool <--> Conn2
+ Pool <--> Conn3
+
+ Note over Pool: 最大连接数:20
空闲超时:30s
最大生命周期:1h
+
+ style Pool fill:#ffeb3b,stroke:#333
+ style DB fill:#2196f3,stroke:#333,color:#fff
+```
+
+**📖 深度解析**:
+- **sql.DB**:不是单个连接,而是**连接池**。
+- **最大连接数**:通过 `db.SetMaxOpenConns(n)` 设置,避免耗尽数据库资源。
+- **空闲超时**:`db.SetConnMaxLifetime()` 设置连接最大生命周期。
+- **复用**:连接用完归还池,下次请求直接复用,避免频繁创建/销毁。
+
+---
+
+## 6.5 深度图解:优雅关闭流程
+
+```mermaid
+sequenceDiagram
+ participant OS
+ participant Main
+ participant Server
+ participant Req1
+ participant Req2
+ participant DB
+
+ OS->>Main: SIGTERM
+ Main->>Server: Shutdown(ctx)
+ Server->>Req1: 拒绝新请求
+ Server->>Req2: 拒绝新请求
+ Req1-->>Server: 完成处理
+ Req2-->>Server: 完成处理
+ Server->>DB: 关闭连接
+ Server-->>Main: 退出
+ Main->>OS: 退出
+```
+
+**📖 深度解析**:
+1. **收到信号**:OS 发送 `SIGTERM`。
+2. **停止监听**:`srv.Shutdown()` 停止接收新请求。
+3. **等待完成**:等待当前正在处理的请求(Req1, Req2)完成。
+4. **关闭资源**:关闭数据库连接、文件句柄等。
+5. **退出**:主进程退出。
+- **超时控制**:`context.WithTimeout` 防止无限等待。
+
+---
+
+## 6.6 Docker 部署
+
+### 6.6.1 Dockerfile
+
+```dockerfile
+FROM golang:1.21-alpine AS builder
+WORKDIR /app
+COPY go.mod go.sum ./
+RUN go mod download
+COPY . .
+RUN CGO_ENABLED=0 GOOS=linux go build -o main ./cmd/server
+
+FROM alpine:latest
+RUN apk --no-cache add ca-certificates
+WORKDIR /root/
+COPY --from=builder /app/main .
+EXPOSE 8080
+CMD ["./main"]
+```
+
+### 6.6.2 docker-compose.yml
+
+```yaml
+version: '3.8'
+services:
+ app:
+ build: .
+ ports:
+ - "8080:8080"
+ environment:
+ - DB_HOST=db
+ - DB_PORT=5432
+ - DB_USER=postgres
+ - DB_PASSWORD=password
+ - DB_NAME=taskdb
+ depends_on:
+ - db
+ restart: always
+
+ db:
+ image: postgres:15-alpine
+ environment:
+ POSTGRES_USER: postgres
+ POSTGRES_PASSWORD: password
+ POSTGRES_DB: taskdb
+ volumes:
+ - postgres_data:/var/lib/postgresql/data
+ ports:
+ - "5432:5432"
+
+volumes:
+ postgres_data:
+```
+
+**📖 深度解析**:
+- **多阶段构建**:第一阶段编译,第二阶段只复制二进制文件,镜像更小。
+- **环境变量**:通过 `docker-compose` 传递配置。
+- **依赖管理**:`depends_on` 确保数据库先启动。
+- **数据持久化**:Volume 挂载,防止数据丢失。
+
+---
+
+## 6.7 总结与扩展
+
+### 6.7.1 核心知识点回顾
+
+1. **分层架构**:Handler -> Service -> Repository,职责清晰。
+2. **中间件**:日志、认证、恢复,横切关注点。
+3. **Context**:超时控制、取消信号、优雅关闭。
+4. **连接池**:数据库连接复用,性能优化。
+5. **Docker**:容器化部署,环境一致性。
+
+### 6.7.2 扩展方向
+
+1. **ORM**:使用 `gorm` 或 `sqlx` 简化数据库操作。
+2. **缓存**:引入 Redis,缓存热点数据。
+3. **消息队列**:使用 Kafka/RabbitMQ 处理异步任务。
+4. **监控**:集成 Prometheus + Grafana。
+5. **CI/CD**:自动化测试、构建、部署。
+
+---
+
+**代码仓库位置**:https://giter.top/openclaw/test/tree/main/chapters/chapter-6
+
+**完整项目代码**:https://giter.top/openclaw/test/tree/main/chapters/chapter-6
+
+---
+
+*最后更新:2026-03-24 00:05 UTC(实战项目版)*
diff --git a/chapters/chapter-6/go.mod b/chapters/chapter-6/go.mod
new file mode 100644
index 0000000..335e3a8
--- /dev/null
+++ b/chapters/chapter-6/go.mod
@@ -0,0 +1,3 @@
+module task-api
+
+go 1.21
diff --git a/chapters/chapter-6/main.go b/chapters/chapter-6/main.go
new file mode 100644
index 0000000..cf72422
--- /dev/null
+++ b/chapters/chapter-6/main.go
@@ -0,0 +1,132 @@
+package main
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "log"
+ "net/http"
+ "os"
+ "os/signal"
+ "syscall"
+ "time"
+)
+
+// 模型
+type Task struct {
+ ID int64 `json:"id"`
+ Title string `json:"title"`
+ Description string `json:"description"`
+ Status string `json:"status"`
+ Priority int `json:"priority"`
+ CreatedAt time.Time `json:"created_at"`
+ UpdatedAt time.Time `json:"updated_at"`
+}
+
+// 响应
+type Response struct {
+ Code int `json:"code"`
+ Message string `json:"message"`
+ Data interface{} `json:"data,omitempty"`
+}
+
+// 内存存储(模拟数据库)
+var tasks = make(map[int64]*Task)
+var nextID int64 = 1
+
+// Handler
+func createTask(w http.ResponseWriter, r *http.Request) {
+ var task Task
+ if err := json.NewDecoder(r.Body).Decode(&task); err != nil {
+ respondError(w, http.StatusBadRequest, "invalid request body")
+ return
+ }
+
+ if task.Title == "" {
+ respondError(w, http.StatusBadRequest, "title is required")
+ return
+ }
+
+ task.ID = nextID
+ nextID++
+ task.Status = "todo"
+ task.CreatedAt = time.Now()
+ task.UpdatedAt = time.Now()
+ tasks[task.ID] = &task
+
+ respondJSON(w, http.StatusCreated, "created", task)
+}
+
+func getTasks(w http.ResponseWriter, r *http.Request) {
+ taskList := make([]*Task, 0, len(tasks))
+ for _, t := range tasks {
+ taskList = append(taskList, t)
+ }
+ respondJSON(w, http.StatusOK, "success", taskList)
+}
+
+func respondJSON(w http.ResponseWriter, code int, msg string, data interface{}) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(code)
+ json.NewEncoder(w).Encode(Response{Code: code, Message: msg, Data: data})
+}
+
+func respondError(w http.ResponseWriter, code int, msg string) {
+ respondJSON(w, code, msg, nil)
+}
+
+// 中间件
+func loggerMiddleware(next http.HandlerFunc) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ start := time.Now()
+ log.Printf("Started %s %s", r.Method, r.URL.Path)
+ next(w, r)
+ log.Printf("Completed %s %s in %v", r.Method, r.URL.Path, time.Since(start))
+ }
+}
+
+func recoveryMiddleware(next http.HandlerFunc) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ defer func() {
+ if err := recover(); err != nil {
+ log.Printf("Panic recovered: %v", err)
+ http.Error(w, "Internal Server Error", http.StatusInternalServerError)
+ }
+ }()
+ next(w, r)
+ }
+}
+
+func main() {
+ // 路由
+ http.HandleFunc("/tasks", recoveryMiddleware(loggerMiddleware(createTask)))
+ http.HandleFunc("/tasks", recoveryMiddleware(loggerMiddleware(getTasks)))
+
+ srv := &http.Server{
+ Addr: ":8080",
+ Handler: nil, // 使用默认 ServeMux
+ }
+
+ // 启动服务器
+ go func() {
+ log.Println("Server starting on port 8080")
+ if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
+ log.Fatal(err)
+ }
+ }()
+
+ // 优雅关闭
+ quit := make(chan os.Signal, 1)
+ signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
+ <-quit
+ log.Println("Shutting down server...")
+
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+
+ if err := srv.Shutdown(ctx); err != nil {
+ log.Fatal("Server forced to shutdown:", err)
+ }
+
+ log.Println("Server exited")
+}