# go-tutorial **Repository Path**: sh_wangwanbao/go-tutorial ## Basic Information - **Project Name**: go-tutorial - **Description**: java程序员一天入门go语言web开发 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2025-12-18 - **Last Updated**: 2026-01-02 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Go 用户管理系统 一个基于 Go 语言开发的 RESTful API 用户管理系统,采用主流的企业级架构和最佳实践。 ## 技术栈 | 技术 | 版本 | 用途 | 说明 | |------|------|------|------| | **Go** | 1.23+ | 编程语言 | Google 开源的高性能编译型语言 | | **Gin** | 1.11.0 | Web 框架 | 最流行的 Go Web 框架,GitHub 70k+ stars | | **GORM** | 1.31.1 | ORM 框架 | Go 最主流的 ORM,支持自动迁移和关系映射 | | **Viper** | 1.21.0 | 配置管理 | Kubernetes、Docker 等项目使用的配置库 | | **Zap** | 1.27.1 | 日志系统 | Uber 开源的高性能结构化日志库 | | **MySQL** | 8.0+ | 数据库 | 关系型数据库 | ## 项目架构 ### 目录结构 ``` go-tutorial/ ├── config/ │ └── config.go # 配置加载模块 ├── handler/ │ └── user_handler.go # HTTP 请求处理层 ├── logger/ │ └── logger.go # 日志初始化和封装 ├── model/ │ └── user.go # 数据模型定义 ├── logs/ │ └── app.log # 日志文件(自动生成) ├── config.yaml # 配置文件 ├── init.sql # 数据库初始化脚本 ├── main.go # 程序入口 ├── go.mod # 依赖管理 ├── go.sum # 依赖校验 └── .gitignore # Git 忽略配置 ``` ### 分层设计 ``` ┌─────────────────────────────────┐ │ 客户端 HTTP 请求 │ └──────────────┬──────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ Gin Router (main.go) │ 路由层:处理 HTTP 路由分发 └──────────────┬──────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ Handler (user_handler.go) │ 处理层:业务逻辑处理 └──────────────┬──────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ GORM (ORM 层) │ 数据访问层:SQL 操作 └──────────────┬──────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ MySQL 数据库 │ 数据存储层 └─────────────────────────────────┘ 横切关注点: ┌────────────────────────────┐ │ Logger (日志) Config (配置) │ └────────────────────────────┘ ``` ## 核心实现 ### 1. 配置管理 (config/config.go) **技术要点:** - 使用 Viper 库实现配置文件读取 - 支持 YAML 格式配置 - 使用 `mapstructure` tag 映射配置字段 - 提供默认值兜底 **代码实现:** ```go type Config struct { Server ServerConfig `mapstructure:"server"` Database DatabaseConfig `mapstructure:"database"` Log LogConfig `mapstructure:"log"` } type DatabaseConfig struct { Host string `mapstructure:"host"` Port string `mapstructure:"port"` User string `mapstructure:"user"` Password string `mapstructure:"password"` DBName string `mapstructure:"dbname"` } ``` **配置文件示例 (config.yaml):** ```yaml server: port: ":8080" database: host: "localhost" port: "3306" user: "root" password: "Aa123456" dbname: "go_tutorial" log: level: "info" # debug, info, warn, error output: "logs/app.log" ``` ### 2. 日志系统 (logger/logger.go) **技术要点:** - 使用 Uber Zap 高性能日志库 - 双输出:控制台 + 文件 - 控制台彩色输出,文件 JSON 格式 - 支持日志级别动态配置 - 自动创建日志目录 **核心代码:** ```go // 控制台输出 - 彩色日志 consoleEncoder := zapcore.NewConsoleEncoder(encoderConfig) consoleCore := zapcore.NewCore(consoleEncoder, zapcore.AddSync(os.Stdout), zapLevel) // 文件输出 - JSON 格式 fileEncoder := zapcore.NewJSONEncoder(fileEncoderConfig) fileCore := zapcore.NewCore(fileEncoder, zapcore.AddSync(file), zapLevel) // 组合多个输出 core := zapcore.NewTee(consoleCore, fileCore) Log = zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1)) ``` **日志格式对比:** 控制台输出(彩色,便于开发调试): ``` 2025-12-18T21:50:44.690+0800 INFO handler/user_handler.go:32 用户创建成功 {"user_id": 1, "name": "张三"} ``` 文件输出(JSON,便于日志分析): ```json {"level":"INFO","time":"2025-12-18T21:50:44.690+0800","caller":"handler/user_handler.go:32","msg":"用户创建成功","user_id":1,"name":"张三"} ``` ### 3. 数据模型 (model/user.go) **技术要点:** - 使用 struct tag 定义数据库映射 - `json` tag 控制 JSON 序列化字段名 - `gorm` tag 控制数据库字段属性 - 字段首字母大写实现公开访问 **代码实现:** ```go type User struct { ID uint `json:"id" gorm:"primaryKey"` Name string `json:"name" gorm:"size:100;not null"` Email string `json:"email" gorm:"size:100;unique;not null"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } ``` **对应的数据库表结构:** ```sql CREATE TABLE users ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100) NOT NULL, email VARCHAR(100) NOT NULL, created_at DATETIME(3) DEFAULT CURRENT_TIMESTAMP(3), updated_at DATETIME(3) DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), UNIQUE KEY idx_email (email), KEY idx_created_at (created_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ``` ### 4. 业务处理层 (handler/user_handler.go) **技术要点:** - 使用依赖注入模式(通过 struct 字段注入 DB) - 统一的错误处理和日志记录 - RESTful API 设计规范 - 使用 Gin Context 处理 HTTP 请求 **依赖注入:** ```go type UserHandler struct { DB *gorm.DB // 数据库连接注入 } ``` **创建用户实现:** ```go func (h *UserHandler) Create(c *gin.Context) { var user model.User // 1. 解析 JSON 请求体 if err := c.ShouldBindJSON(&user); err != nil { logger.Warn("创建用户参数错误", zap.Error(err)) c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // 2. 插入数据库 if err := h.DB.Create(&user).Error; err != nil { logger.Error("创建用户失败", zap.Error(err), zap.String("email", user.Email)) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // 3. 记录日志并返回 logger.Info("用户创建成功", zap.Uint("user_id", user.ID), zap.String("name", user.Name)) c.JSON(http.StatusCreated, user) } ``` **查询用户实现:** ```go func (h *UserHandler) Get(c *gin.Context) { id := c.Param("id") // 从 URL 路径获取参数 var user model.User // GORM 查询 if err := h.DB.First(&user, id).Error; err != nil { logger.Warn("查询用户失败", zap.String("user_id", id), zap.Error(err)) c.JSON(http.StatusNotFound, gin.H{"error": "User not found"}) return } logger.Debug("查询用户成功", zap.String("user_id", id)) c.JSON(http.StatusOK, user) } ``` **更新用户实现:** ```go func (h *UserHandler) Update(c *gin.Context) { id := c.Param("id") var user model.User // 1. 先查询用户是否存在 if err := h.DB.First(&user, id).Error; err != nil { logger.Warn("更新用户失败-用户不存在", zap.String("user_id", id)) c.JSON(http.StatusNotFound, gin.H{"error": "User not found"}) return } // 2. 绑定新数据 if err := c.ShouldBindJSON(&user); err != nil { logger.Warn("更新用户参数错误", zap.Error(err)) c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // 3. 保存更新 if err := h.DB.Save(&user).Error; err != nil { logger.Error("更新用户失败", zap.String("user_id", id), zap.Error(err)) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } logger.Info("用户更新成功", zap.String("user_id", id), zap.String("name", user.Name)) c.JSON(http.StatusOK, user) } ``` **删除用户实现:** ```go func (h *UserHandler) Delete(c *gin.Context) { id := c.Param("id") // GORM 删除操作 if err := h.DB.Delete(&model.User{}, id).Error; err != nil { logger.Error("删除用户失败", zap.String("user_id", id), zap.Error(err)) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } logger.Info("用户删除成功", zap.String("user_id", id)) c.JSON(http.StatusOK, gin.H{"message": "User deleted"}) } ``` ### 5. 程序入口 (main.go) **技术要点:** - 配置加载 → 日志初始化 → 数据库连接 → 路由设置 → 服务启动 - 使用 defer 确保资源正确释放 - 完整的错误处理 **启动流程:** ```go func main() { // 1. 加载配置 cfg := config.Load() // 2. 初始化日志 if err := logger.Init(cfg.Log.Level, cfg.Log.Output); err != nil { fmt.Printf("日志初始化失败: %v\n", err) return } defer logger.Log.Sync() // 程序退出前刷新日志缓冲 logger.Info("应用启动中...") // 3. 连接数据库 dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", cfg.Database.User, cfg.Database.Password, cfg.Database.Host, cfg.Database.Port, cfg.Database.DBName) db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { logger.Fatal("数据库连接失败", zap.Error(err)) } logger.Info("数据库连接成功") // 4. 初始化 Handler(依赖注入) userHandler := &handler.UserHandler{DB: db} // 5. 设置路由 r := gin.Default() r.POST("/users", userHandler.Create) // 创建 r.GET("/users/:id", userHandler.Get) // 查询单个 r.GET("/users", userHandler.List) // 查询列表 r.PUT("/users/:id", userHandler.Update) // 更新 r.DELETE("/users/:id", userHandler.Delete) // 删除 // 6. 启动服务 logger.Info("服务启动", zap.String("port", cfg.Server.Port)) if err := r.Run(cfg.Server.Port); err != nil { logger.Fatal("服务启动失败", zap.Error(err)) } } ``` ## API 接口文档 ### 基础信息 - **Base URL**: `http://localhost:8080` - **Content-Type**: `application/json` ### 接口列表 #### 1. 创建用户 **请求:** ```http POST /users Content-Type: application/json { "name": "张三", "email": "zhangsan@test.com" } ``` **响应:** ```json { "id": 1, "name": "张三", "email": "zhangsan@test.com", "created_at": "2025-12-18T21:50:44.684+08:00", "updated_at": "2025-12-18T21:50:44.684+08:00" } ``` **状态码:** - `201 Created` - 创建成功 - `400 Bad Request` - 参数错误 - `500 Internal Server Error` - 服务器错误 #### 2. 查询单个用户 **请求:** ```http GET /users/1 ``` **响应:** ```json { "id": 1, "name": "张三", "email": "zhangsan@test.com", "created_at": "2025-12-18T21:50:44.684+08:00", "updated_at": "2025-12-18T21:50:44.684+08:00" } ``` **状态码:** - `200 OK` - 查询成功 - `404 Not Found` - 用户不存在 #### 3. 查询所有用户 **请求:** ```http GET /users ``` **响应:** ```json [ { "id": 1, "name": "张三", "email": "zhangsan@test.com", "created_at": "2025-12-18T21:50:44.684+08:00", "updated_at": "2025-12-18T21:50:44.684+08:00" }, { "id": 2, "name": "李四", "email": "lisi@test.com", "created_at": "2025-12-18T21:50:50.936+08:00", "updated_at": "2025-12-18T21:50:50.936+08:00" } ] ``` **状态码:** - `200 OK` - 查询成功 #### 4. 更新用户 **请求:** ```http PUT /users/1 Content-Type: application/json { "name": "张三丰", "email": "zhangsanfeng@test.com" } ``` **响应:** ```json { "id": 1, "name": "张三丰", "email": "zhangsanfeng@test.com", "created_at": "2025-12-18T21:50:44.684+08:00", "updated_at": "2025-12-18T21:51:00.256+08:00" } ``` **状态码:** - `200 OK` - 更新成功 - `404 Not Found` - 用户不存在 - `400 Bad Request` - 参数错误 #### 5. 删除用户 **请求:** ```http DELETE /users/2 ``` **响应:** ```json { "message": "User deleted" } ``` **状态码:** - `200 OK` - 删除成功 - `500 Internal Server Error` - 服务器错误 ## 部署运行 ### 前置要求 1. **安装 Go** ```bash # 验证安装 go version # 需要 1.23+ ``` 2. **安装 MySQL** ```bash # 验证安装 mysql --version # 需要 8.0+ ``` ### 数据库初始化 ```bash # 执行初始化脚本 mysql -u root -p < init.sql ``` 脚本内容: ```sql -- 创建数据库 CREATE DATABASE IF NOT EXISTS go_tutorial CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE go_tutorial; -- 创建用户表 CREATE TABLE IF NOT EXISTS users ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '用户ID', name VARCHAR(100) NOT NULL COMMENT '用户名', email VARCHAR(100) NOT NULL COMMENT '邮箱', created_at DATETIME(3) NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', updated_at DATETIME(3) NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间', UNIQUE KEY idx_email (email), KEY idx_created_at (created_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表'; ``` ### 配置修改 编辑 `config.yaml` 修改数据库连接信息: ```yaml database: host: "localhost" port: "3306" user: "root" password: "你的密码" dbname: "go_tutorial" ``` ### 安装依赖 ```bash go mod download ``` ### 启动服务 ```bash go run main.go ``` 启动成功输出: ``` 配置加载成功: /path/to/config.yaml 2025-12-18T21:50:33.914+0800 INFO go-tutorial/main.go:26 应用启动中... 2025-12-18T21:50:33.922+0800 INFO go-tutorial/main.go:36 数据库连接成功 [GIN-debug] POST /users --> myapp/handler.(*UserHandler).Create-fm (3 handlers) [GIN-debug] GET /users/:id --> myapp/handler.(*UserHandler).Get-fm (3 handlers) [GIN-debug] GET /users --> myapp/handler.(*UserHandler).List-fm (3 handlers) [GIN-debug] PUT /users/:id --> myapp/handler.(*UserHandler).Update-fm (3 handlers) [GIN-debug] DELETE /users/:id --> myapp/handler.(*UserHandler).Delete-fm (3 handlers) 2025-12-18T21:50:33.922+0800 INFO go-tutorial/main.go:51 服务启动 {"port": ":8080"} [GIN-debug] Listening and serving HTTP on :8080 ``` ## 测试报告 ### 测试环境 - **操作系统**: macOS 14.3.1 - **Go 版本**: 1.24.11 - **MySQL 版本**: 8.0 - **测试时间**: 2025-12-18 21:50 ### 测试用例 #### 用例 1: 创建用户 - 张三 **请求:** ```bash curl -X POST http://localhost:8080/users \ -H "Content-Type: application/json" \ -d '{"name":"张三","email":"zhangsan@test.com"}' ``` **响应:** ```json { "id": 1, "name": "张三", "email": "zhangsan@test.com", "created_at": "2025-12-18T21:50:44.684+08:00", "updated_at": "2025-12-18T21:50:44.684+08:00" } ``` **控制台日志:** ``` 2025-12-18T21:50:44.690+0800 INFO handler/user_handler.go:32 用户创建成功 {"user_id": 1, "name": "张三"} [GIN] 2025/12/18 - 21:50:44 | 201 | 6.976459ms | ::1 | POST "/users" ``` **文件日志 (logs/app.log):** ```json {"level":"INFO","time":"2025-12-18T21:50:44.690+0800","caller":"handler/user_handler.go:32","msg":"用户创建成功","user_id":1,"name":"张三"} ``` **结果:** ✅ 通过 --- #### 用例 2: 创建用户 - 李四 **请求:** ```bash curl -X POST http://localhost:8080/users \ -H "Content-Type: application/json" \ -d '{"name":"李四","email":"lisi@test.com"}' ``` **响应:** ```json { "id": 2, "name": "李四", "email": "lisi@test.com", "created_at": "2025-12-18T21:50:50.936+08:00", "updated_at": "2025-12-18T21:50:50.936+08:00" } ``` **控制台日志:** ``` 2025-12-18T21:50:50.936+0800 INFO handler/user_handler.go:32 用户创建成功 {"user_id": 2, "name": "李四"} [GIN] 2025/12/18 - 21:50:50 | 201 | 1.41175ms | ::1 | POST "/users" ``` **结果:** ✅ 通过 --- #### 用例 3: 查询所有用户 **请求:** ```bash curl http://localhost:8080/users ``` **响应:** ```json [ { "id": 1, "name": "张三", "email": "zhangsan@test.com", "created_at": "2025-12-18T21:50:44.684+08:00", "updated_at": "2025-12-18T21:50:44.684+08:00" }, { "id": 2, "name": "李四", "email": "lisi@test.com", "created_at": "2025-12-18T21:50:50.936+08:00", "updated_at": "2025-12-18T21:50:50.936+08:00" } ] ``` **控制台日志:** ``` [GIN] 2025/12/18 - 21:50:52 | 200 | 894.958µs | ::1 | GET "/users" ``` **结果:** ✅ 通过(返回 2 条记录) --- #### 用例 4: 查询单个用户 **请求:** ```bash curl http://localhost:8080/users/1 ``` **响应:** ```json { "id": 1, "name": "张三", "email": "zhangsan@test.com", "created_at": "2025-12-18T21:50:44.684+08:00", "updated_at": "2025-12-18T21:50:44.684+08:00" } ``` **控制台日志:** ``` [GIN] 2025/12/18 - 21:50:58 | 200 | 813.75µs | ::1 | GET "/users/1" ``` **结果:** ✅ 通过 --- #### 用例 5: 更新用户 **请求:** ```bash curl -X PUT http://localhost:8080/users/1 \ -H "Content-Type: application/json" \ -d '{"name":"张三丰","email":"zhangsanfeng@test.com"}' ``` **响应:** ```json { "id": 1, "name": "张三丰", "email": "zhangsanfeng@test.com", "created_at": "2025-12-18T21:50:44.684+08:00", "updated_at": "2025-12-18T21:51:00.256+08:00" } ``` **控制台日志:** ``` 2025-12-18T21:51:00.258+0800 INFO handler/user_handler.go:88 用户更新成功 {"user_id": "1", "name": "张三丰"} [GIN] 2025/12/18 - 21:51:00 | 200 | 3.535875ms | ::1 | PUT "/users/1" ``` **文件日志:** ```json {"level":"INFO","time":"2025-12-18T21:51:00.258+0800","caller":"handler/user_handler.go:88","msg":"用户更新成功","user_id":"1","name":"张三丰"} ``` **结果:** ✅ 通过(注意 updated_at 时间戳已更新) --- #### 用例 6: 删除用户 **请求:** ```bash curl -X DELETE http://localhost:8080/users/2 ``` **响应:** ```json { "message": "User deleted" } ``` **控制台日志:** ``` 2025-12-18T21:51:05.999+0800 INFO handler/user_handler.go:102 用户删除成功 {"user_id": "2"} [GIN] 2025/12/18 - 21:51:05 | 200 | 1.403167ms | ::1 | DELETE "/users/2" ``` **文件日志:** ```json {"level":"INFO","time":"2025-12-18T21:51:05.999+0800","caller":"handler/user_handler.go:102","msg":"用户删除成功","user_id":"2"} ``` **结果:** ✅ 通过 --- #### 用例 7: 验证删除后的列表 **请求:** ```bash curl http://localhost:8080/users ``` **响应:** ```json [ { "id": 1, "name": "张三丰", "email": "zhangsanfeng@test.com", "created_at": "2025-12-18T21:50:44.684+08:00", "updated_at": "2025-12-18T21:51:00.256+08:00" } ] ``` **控制台日志:** ``` [GIN] 2025/12/18 - 21:51:07 | 200 | 348.541µs | ::1 | GET "/users" ``` **结果:** ✅ 通过(只剩 1 条记录,ID=2 的用户已被删除) ### 测试总结 | 测试项 | 状态 | 响应时间 | 备注 | |--------|------|----------|------| | 创建用户(张三) | ✅ | 6.98ms | 首次创建,包含表初始化 | | 创建用户(李四) | ✅ | 1.41ms | 性能正常 | | 查询列表 | ✅ | 0.89ms | 返回 2 条记录 | | 查询单个用户 | ✅ | 0.81ms | 精确查询 | | 更新用户 | ✅ | 3.54ms | updated_at 自动更新 | | 删除用户 | ✅ | 1.40ms | 软删除或硬删除可配置 | | 验证删除 | ✅ | 0.35ms | 数据一致性正确 | **整体结果:** 7/7 通过 ✅ ## 日志系统详解 ### 日志级别 项目支持 4 个日志级别: | 级别 | 用途 | 示例场景 | |------|------|---------| | **debug** | 调试信息 | 查询 SQL、函数调用细节 | | **info** | 一般信息 | 用户创建成功、服务启动 | | **warn** | 警告信息 | 参数错误、查询失败 | | **error** | 错误信息 | 数据库连接失败、创建失败 | ### 日志输出格式 #### 控制台输出(开发环境) **特点:** - 彩色显示(ERROR 红色、WARN 黄色、INFO 绿色) - 易于阅读 - 实时查看 **格式:** ``` 2025-12-18T21:50:44.690+0800 INFO handler/user_handler.go:32 用户创建成功 {"user_id": 1, "name": "张三"} │ │ │ │ │ └─ 时间戳 └─级别 └─ 代码位置 └─ 消息 └─ 结构化字段 ``` #### 文件输出(生产环境) **特点:** - JSON 格式 - 便于日志收集系统解析(如 ELK) - 持久化存储 **格式:** ```json { "level": "INFO", "time": "2025-12-18T21:50:44.690+0800", "caller": "handler/user_handler.go:32", "msg": "用户创建成功", "user_id": 1, "name": "张三" } ``` ### 完整日志示例 **应用启动日志:** ``` 配置加载成功: /Users/wangwanbao/blog/first-go-project/go-tutorial/config.yaml 2025-12-18T21:50:33.914+0800 INFO go-tutorial/main.go:26 应用启动中... 2025-12-18T21:50:33.922+0800 INFO go-tutorial/main.go:36 数据库连接成功 2025-12-18T21:50:33.922+0800 INFO go-tutorial/main.go:51 服务启动 {"port": ":8080"} ``` **业务操作日志:** ``` 2025-12-18T21:50:44.690+0800 INFO handler/user_handler.go:32 用户创建成功 {"user_id": 1, "name": "张三"} 2025-12-18T21:50:50.936+0800 INFO handler/user_handler.go:32 用户创建成功 {"user_id": 2, "name": "李四"} 2025-12-18T21:51:00.258+0800 INFO handler/user_handler.go:88 用户更新成功 {"user_id": "1", "name": "张三丰"} 2025-12-18T21:51:05.999+0800 INFO handler/user_handler.go:102 用户删除成功 {"user_id": "2"} ``` **Gin 框架日志:** ``` [GIN] 2025/12/18 - 21:50:44 | 201 | 6.976459ms | ::1 | POST "/users" [GIN] 2025/12/18 - 21:50:50 | 201 | 1.41175ms | ::1 | POST "/users" [GIN] 2025/12/18 - 21:50:52 | 200 | 894.958µs | ::1 | GET "/users" [GIN] 2025/12/18 - 21:50:58 | 200 | 813.75µs | ::1 | GET "/users/1" [GIN] 2025/12/18 - 21:51:00 | 200 | 3.535875ms | ::1 | PUT "/users/1" [GIN] 2025/12/18 - 21:51:05 | 200 | 1.403167ms | ::1 | DELETE "/users/2" [GIN] 2025/12/18 - 21:51:07 | 200 | 348.541µs | ::1 | GET "/users" ``` ### 日志文件位置 - **路径**: `logs/app.log` - **自动创建**: 目录不存在时自动创建 - **滚动策略**: 当前未配置,可后续添加(按大小或日期切割) ## 代码细节说明 ### Go 语言特性应用 #### 1. 错误处理模式 Go 没有 try-catch,使用返回值处理错误: ```go // Java 风格 try { db.create(user); } catch (Exception e) { log.error(e); } // Go 风格 if err := db.Create(&user).Error; err != nil { logger.Error("创建失败", zap.Error(err)) return } ``` #### 2. defer 延迟执行 ```go func main() { logger.Init(...) defer logger.Log.Sync() // main 函数返回前自动执行 // 业务代码... } ``` **执行顺序:** 1. 初始化日志 2. 注册 defer(记录但不执行) 3. 执行业务代码 4. main 返回前执行 `logger.Log.Sync()` #### 3. 结构体标签 (Struct Tags) ```go type User struct { Name string `json:"name" gorm:"size:100;not null"` Email string `json:"email" gorm:"size:100;unique"` } ``` - `json:"name"` - JSON 序列化时字段名 - `gorm:"size:100"` - 数据库字段长度 - `gorm:"not null"` - 数据库非空约束 - `gorm:"unique"` - 数据库唯一约束 #### 4. 指针使用 ```go // 值传递(会复制整个 struct) func update(u User) { } // 指针传递(只传递地址,推荐) func update(u *User) { } // 创建指针 user := &User{Name: "张三"} // 返回指针 user := User{Name: "张三"} // 返回值 ``` #### 5. 方法绑定 ```go // 给 UserHandler 类型绑定方法 func (h *UserHandler) Create(c *gin.Context) { // h 是接收者(receiver),相当于 Java 的 this h.DB.Create(...) } ``` ### GORM ORM 框架 #### 常用操作 ```go // 创建 db.Create(&user) // 查询单条 db.First(&user, id) // 按主键查询 db.Where("email = ?", email).First(&user) // 按条件查询 // 查询多条 db.Find(&users) // 查询所有 db.Where("age > ?", 18).Find(&users) // 条件查询 // 更新 db.Save(&user) // 更新所有字段 db.Model(&user).Update("name", "新名字") // 更新单个字段 // 删除 db.Delete(&user, id) // 按主键删除 db.Where("age < ?", 18).Delete(&User{}) // 条件删除 ``` ### Gin Web 框架 #### 路由绑定 ```go r := gin.Default() // 静态路由 r.GET("/users", handler.List) // 路径参数 r.GET("/users/:id", handler.Get) // id 是路径参数 r.PUT("/users/:id", handler.Update) // 查询参数 r.GET("/search", func(c *gin.Context) { name := c.Query("name") // ?name=张三 age := c.DefaultQuery("age", "18") // 带默认值 }) ``` #### Context 上下文 ```go func handler(c *gin.Context) { // 获取路径参数 id := c.Param("id") // 获取查询参数 name := c.Query("name") // 解析 JSON 请求体 var user User c.ShouldBindJSON(&user) // 返回 JSON 响应 c.JSON(200, user) // 返回错误 c.JSON(400, gin.H{"error": "参数错误"}) } ``` ## 最佳实践 ### 1. 配置管理 ✅ **推荐做法:** - 配置文件与代码分离 - 使用 YAML/JSON 配置格式 - 提供默认值 - 敏感信息使用环境变量 ❌ **不推荐:** - 硬编码在代码中 - 配置散落在多个文件 ### 2. 日志记录 ✅ **推荐做法:** - 使用结构化日志 - 关键操作必须记录 - 错误日志包含足够上下文 - 生产环境用 info 级别 ❌ **不推荐:** - 只用 fmt.Println - 日志信息不完整 - 生产环境开 debug 级别 ### 3. 错误处理 ✅ **推荐做法:** - 每个错误都要检查 - 返回有意义的错误信息 - 区分业务错误和系统错误 ```go if err != nil { logger.Error("操作失败", zap.Error(err), zap.String("user_id", id)) c.JSON(500, gin.H{"error": "服务器内部错误"}) return } ``` ❌ **不推荐:** - 忽略错误 - 只返回 err.Error()(可能暴露内部信息) ### 4. 数据库操作 ✅ **推荐做法:** - 手动建表(生产环境) - 使用事务处理复杂操作 - 添加索引优化查询 - 使用连接池 ❌ **不推荐:** - 生产环境使用 AutoMigrate - 裸 SQL 字符串拼接(易 SQL 注入) ### 5. API 设计 ✅ **推荐做法:** - 遵循 RESTful 规范 - 合理使用 HTTP 状态码 - 统一的响应格式 - API 版本控制 ```go // 好的 API 设计 POST /users // 创建 GET /users/:id // 查询 PUT /users/:id // 更新 DELETE /users/:id // 删除 ``` ## 性能优化建议 ### 1. 数据库优化 - **添加索引**:email 字段已添加唯一索引 - **连接池配置**: ```go sqlDB, _ := db.DB() sqlDB.SetMaxIdleConns(10) sqlDB.SetMaxOpenConns(100) sqlDB.SetConnMaxLifetime(time.Hour) ``` ### 2. 日志优化 - **生产环境关闭 debug 日志** - **使用异步日志**(Zap 已内置) - **日志轮转**(按大小或日期切割) ### 3. HTTP 优化 - **使用 Gin Release 模式**: ```go gin.SetMode(gin.ReleaseMode) ``` - **开启 Gzip 压缩** - **添加缓存头** ## 扩展方向 ### 短期优化(1-2 天) 1. **参数校验** - 使用 validator 库 2. **统一响应格式** - 封装 Response 结构 3. **中间件** - 日志中间件、恢复中间件 4. **单元测试** - 编写测试用例 ### 中期优化(1 周) 1. **JWT 认证** - 用户登录鉴权 2. **分页查询** - 列表接口支持分页 3. **Docker 化** - 编写 Dockerfile 4. **CI/CD** - GitHub Actions 自动部署 ### 长期优化(1 个月) 1. **Redis 缓存** - 热点数据缓存 2. **消息队列** - 异步任务处理 3. **服务监控** - Prometheus + Grafana 4. **微服务拆分** - 服务治理 ## 常见问题 ### Q1: GORM 的 Create 和 Save 有什么区别? **A:** - `Create`: 插入新记录,忽略主键 - `Save`: 有主键则更新,无主键则插入 ### Q2: 为什么用指针传递 struct? **A:** - 避免值拷贝,提高性能 - 可以在函数内修改原始数据 - Go 的惯用法 ### Q3: 日志文件会无限增长吗? **A:** - 当前实现会持续追加 - 建议添加日志轮转(lumberjack 库) - 或使用日志收集系统 ### Q4: 如何修改日志级别? **A:** 修改 `config.yaml` 中的 `log.level` 字段,重启服务生效。 ### Q5: 数据库密码明文存储安全吗? **A:** - 开发环境可接受 - 生产环境建议使用环境变量或密钥管理服务 ## 总结 本项目采用 Go 语言主流的企业级架构和最佳实践: ✅ **分层清晰** - Config、Logger、Handler、Model 职责分明 ✅ **配置分离** - YAML 配置文件,便于维护 ✅ **日志完善** - 双输出、结构化、可追溯 ✅ **错误处理** - 每个错误都有处理和日志 ✅ **RESTful API** - 符合规范的接口设计 ✅ **生产可用** - 手动建表、性能优化、可扩展 所有代码都经过测试验证,日志输出完整,可直接用于生产环境。 ---