From b010f82221f48377fe36d62fee66c5cba08a7a01 Mon Sep 17 00:00:00 2001 From: liumangmang Date: Tue, 30 Dec 2025 18:00:42 +0800 Subject: [PATCH] =?UTF-8?q?feat(auth):=20=E6=B7=BB=E5=8A=A0=E5=AE=8C?= =?UTF-8?q?=E6=95=B4=E7=9A=84=E7=94=A8=E6=88=B7=E8=AE=A4=E8=AF=81API?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现用户注册、登录、JWT令牌认证功能 - 集成Gin、GORM、Viper、Zap等框架 - 添加密码加密、数据库操作、中间件等完整功能 - 配置多环境支持、日志轮转、CORS处理 - 创建完整的项目结构和配置文件体系 --- .../01go-gin-demo}/context_demo.go | 0 {go-gin-demo => Web开发/01go-gin-demo}/go.mod | 0 {go-gin-demo => Web开发/01go-gin-demo}/go.sum | 0 .../01go-gin-demo}/handler_demo.go | 0 .../01go-gin-demo}/main.go | 0 Web开发/01go-gin-demo/params_form_demo.go | 27 + .../01go-gin-demo}/params_json_demo.go | 0 Web开发/01go-gin-demo/params_path_demo.go | 24 + Web开发/01go-gin-demo/params_query_demo.go | 20 + .../01go-gin-demo}/routes_demo.go | 0 .../01go-gin-demo}/user_manager.go | 0 .../02go-gin-middleware}/go.mod | 0 .../02go-gin-middleware}/go.sum | 0 .../02go-gin-middleware}/middleware_abort.go | 0 .../02go-gin-middleware}/middleware_auth.go | 0 .../02go-gin-middleware}/middleware_basic.go | 0 .../02go-gin-middleware}/middleware_complete.go | 0 .../02go-gin-middleware}/middleware_cors.go | 0 .../middleware_cors_custom.go | 0 .../02go-gin-middleware}/middleware_logger.go | 0 .../02go-gin-middleware}/middleware_recovery.go | 0 .../02go-gin-middleware}/middleware_token.go | 0 .../03go-gorm-demo}/association_methods.go | 0 .../03go-gorm-demo}/crud_create.go | 0 .../03go-gorm-demo}/crud_delete.go | 0 .../03go-gorm-demo}/crud_read.go | 0 .../03go-gorm-demo}/crud_update.go | 0 Web开发/03go-gorm-demo/db_connect.go | 29 + .../03go-gorm-demo}/go.mod | 0 .../03go-gorm-demo}/go.sum | 0 .../03go-gorm-demo}/model_basic.go | 0 .../03go-gorm-demo}/preload_strategies.go | 0 .../03go-gorm-demo}/query_advanced.go | 0 .../03go-gorm-demo}/raw_sql.go | 0 .../03go-gorm-demo}/relation_has_many.go | 0 .../03go-gorm-demo}/relation_has_one.go | 0 .../03go-gorm-demo}/relation_many_to_many.go | 0 .../03go-gorm-demo}/test.db | Bin .../03go-gorm-demo}/transaction_basic.go | 0 .../03go-gorm-demo}/transaction_rollback.go | 0 .../03go-gorm-demo}/transaction_savepoint.go | 0 Web开发/04go-viper-demo/01viper_basic.go | 36 + Web开发/04go-viper-demo/02viper_struct.go | 67 ++ Web开发/04go-viper-demo/03viper_env.go | 100 +++ Web开发/04go-viper-demo/04viper_multi_env.go | 67 ++ Web开发/04go-viper-demo/05viper_watch.go | 39 + Web开发/04go-viper-demo/06viper_json.go | 20 + Web开发/04go-viper-demo/07viper_toml.go | 22 + Web开发/04go-viper-demo/config.dev.yaml | 11 + Web开发/04go-viper-demo/config.json | 10 + Web开发/04go-viper-demo/config.prod.yaml | 11 + Web开发/04go-viper-demo/config.yaml | 17 + Web开发/04go-viper-demo/go.mod | 26 + Web开发/04go-viper-demo/go.sum | 52 ++ Web开发/04go-viper-demo/myconfig.toml | 7 + Web开发/05go-zap-demo/01zap_basic.go | 31 + Web开发/05go-zap-demo/02zap_production.go | 22 + Web开发/05go-zap-demo/03zap_custom.go | 51 ++ Web开发/05go-zap-demo/04zap_rotate.go | 48 + Web开发/05go-zap-demo/05zap_multi_output.go | 39 + Web开发/05go-zap-demo/06zap_sugar.go | 29 + Web开发/05go-zap-demo/07zap_dynamic_level.go | 45 + Web开发/05go-zap-demo/08zap_global.go | 32 + Web开发/05go-zap-demo/09zap_performance.go | 34 + Web开发/05go-zap-demo/app.log | 6 + Web开发/05go-zap-demo/go.mod | 9 + Web开发/05go-zap-demo/go.sum | 6 + Web开发/05go-zap-demo/logs/app.log | 101 +++ Web开发/06go-auth-api/.idea/.gitignore | 8 + Web开发/06go-auth-api/.idea/06go-auth-api.iml | 9 + Web开发/06go-auth-api/.idea/dataSources.xml | 20 + .../06go-auth-api/.idea/data_source_mapping.xml | 6 + Web开发/06go-auth-api/.idea/modules.xml | 8 + Web开发/06go-auth-api/.idea/vcs.xml | 6 + Web开发/06go-auth-api/21综合实战项目.md | 826 ++++++++++++++++++ Web开发/06go-auth-api/auth.db | Bin 0 -> 16384 bytes Web开发/06go-auth-api/config.go | 60 ++ Web开发/06go-auth-api/config/app.prod.yaml | 6 + Web开发/06go-auth-api/config/app.yaml | 20 + Web开发/06go-auth-api/db.go | 32 + Web开发/06go-auth-api/go.mod | 62 ++ Web开发/06go-auth-api/go.sum | 134 +++ Web开发/06go-auth-api/handlers.go | 159 ++++ Web开发/06go-auth-api/jwt.go | 66 ++ Web开发/06go-auth-api/logger.go | 56 ++ Web开发/06go-auth-api/logs/app.log | 24 + Web开发/06go-auth-api/main.go | 66 ++ Web开发/06go-auth-api/middleware.go | 94 ++ Web开发/06go-auth-api/models.go | 42 + Web开发/06go-auth-api/utils.go | 28 + go-gin-demo/params_form_demo.go | 27 - go-gin-demo/params_path_demo.go | 24 - go-gin-demo/params_query_demo.go | 20 - go-gorm-demo/db_connect.go | 29 - {hello-go => go基础语法/01hello-go}/go.mod | 0 {hello-go => go基础语法/01hello-go}/hello-go | Bin {hello-go => go基础语法/01hello-go}/main.go | 0 {go-basics => go基础语法/02go-basics}/go.mod | 0 {go-basics => go基础语法/02go-basics}/main.go | 0 .../03go-slice-map}/go.mod | 0 .../03go-slice-map}/main.go | 0 .../04go-struct-methods}/go.mod | 0 .../04go-struct-methods}/main.go | 0 .../05go-interfaces}/go.mod | 0 .../05go-interfaces}/main.go | 0 .../06go-error-handling}/go.mod | 0 .../06go-error-handling}/main.go | 0 .../07go-mini-logger}/app.log | 0 .../07go-mini-logger}/go.mod | 0 .../07go-mini-logger}/logger.go | 0 .../07go-mini-logger}/main.go | 0 .../01go-goroutine-gpm}/go.mod | 0 .../01go-goroutine-gpm}/main.go | 0 .../02go-channel-practice}/buffered.go | 0 .../02go-channel-practice}/directional.go | 4 +- .../02go-channel-practice}/go.mod | 0 .../02go-channel-practice}/pipeline.go | 0 .../02go-channel-practice}/unbuffered.go | 0 .../03go-select-practice}/go.mod | 0 .../03go-select-practice}/select_basic.go | 1 - .../03go-select-practice}/select_context.go | 0 .../03go-select-practice}/select_default.go | 0 .../03go-select-practice}/select_timeout.go | 0 .../04go-context-practice}/cancel_basic.go | 0 .../04go-context-practice}/derived_context.go | 0 .../04go-context-practice}/go.mod | 0 .../timeout_with_context.go | 0 .../05go-sync-practice}/go.mod | 0 .../05go-sync-practice}/mutex_counter.go | 0 .../05go-sync-practice}/race_counter.go | 0 .../05go-sync-practice}/rwmutex_cache.go | 0 .../05go-sync-practice}/waitgroup_like_latch.go | 0 .../06go-atomic-cpu}/atomic_cas.go | 0 .../06go-atomic-cpu}/atomic_counter.go | 0 .../06go-atomic-cpu}/cpu_busy_loop.go | 0 .../06go-atomic-cpu}/cpu_channel_wait.go | 0 .../06go-atomic-cpu}/go.mod | 0 .../07go-crawler}/crawler_basic.go | 0 .../07go-crawler}/go.mod | 0 139 files changed, 2772 insertions(+), 103 deletions(-) rename {go-gin-demo => Web开发/01go-gin-demo}/context_demo.go (100%) rename {go-gin-demo => Web开发/01go-gin-demo}/go.mod (100%) rename {go-gin-demo => Web开发/01go-gin-demo}/go.sum (100%) rename {go-gin-demo => Web开发/01go-gin-demo}/handler_demo.go (100%) rename {go-gin-demo => Web开发/01go-gin-demo}/main.go (100%) create mode 100644 Web开发/01go-gin-demo/params_form_demo.go rename {go-gin-demo => Web开发/01go-gin-demo}/params_json_demo.go (100%) create mode 100644 Web开发/01go-gin-demo/params_path_demo.go create mode 100644 Web开发/01go-gin-demo/params_query_demo.go rename {go-gin-demo => Web开发/01go-gin-demo}/routes_demo.go (100%) rename {go-gin-demo => Web开发/01go-gin-demo}/user_manager.go (100%) rename {go-gin-middleware => Web开发/02go-gin-middleware}/go.mod (100%) rename {go-gin-middleware => Web开发/02go-gin-middleware}/go.sum (100%) rename {go-gin-middleware => Web开发/02go-gin-middleware}/middleware_abort.go (100%) rename {go-gin-middleware => Web开发/02go-gin-middleware}/middleware_auth.go (100%) rename {go-gin-middleware => Web开发/02go-gin-middleware}/middleware_basic.go (100%) rename {go-gin-middleware => Web开发/02go-gin-middleware}/middleware_complete.go (100%) rename {go-gin-middleware => Web开发/02go-gin-middleware}/middleware_cors.go (100%) rename {go-gin-middleware => Web开发/02go-gin-middleware}/middleware_cors_custom.go (100%) rename {go-gin-middleware => Web开发/02go-gin-middleware}/middleware_logger.go (100%) rename {go-gin-middleware => Web开发/02go-gin-middleware}/middleware_recovery.go (100%) rename {go-gin-middleware => Web开发/02go-gin-middleware}/middleware_token.go (100%) rename {go-gorm-demo => Web开发/03go-gorm-demo}/association_methods.go (100%) rename {go-gorm-demo => Web开发/03go-gorm-demo}/crud_create.go (100%) rename {go-gorm-demo => Web开发/03go-gorm-demo}/crud_delete.go (100%) rename {go-gorm-demo => Web开发/03go-gorm-demo}/crud_read.go (100%) rename {go-gorm-demo => Web开发/03go-gorm-demo}/crud_update.go (100%) create mode 100644 Web开发/03go-gorm-demo/db_connect.go rename {go-gorm-demo => Web开发/03go-gorm-demo}/go.mod (100%) rename {go-gorm-demo => Web开发/03go-gorm-demo}/go.sum (100%) rename {go-gorm-demo => Web开发/03go-gorm-demo}/model_basic.go (100%) rename {go-gorm-demo => Web开发/03go-gorm-demo}/preload_strategies.go (100%) rename {go-gorm-demo => Web开发/03go-gorm-demo}/query_advanced.go (100%) rename {go-gorm-demo => Web开发/03go-gorm-demo}/raw_sql.go (100%) rename {go-gorm-demo => Web开发/03go-gorm-demo}/relation_has_many.go (100%) rename {go-gorm-demo => Web开发/03go-gorm-demo}/relation_has_one.go (100%) rename {go-gorm-demo => Web开发/03go-gorm-demo}/relation_many_to_many.go (100%) rename {go-gorm-demo => Web开发/03go-gorm-demo}/test.db (100%) rename {go-gorm-demo => Web开发/03go-gorm-demo}/transaction_basic.go (100%) rename {go-gorm-demo => Web开发/03go-gorm-demo}/transaction_rollback.go (100%) rename {go-gorm-demo => Web开发/03go-gorm-demo}/transaction_savepoint.go (100%) create mode 100644 Web开发/04go-viper-demo/01viper_basic.go create mode 100644 Web开发/04go-viper-demo/02viper_struct.go create mode 100644 Web开发/04go-viper-demo/03viper_env.go create mode 100644 Web开发/04go-viper-demo/04viper_multi_env.go create mode 100644 Web开发/04go-viper-demo/05viper_watch.go create mode 100644 Web开发/04go-viper-demo/06viper_json.go create mode 100644 Web开发/04go-viper-demo/07viper_toml.go create mode 100644 Web开发/04go-viper-demo/config.dev.yaml create mode 100644 Web开发/04go-viper-demo/config.json create mode 100644 Web开发/04go-viper-demo/config.prod.yaml create mode 100644 Web开发/04go-viper-demo/config.yaml create mode 100644 Web开发/04go-viper-demo/go.mod create mode 100644 Web开发/04go-viper-demo/go.sum create mode 100644 Web开发/04go-viper-demo/myconfig.toml create mode 100644 Web开发/05go-zap-demo/01zap_basic.go create mode 100644 Web开发/05go-zap-demo/02zap_production.go create mode 100644 Web开发/05go-zap-demo/03zap_custom.go create mode 100644 Web开发/05go-zap-demo/04zap_rotate.go create mode 100644 Web开发/05go-zap-demo/05zap_multi_output.go create mode 100644 Web开发/05go-zap-demo/06zap_sugar.go create mode 100644 Web开发/05go-zap-demo/07zap_dynamic_level.go create mode 100644 Web开发/05go-zap-demo/08zap_global.go create mode 100644 Web开发/05go-zap-demo/09zap_performance.go create mode 100644 Web开发/05go-zap-demo/app.log create mode 100644 Web开发/05go-zap-demo/go.mod create mode 100644 Web开发/05go-zap-demo/go.sum create mode 100644 Web开发/05go-zap-demo/logs/app.log create mode 100644 Web开发/06go-auth-api/.idea/.gitignore create mode 100644 Web开发/06go-auth-api/.idea/06go-auth-api.iml create mode 100644 Web开发/06go-auth-api/.idea/dataSources.xml create mode 100644 Web开发/06go-auth-api/.idea/data_source_mapping.xml create mode 100644 Web开发/06go-auth-api/.idea/modules.xml create mode 100644 Web开发/06go-auth-api/.idea/vcs.xml create mode 100644 Web开发/06go-auth-api/21综合实战项目.md create mode 100644 Web开发/06go-auth-api/auth.db create mode 100644 Web开发/06go-auth-api/config.go create mode 100644 Web开发/06go-auth-api/config/app.prod.yaml create mode 100644 Web开发/06go-auth-api/config/app.yaml create mode 100644 Web开发/06go-auth-api/db.go create mode 100644 Web开发/06go-auth-api/go.mod create mode 100644 Web开发/06go-auth-api/go.sum create mode 100644 Web开发/06go-auth-api/handlers.go create mode 100644 Web开发/06go-auth-api/jwt.go create mode 100644 Web开发/06go-auth-api/logger.go create mode 100644 Web开发/06go-auth-api/logs/app.log create mode 100644 Web开发/06go-auth-api/main.go create mode 100644 Web开发/06go-auth-api/middleware.go create mode 100644 Web开发/06go-auth-api/models.go create mode 100644 Web开发/06go-auth-api/utils.go delete mode 100644 go-gin-demo/params_form_demo.go delete mode 100644 go-gin-demo/params_path_demo.go delete mode 100644 go-gin-demo/params_query_demo.go delete mode 100644 go-gorm-demo/db_connect.go rename {hello-go => go基础语法/01hello-go}/go.mod (100%) rename {hello-go => go基础语法/01hello-go}/hello-go (100%) rename {hello-go => go基础语法/01hello-go}/main.go (100%) rename {go-basics => go基础语法/02go-basics}/go.mod (100%) rename {go-basics => go基础语法/02go-basics}/main.go (100%) rename {go-slice-map => go基础语法/03go-slice-map}/go.mod (100%) rename {go-slice-map => go基础语法/03go-slice-map}/main.go (100%) rename {go-struct-methods => go基础语法/04go-struct-methods}/go.mod (100%) rename {go-struct-methods => go基础语法/04go-struct-methods}/main.go (100%) rename {go-interfaces => go基础语法/05go-interfaces}/go.mod (100%) rename {go-interfaces => go基础语法/05go-interfaces}/main.go (100%) rename {go-error-handling => go基础语法/06go-error-handling}/go.mod (100%) rename {go-error-handling => go基础语法/06go-error-handling}/main.go (100%) rename {go-mini-logger => go基础语法/07go-mini-logger}/app.log (100%) rename {go-mini-logger => go基础语法/07go-mini-logger}/go.mod (100%) rename {go-mini-logger => go基础语法/07go-mini-logger}/logger.go (100%) rename {go-mini-logger => go基础语法/07go-mini-logger}/main.go (100%) rename {go-goroutine-gpm => go并发模型/01go-goroutine-gpm}/go.mod (100%) rename {go-goroutine-gpm => go并发模型/01go-goroutine-gpm}/main.go (100%) rename {go-channel-practice => go并发模型/02go-channel-practice}/buffered.go (100%) rename {go-channel-practice => go并发模型/02go-channel-practice}/directional.go (78%) rename {go-channel-practice => go并发模型/02go-channel-practice}/go.mod (100%) rename {go-channel-practice => go并发模型/02go-channel-practice}/pipeline.go (100%) rename {go-channel-practice => go并发模型/02go-channel-practice}/unbuffered.go (100%) rename {go-select-practice => go并发模型/03go-select-practice}/go.mod (100%) rename {go-select-practice => go并发模型/03go-select-practice}/select_basic.go (99%) rename {go-select-practice => go并发模型/03go-select-practice}/select_context.go (100%) rename {go-select-practice => go并发模型/03go-select-practice}/select_default.go (100%) rename {go-select-practice => go并发模型/03go-select-practice}/select_timeout.go (100%) rename {go-context-practice => go并发模型/04go-context-practice}/cancel_basic.go (100%) rename {go-context-practice => go并发模型/04go-context-practice}/derived_context.go (100%) rename {go-context-practice => go并发模型/04go-context-practice}/go.mod (100%) rename {go-context-practice => go并发模型/04go-context-practice}/timeout_with_context.go (100%) rename {go-sync-practice => go并发模型/05go-sync-practice}/go.mod (100%) rename {go-sync-practice => go并发模型/05go-sync-practice}/mutex_counter.go (100%) rename {go-sync-practice => go并发模型/05go-sync-practice}/race_counter.go (100%) rename {go-sync-practice => go并发模型/05go-sync-practice}/rwmutex_cache.go (100%) rename {go-sync-practice => go并发模型/05go-sync-practice}/waitgroup_like_latch.go (100%) rename {go-atomic-cpu => go并发模型/06go-atomic-cpu}/atomic_cas.go (100%) rename {go-atomic-cpu => go并发模型/06go-atomic-cpu}/atomic_counter.go (100%) rename {go-atomic-cpu => go并发模型/06go-atomic-cpu}/cpu_busy_loop.go (100%) rename {go-atomic-cpu => go并发模型/06go-atomic-cpu}/cpu_channel_wait.go (100%) rename {go-atomic-cpu => go并发模型/06go-atomic-cpu}/go.mod (100%) rename {go-crawler => go并发模型/07go-crawler}/crawler_basic.go (100%) rename {go-crawler => go并发模型/07go-crawler}/go.mod (100%) diff --git a/go-gin-demo/context_demo.go b/Web开发/01go-gin-demo/context_demo.go similarity index 100% rename from go-gin-demo/context_demo.go rename to Web开发/01go-gin-demo/context_demo.go diff --git a/go-gin-demo/go.mod b/Web开发/01go-gin-demo/go.mod similarity index 100% rename from go-gin-demo/go.mod rename to Web开发/01go-gin-demo/go.mod diff --git a/go-gin-demo/go.sum b/Web开发/01go-gin-demo/go.sum similarity index 100% rename from go-gin-demo/go.sum rename to Web开发/01go-gin-demo/go.sum diff --git a/go-gin-demo/handler_demo.go b/Web开发/01go-gin-demo/handler_demo.go similarity index 100% rename from go-gin-demo/handler_demo.go rename to Web开发/01go-gin-demo/handler_demo.go diff --git a/go-gin-demo/main.go b/Web开发/01go-gin-demo/main.go similarity index 100% rename from go-gin-demo/main.go rename to Web开发/01go-gin-demo/main.go diff --git a/Web开发/01go-gin-demo/params_form_demo.go b/Web开发/01go-gin-demo/params_form_demo.go new file mode 100644 index 0000000..4c0448c --- /dev/null +++ b/Web开发/01go-gin-demo/params_form_demo.go @@ -0,0 +1,27 @@ +package main + +import ( + "github.com/gin-gonic/gin" +) + +func main() { + r := gin.Default() + + r.POST("/login", func(c *gin.Context) { + username := c.PostForm("username") // 获取表单参数 + password := c.PostForm("password") + + // 简单验证 + if username == "" || password == "" { + c.JSON(400, gin.H{"error": "username and password required"}) + return + } + + c.JSON(200, gin.H{ + "message": "Login successful", + "username": username, + }) + }) + + r.Run(":8080") +} diff --git a/go-gin-demo/params_json_demo.go b/Web开发/01go-gin-demo/params_json_demo.go similarity index 100% rename from go-gin-demo/params_json_demo.go rename to Web开发/01go-gin-demo/params_json_demo.go diff --git a/Web开发/01go-gin-demo/params_path_demo.go b/Web开发/01go-gin-demo/params_path_demo.go new file mode 100644 index 0000000..b7cd950 --- /dev/null +++ b/Web开发/01go-gin-demo/params_path_demo.go @@ -0,0 +1,24 @@ +package main + +import ( + "github.com/gin-gonic/gin" +) + +func main() { + r := gin.Default() + + // 单个参数 + r.GET("/users/:id", func(c *gin.Context) { + id := c.Param("id") // 获取参数 + c.JSON(200, gin.H{"user_id": id}) + }) + + // 多个参数 + r.GET("/users/:uid/posts/:pid", func(c *gin.Context) { + uid := c.Param("uid") + pid := c.Param("pid") + c.JSON(200, gin.H{"uid": uid, "pid": pid}) + }) + + r.Run(":8080") +} diff --git a/Web开发/01go-gin-demo/params_query_demo.go b/Web开发/01go-gin-demo/params_query_demo.go new file mode 100644 index 0000000..68cf24c --- /dev/null +++ b/Web开发/01go-gin-demo/params_query_demo.go @@ -0,0 +1,20 @@ +package main + +import ( + "github.com/gin-gonic/gin" +) + +func main() { + r := gin.Default() + + r.GET("/search", func(c *gin.Context) { + keyword := c.Query("q") // 获取查询参数 + limit := c.DefaultQuery("limit", "10") // 带默认值 + c.JSON(200, gin.H{ + "keyword": keyword, + "limit": limit, + }) + }) + + r.Run(":8080") +} diff --git a/go-gin-demo/routes_demo.go b/Web开发/01go-gin-demo/routes_demo.go similarity index 100% rename from go-gin-demo/routes_demo.go rename to Web开发/01go-gin-demo/routes_demo.go diff --git a/go-gin-demo/user_manager.go b/Web开发/01go-gin-demo/user_manager.go similarity index 100% rename from go-gin-demo/user_manager.go rename to Web开发/01go-gin-demo/user_manager.go diff --git a/go-gin-middleware/go.mod b/Web开发/02go-gin-middleware/go.mod similarity index 100% rename from go-gin-middleware/go.mod rename to Web开发/02go-gin-middleware/go.mod diff --git a/go-gin-middleware/go.sum b/Web开发/02go-gin-middleware/go.sum similarity index 100% rename from go-gin-middleware/go.sum rename to Web开发/02go-gin-middleware/go.sum diff --git a/go-gin-middleware/middleware_abort.go b/Web开发/02go-gin-middleware/middleware_abort.go similarity index 100% rename from go-gin-middleware/middleware_abort.go rename to Web开发/02go-gin-middleware/middleware_abort.go diff --git a/go-gin-middleware/middleware_auth.go b/Web开发/02go-gin-middleware/middleware_auth.go similarity index 100% rename from go-gin-middleware/middleware_auth.go rename to Web开发/02go-gin-middleware/middleware_auth.go diff --git a/go-gin-middleware/middleware_basic.go b/Web开发/02go-gin-middleware/middleware_basic.go similarity index 100% rename from go-gin-middleware/middleware_basic.go rename to Web开发/02go-gin-middleware/middleware_basic.go diff --git a/go-gin-middleware/middleware_complete.go b/Web开发/02go-gin-middleware/middleware_complete.go similarity index 100% rename from go-gin-middleware/middleware_complete.go rename to Web开发/02go-gin-middleware/middleware_complete.go diff --git a/go-gin-middleware/middleware_cors.go b/Web开发/02go-gin-middleware/middleware_cors.go similarity index 100% rename from go-gin-middleware/middleware_cors.go rename to Web开发/02go-gin-middleware/middleware_cors.go diff --git a/go-gin-middleware/middleware_cors_custom.go b/Web开发/02go-gin-middleware/middleware_cors_custom.go similarity index 100% rename from go-gin-middleware/middleware_cors_custom.go rename to Web开发/02go-gin-middleware/middleware_cors_custom.go diff --git a/go-gin-middleware/middleware_logger.go b/Web开发/02go-gin-middleware/middleware_logger.go similarity index 100% rename from go-gin-middleware/middleware_logger.go rename to Web开发/02go-gin-middleware/middleware_logger.go diff --git a/go-gin-middleware/middleware_recovery.go b/Web开发/02go-gin-middleware/middleware_recovery.go similarity index 100% rename from go-gin-middleware/middleware_recovery.go rename to Web开发/02go-gin-middleware/middleware_recovery.go diff --git a/go-gin-middleware/middleware_token.go b/Web开发/02go-gin-middleware/middleware_token.go similarity index 100% rename from go-gin-middleware/middleware_token.go rename to Web开发/02go-gin-middleware/middleware_token.go diff --git a/go-gorm-demo/association_methods.go b/Web开发/03go-gorm-demo/association_methods.go similarity index 100% rename from go-gorm-demo/association_methods.go rename to Web开发/03go-gorm-demo/association_methods.go diff --git a/go-gorm-demo/crud_create.go b/Web开发/03go-gorm-demo/crud_create.go similarity index 100% rename from go-gorm-demo/crud_create.go rename to Web开发/03go-gorm-demo/crud_create.go diff --git a/go-gorm-demo/crud_delete.go b/Web开发/03go-gorm-demo/crud_delete.go similarity index 100% rename from go-gorm-demo/crud_delete.go rename to Web开发/03go-gorm-demo/crud_delete.go diff --git a/go-gorm-demo/crud_read.go b/Web开发/03go-gorm-demo/crud_read.go similarity index 100% rename from go-gorm-demo/crud_read.go rename to Web开发/03go-gorm-demo/crud_read.go diff --git a/go-gorm-demo/crud_update.go b/Web开发/03go-gorm-demo/crud_update.go similarity index 100% rename from go-gorm-demo/crud_update.go rename to Web开发/03go-gorm-demo/crud_update.go diff --git a/Web开发/03go-gorm-demo/db_connect.go b/Web开发/03go-gorm-demo/db_connect.go new file mode 100644 index 0000000..5594b01 --- /dev/null +++ b/Web开发/03go-gorm-demo/db_connect.go @@ -0,0 +1,29 @@ +package main + +import ( + "fmt" + "gorm.io/driver/sqlite" + "gorm.io/gorm" +) + +func main() { + // 连接 SQLite(文件数据库) + db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{}) + if err != nil { + panic("failed to connect database") + } + + fmt.Println("Database connected successfully!") + + // 获取底层的 *sql.DB + sqlDB, err := db.DB() + if err != nil { + panic(err) + } + + // 设置连接池 + sqlDB.SetMaxIdleConns(10) + sqlDB.SetMaxOpenConns(100) + + fmt.Println("Connection pool configured") +} diff --git a/go-gorm-demo/go.mod b/Web开发/03go-gorm-demo/go.mod similarity index 100% rename from go-gorm-demo/go.mod rename to Web开发/03go-gorm-demo/go.mod diff --git a/go-gorm-demo/go.sum b/Web开发/03go-gorm-demo/go.sum similarity index 100% rename from go-gorm-demo/go.sum rename to Web开发/03go-gorm-demo/go.sum diff --git a/go-gorm-demo/model_basic.go b/Web开发/03go-gorm-demo/model_basic.go similarity index 100% rename from go-gorm-demo/model_basic.go rename to Web开发/03go-gorm-demo/model_basic.go diff --git a/go-gorm-demo/preload_strategies.go b/Web开发/03go-gorm-demo/preload_strategies.go similarity index 100% rename from go-gorm-demo/preload_strategies.go rename to Web开发/03go-gorm-demo/preload_strategies.go diff --git a/go-gorm-demo/query_advanced.go b/Web开发/03go-gorm-demo/query_advanced.go similarity index 100% rename from go-gorm-demo/query_advanced.go rename to Web开发/03go-gorm-demo/query_advanced.go diff --git a/go-gorm-demo/raw_sql.go b/Web开发/03go-gorm-demo/raw_sql.go similarity index 100% rename from go-gorm-demo/raw_sql.go rename to Web开发/03go-gorm-demo/raw_sql.go diff --git a/go-gorm-demo/relation_has_many.go b/Web开发/03go-gorm-demo/relation_has_many.go similarity index 100% rename from go-gorm-demo/relation_has_many.go rename to Web开发/03go-gorm-demo/relation_has_many.go diff --git a/go-gorm-demo/relation_has_one.go b/Web开发/03go-gorm-demo/relation_has_one.go similarity index 100% rename from go-gorm-demo/relation_has_one.go rename to Web开发/03go-gorm-demo/relation_has_one.go diff --git a/go-gorm-demo/relation_many_to_many.go b/Web开发/03go-gorm-demo/relation_many_to_many.go similarity index 100% rename from go-gorm-demo/relation_many_to_many.go rename to Web开发/03go-gorm-demo/relation_many_to_many.go diff --git a/go-gorm-demo/test.db b/Web开发/03go-gorm-demo/test.db similarity index 100% rename from go-gorm-demo/test.db rename to Web开发/03go-gorm-demo/test.db diff --git a/go-gorm-demo/transaction_basic.go b/Web开发/03go-gorm-demo/transaction_basic.go similarity index 100% rename from go-gorm-demo/transaction_basic.go rename to Web开发/03go-gorm-demo/transaction_basic.go diff --git a/go-gorm-demo/transaction_rollback.go b/Web开发/03go-gorm-demo/transaction_rollback.go similarity index 100% rename from go-gorm-demo/transaction_rollback.go rename to Web开发/03go-gorm-demo/transaction_rollback.go diff --git a/go-gorm-demo/transaction_savepoint.go b/Web开发/03go-gorm-demo/transaction_savepoint.go similarity index 100% rename from go-gorm-demo/transaction_savepoint.go rename to Web开发/03go-gorm-demo/transaction_savepoint.go diff --git a/Web开发/04go-viper-demo/01viper_basic.go b/Web开发/04go-viper-demo/01viper_basic.go new file mode 100644 index 0000000..71f0b44 --- /dev/null +++ b/Web开发/04go-viper-demo/01viper_basic.go @@ -0,0 +1,36 @@ +package main + +import ( + "fmt" + + "github.com/spf13/viper" +) + +func main() { + // 1. 设置配置文件名和路径 + viper.SetConfigName("config") // 配置文件名(不含扩展名) + viper.SetConfigType("yaml") // 配置文件类型 + viper.AddConfigPath(".") // 配置文件路径 + + // 2. 读取配置文件 + if err := viper.ReadInConfig(); err != nil { + panic(fmt.Errorf("Fatal error config file: %s", err)) + } + + fmt.Println("Config file loaded successfully!") + + // 3. 读取单个配置项 + appName := viper.GetString("app.name") + appPort := viper.GetInt("app.port") + fmt.Printf("App Name: %s\n", appName) + fmt.Printf("App Port: %d\n", appPort) + + // 4. 读取嵌套配置 + dbHost := viper.GetString("database.host") + dbPort := viper.GetInt("database.port") + fmt.Printf("Database: %s:%d\n", dbHost, dbPort) + + // 5. 读取所有配置 + allSettings := viper.AllSettings() + fmt.Printf("All settings: %v\n", allSettings) +} diff --git a/Web开发/04go-viper-demo/02viper_struct.go b/Web开发/04go-viper-demo/02viper_struct.go new file mode 100644 index 0000000..c067e62 --- /dev/null +++ b/Web开发/04go-viper-demo/02viper_struct.go @@ -0,0 +1,67 @@ +package main + +import ( + "fmt" + + "github.com/spf13/viper" +) + +// 配置结构体 +type Config struct { + App AppConfig `mapstructure:"app"` + Database DatabaseConfig `mapstructure:"database"` + Redis RedisConfig `mapstructure:"redis"` +} + +type AppConfig struct { + Name string `mapstructure:"name"` + Version string `mapstructure:"version"` + Port int `mapstructure:"port"` +} + +type DatabaseConfig struct { + Host string `mapstructure:"host"` + Port int `mapstructure:"port"` + Username string `mapstructure:"username"` + Password string `mapstructure:"password"` + DBName string `mapstructure:"dbname"` +} + +type RedisConfig struct { + Host string `mapstructure:"host"` + Port int `mapstructure:"port"` + Password string `mapstructure:"password"` + DB int `mapstructure:"db"` +} + +func main() { + // 读取配置文件 + viper.SetConfigName("config") + viper.SetConfigType("yaml") + viper.AddConfigPath(".") + + if err := viper.ReadInConfig(); err != nil { + panic(err) + } + + // 将配置绑定到结构体 + var config Config + if err := viper.Unmarshal(&config); err != nil { + panic(err) + } + + // 使用配置 + fmt.Printf("App: %s v%s\n", config.App.Name, config.App.Version) + fmt.Printf("Port: %d\n", config.App.Port) + fmt.Printf("Database: %s@%s:%d/%s\n", + config.Database.Username, + config.Database.Host, + config.Database.Port, + config.Database.DBName, + ) + fmt.Printf("Redis: %s:%d (DB %d)\n", + config.Redis.Host, + config.Redis.Port, + config.Redis.DB, + ) +} diff --git a/Web开发/04go-viper-demo/03viper_env.go b/Web开发/04go-viper-demo/03viper_env.go new file mode 100644 index 0000000..c43accd --- /dev/null +++ b/Web开发/04go-viper-demo/03viper_env.go @@ -0,0 +1,100 @@ +// package main +// +// import ( +// +// "fmt" +// "os" +// +// "github.com/spf13/viper" +// +// ) +// +// func main() { +// // 方案:先设置环境变量,再设置前缀和绑定 +// // 1. 先设置环境变量(模拟) +// os.Setenv("MYAPP_PORT", "9000") +// os.Setenv("MYAPP_DEBUG", "true") +// os.Setenv("MYAPP_DB_HOST", "192.168.1.100") +// +// // 2. 设置环境变量前缀 +// viper.SetEnvPrefix("MYAPP") +// +// // 3. 设置默认值 +// viper.SetDefault("port", 8080) +// viper.SetDefault("debug", false) +// viper.SetDefault("db.host", "localhost") +// +// // 4. 绑定环境变量 - 显式指定完整的环境变量名(不加前缀) +// viper.BindEnv("port", "MYAPP_PORT") +// viper.BindEnv("debug", "MYAPP_DEBUG") +// viper.BindEnv("db.host", "MYAPP_DB_HOST") // 直接指定完整的环境变量名 +// +// // 5. 读取配置 +// port := viper.GetInt("port") +// debug := viper.GetBool("debug") +// dbHost := viper.GetString("db.host") +// +// fmt.Printf("Port: %d (from env)\n", port) +// fmt.Printf("Debug: %v (from env)\n", debug) +// fmt.Printf("DB Host: %s (from env)\n", dbHost) +// +// // 6. 启用自动环境变量绑定 +// viper.AutomaticEnv() +// +// // 设置自定义环境变量后获取 +// os.Setenv("MYAPP_CUSTOM_KEY", "custom_value") +// // 注意:AutomaticEnv 后,MYAPP_CUSTOM_KEY 会映射到 custom_key +// customKey := viper.GetString("custom_key") +// fmt.Printf("Custom Key: %s\n", customKey) +// +// // 验证环境变量是否真的设置成功 +// fmt.Println("\n=== 验证环境变量 ===") +// fmt.Printf("MYAPP_PORT: %s\n", os.Getenv("MYAPP_PORT")) +// fmt.Printf("MYAPP_DEBUG: %s\n", os.Getenv("MYAPP_DEBUG")) +// fmt.Printf("MYAPP_DB_HOST: %s\n", os.Getenv("MYAPP_DB_HOST")) +// } +package main + +import ( + "fmt" + "os" + "strings" + + "github.com/spf13/viper" +) + +func main() { + // 1. 设置环境变量前缀 + viper.SetEnvPrefix("MYAPP") // 自动添加前缀 + viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) + + // 2. 绑定环境变量 + viper.BindEnv("port") // 绑定 MYAPP_PORT + viper.BindEnv("debug") // 绑定 MYAPP_DEBUG + viper.BindEnv("db.host") // 绑定 MYAPP_DB_HOST + + // 3. 设置默认值 + viper.SetDefault("port", 8080) + viper.SetDefault("debug", false) + + // 4. 设置环境变量(模拟) + os.Setenv("MYAPP_PORT", "9000") + os.Setenv("MYAPP_DEBUG", "true") + os.Setenv("MYAPP_DB_HOST", "192.168.1.100") + + // 5. 读取配置(优先级:环境变量 > 配置文件 > 默认值) + port := viper.GetInt("port") + debug := viper.GetBool("debug") + dbHost := viper.GetString("db.host") + + fmt.Printf("Port: %d (from env)\n", port) + fmt.Printf("Debug: %v (from env)\n", debug) + fmt.Printf("DB Host: %s (from env)\n", dbHost) + + // 6. 自动绑定所有环境变量 + viper.AutomaticEnv() + + os.Setenv("MYAPP_CUSTOM_KEY", "custom_value") + customKey := viper.GetString("custom.key") // 自动转换 CUSTOM_KEY -> custom.key + fmt.Printf("Custom Key: %s\n", customKey) +} diff --git a/Web开发/04go-viper-demo/04viper_multi_env.go b/Web开发/04go-viper-demo/04viper_multi_env.go new file mode 100644 index 0000000..a6f6f8f --- /dev/null +++ b/Web开发/04go-viper-demo/04viper_multi_env.go @@ -0,0 +1,67 @@ +package main + +import ( + "fmt" + "github.com/spf13/viper" + "os" +) + +type Config struct { + App struct { + Name string `mapstructure:"name"` + Port int `mapstructure:"port"` + Debug bool `mapstructure:"debug"` + } `mapstructure:"app"` + Database struct { + Host string `mapstructure:"host"` + Port int `mapstructure:"port"` + Username string `mapstructure:"username"` + Password string `mapstructure:"password"` + DBName string `mapstructure:"dbname"` + } `mapstructure:"database"` +} + +func LoadConfig(env string) (*Config, error) { + // 根据环境加载配置文件 + configName := fmt.Sprintf("config.%s", env) + + viper.SetConfigName(configName) + viper.SetConfigType("yaml") + viper.AddConfigPath(".") + + if err := viper.ReadInConfig(); err != nil { + return nil, err + } + + var config Config + if err := viper.Unmarshal(&config); err != nil { + return nil, err + } + + return &config, nil +} + +func main() { + // 从环境变量读取环境名称 + env := os.Getenv("APP_ENV") + if env == "" { + env = "dev" // 默认开发环境 + } + + fmt.Printf("Loading config for environment: %s\n", env) + + config, err := LoadConfig(env) + if err != nil { + panic(err) + } + + fmt.Printf("App: %s\n", config.App.Name) + fmt.Printf("Port: %d\n", config.App.Port) + fmt.Printf("Debug: %v\n", config.App.Debug) + fmt.Printf("Database: %s@%s:%d/%s\n", + config.Database.Username, + config.Database.Host, + config.Database.Port, + config.Database.DBName, + ) +} diff --git a/Web开发/04go-viper-demo/05viper_watch.go b/Web开发/04go-viper-demo/05viper_watch.go new file mode 100644 index 0000000..d13db47 --- /dev/null +++ b/Web开发/04go-viper-demo/05viper_watch.go @@ -0,0 +1,39 @@ +package main + +import ( + "fmt" + + "github.com/fsnotify/fsnotify" + "github.com/spf13/viper" +) + +func main() { + // 读取配置文件 + viper.SetConfigName("config") + viper.SetConfigType("yaml") + viper.AddConfigPath(".") + + if err := viper.ReadInConfig(); err != nil { + panic(err) + } + + fmt.Println("Initial config loaded") + fmt.Printf("App Name: %s\n", viper.GetString("app.name")) + fmt.Printf("App Port: %d\n", viper.GetInt("app.port")) + + // 监听配置文件变化 + viper.WatchConfig() + viper.OnConfigChange(func(in fsnotify.Event) { + fmt.Println("\n--- Config file changed! ---") + fmt.Printf("Event: %s\n", in.Op.String()) + fmt.Printf("File: %s\n", in.Name) + fmt.Printf("App Name: %s\n", viper.GetString("app.name")) + fmt.Printf("App Port: %d\n", viper.GetInt("app.port")) + }) + + fmt.Println("\nWatching for config changes... (modify config.yaml to see changes)") + fmt.Println("Press Ctrl+C to exit") + + // 保持程序运行 + select {} +} diff --git a/Web开发/04go-viper-demo/06viper_json.go b/Web开发/04go-viper-demo/06viper_json.go new file mode 100644 index 0000000..cc0dbf1 --- /dev/null +++ b/Web开发/04go-viper-demo/06viper_json.go @@ -0,0 +1,20 @@ +package main + +import ( + "fmt" + + "github.com/spf13/viper" +) + +func main() { + viper.SetConfigName("config") + viper.SetConfigType("json") + viper.AddConfigPath(".") + + if err := viper.ReadInConfig(); err != nil { + panic(err) + } + + fmt.Printf("App Name: %s\n", viper.GetString("app.name")) + fmt.Printf("App Port: %d\n", viper.GetInt("app.port")) +} diff --git a/Web开发/04go-viper-demo/07viper_toml.go b/Web开发/04go-viper-demo/07viper_toml.go new file mode 100644 index 0000000..a06310e --- /dev/null +++ b/Web开发/04go-viper-demo/07viper_toml.go @@ -0,0 +1,22 @@ +package main + +import ( + "fmt" + + "github.com/spf13/viper" +) + +func main() { + // 直接指定完整文件路径(避免和其他 config.* 文件冲突) + viper.SetConfigFile("./myconfig.toml") + + if err := viper.ReadInConfig(); err != nil { + panic(err) + } + + // 打印实际读取的配置文件 + fmt.Printf("Using config file: %s\n\n", viper.ConfigFileUsed()) + + fmt.Printf("App Name: %s\n", viper.GetString("app.name")) + fmt.Printf("App Port: %d\n", viper.GetInt("app.port")) +} diff --git a/Web开发/04go-viper-demo/config.dev.yaml b/Web开发/04go-viper-demo/config.dev.yaml new file mode 100644 index 0000000..041b618 --- /dev/null +++ b/Web开发/04go-viper-demo/config.dev.yaml @@ -0,0 +1,11 @@ +app: + name: MyApp-Dev + port: 8080 + debug: true + +database: + host: localhost + port: 3306 + username: root + password: password + dbname: dev_db diff --git a/Web开发/04go-viper-demo/config.json b/Web开发/04go-viper-demo/config.json new file mode 100644 index 0000000..788e5f4 --- /dev/null +++ b/Web开发/04go-viper-demo/config.json @@ -0,0 +1,10 @@ +{ + "app": { + "name": "MyApp", + "port": 8080 + }, + "database": { + "host": "localhost", + "port": 3306 + } +} diff --git a/Web开发/04go-viper-demo/config.prod.yaml b/Web开发/04go-viper-demo/config.prod.yaml new file mode 100644 index 0000000..1cd62bc --- /dev/null +++ b/Web开发/04go-viper-demo/config.prod.yaml @@ -0,0 +1,11 @@ +app: + name: MyApp-Prod + port: 80 + debug: false + +database: + host: prod.database.com + port: 3306 + username: prod_user + password: prod_password + dbname: prod_db diff --git a/Web开发/04go-viper-demo/config.yaml b/Web开发/04go-viper-demo/config.yaml new file mode 100644 index 0000000..c12f74a --- /dev/null +++ b/Web开发/04go-viper-demo/config.yaml @@ -0,0 +1,17 @@ +app: + name: MangMang + version: 1.0.0 + port: 9999 + +database: + host: localhost + port: 3306 + username: root + password: password + dbname: testdb + +redis: + host: localhost + port: 6379 + password: "" + db: 0 diff --git a/Web开发/04go-viper-demo/go.mod b/Web开发/04go-viper-demo/go.mod new file mode 100644 index 0000000..1942eb4 --- /dev/null +++ b/Web开发/04go-viper-demo/go.mod @@ -0,0 +1,26 @@ +module go-viper-demo + +go 1.22.2 + +require ( + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.18.2 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.9.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/Web开发/04go-viper-demo/go.sum b/Web开发/04go-viper-demo/go.sum new file mode 100644 index 0000000..3fadd40 --- /dev/null +++ b/Web开发/04go-viper-demo/go.sum @@ -0,0 +1,52 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= +github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/Web开发/04go-viper-demo/myconfig.toml b/Web开发/04go-viper-demo/myconfig.toml new file mode 100644 index 0000000..9e46bea --- /dev/null +++ b/Web开发/04go-viper-demo/myconfig.toml @@ -0,0 +1,7 @@ +[app] +name = "MyApp" +port = 8080 + +[database] +host = "localhost" +port = 3306 diff --git a/Web开发/05go-zap-demo/01zap_basic.go b/Web开发/05go-zap-demo/01zap_basic.go new file mode 100644 index 0000000..e2bbad5 --- /dev/null +++ b/Web开发/05go-zap-demo/01zap_basic.go @@ -0,0 +1,31 @@ +package main + +import ( + "go.uber.org/zap" +) + +func main() { + // 1. 创建默认 Logger(开发模式) + logger, _ := zap.NewDevelopment() + defer logger.Sync() // 刷新缓冲区 + + // 2. 基础日志 + logger.Info("Hello Zap!") + logger.Warn("This is a warning") + logger.Error("This is an error") + + // 3. 结构化日志(带字段) + logger.Info("User logged in", + zap.String("username", "alice"), + zap.Int("user_id", 123), + zap.Bool("is_admin", true), + ) + + // 4. 不同日志级别 + logger.Debug("Debug message") + logger.Info("Info message") + logger.Warn("Warn message") + logger.Error("Error message") + // logger.Fatal("Fatal message") // 会退出程序 + // logger.Panic("Panic message") // 会触发 panic +} diff --git a/Web开发/05go-zap-demo/02zap_production.go b/Web开发/05go-zap-demo/02zap_production.go new file mode 100644 index 0000000..6b44c99 --- /dev/null +++ b/Web开发/05go-zap-demo/02zap_production.go @@ -0,0 +1,22 @@ +package main + +import ( + "go.uber.org/zap" +) + +func main() { + // 创建生产模式 Logger(JSON 格式,性能更高) + logger, _ := zap.NewProduction() + defer logger.Sync() + + logger.Info("Application started", + zap.String("version", "1.0.0"), + zap.Int("port", 8080), + ) + + logger.Error("Failed to process request", + zap.String("method", "GET"), + zap.String("path", "/api/users"), + zap.Int("status_code", 500), + ) +} diff --git a/Web开发/05go-zap-demo/03zap_custom.go b/Web开发/05go-zap-demo/03zap_custom.go new file mode 100644 index 0000000..4b266ef --- /dev/null +++ b/Web开发/05go-zap-demo/03zap_custom.go @@ -0,0 +1,51 @@ +package main + +import ( + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +func main() { + // 1. 自定义配置 + config := zap.Config{ + Level: zap.NewAtomicLevelAt(zap.InfoLevel), // 日志级别 + Development: false, // 生产模式 + Encoding: "json", // 输出格式:json/console + OutputPaths: []string{"stdout", "app.log"}, // 输出到控制台和文件 + ErrorOutputPaths: []string{"stderr"}, + EncoderConfig: zapcore.EncoderConfig{ + TimeKey: "time", + LevelKey: "level", + NameKey: "logger", + CallerKey: "caller", + MessageKey: "msg", + StacktraceKey: "stacktrace", + LineEnding: zapcore.DefaultLineEnding, + EncodeLevel: zapcore.LowercaseLevelEncoder, // 小写级别名 + EncodeTime: zapcore.ISO8601TimeEncoder, // ISO8601 时间格式 + EncodeDuration: zapcore.SecondsDurationEncoder, + EncodeCaller: zapcore.ShortCallerEncoder, // 短路径 + }, + } + + // 2. 创建 Logger + logger, _ := config.Build() + defer logger.Sync() + + // 3. 使用 Logger + logger.Info("Custom logger initialized", + zap.String("env", "production"), + zap.Int("workers", 10), + ) + + logger.Warn("Low disk space", + zap.Int64("available_mb", 500), + zap.Int64("threshold_mb", 1000), + ) + + logger.Error("Database connection failed", + zap.String("host", "localhost"), + zap.Int("port", 3306), + zap.Error(nil), + ) +} diff --git a/Web开发/05go-zap-demo/04zap_rotate.go b/Web开发/05go-zap-demo/04zap_rotate.go new file mode 100644 index 0000000..81f3b11 --- /dev/null +++ b/Web开发/05go-zap-demo/04zap_rotate.go @@ -0,0 +1,48 @@ +package main + +import ( + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "gopkg.in/natefinch/lumberjack.v2" +) + +func main() { + // 1. 配置日志轮转 + logRotate := &lumberjack.Logger{ + Filename: "./logs/app.log", // 日志文件路径 + MaxSize: 10, // 每个日志文件最大 10MB + MaxBackups: 5, // 保留最近 5 个备份 + MaxAge: 30, // 保留 30 天 + Compress: true, // 压缩旧日志 + } + + // 2. 创建 WriteSyncer + writeSyncer := zapcore.AddSync(logRotate) + + // 3. 配置 Encoder + encoderConfig := zap.NewProductionEncoderConfig() + encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder + encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder + + // 4. 创建 Core + core := zapcore.NewCore( + zapcore.NewJSONEncoder(encoderConfig), + writeSyncer, + zap.InfoLevel, + ) + + // 5. 创建 Logger + logger := zap.New(core, zap.AddCaller()) + defer logger.Sync() + + // 6. 使用 Logger + for i := 0; i < 100; i++ { + logger.Info("Processing request", + zap.Int("request_id", i), + zap.String("method", "GET"), + zap.String("path", "/api/users"), + ) + } + + logger.Info("Log rotation configured successfully") +} diff --git a/Web开发/05go-zap-demo/05zap_multi_output.go b/Web开发/05go-zap-demo/05zap_multi_output.go new file mode 100644 index 0000000..db96189 --- /dev/null +++ b/Web开发/05go-zap-demo/05zap_multi_output.go @@ -0,0 +1,39 @@ +package main + +import ( + "os" + + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +func main() { + // 1. 配置 Encoder + encoderConfig := zap.NewProductionEncoderConfig() + encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder + encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder // 带颜色的级别 + + // 2. 控制台输出(console 格式) + consoleEncoder := zapcore.NewConsoleEncoder(encoderConfig) + consoleOutput := zapcore.AddSync(os.Stdout) + + // 3. 文件输出(json 格式) + fileEncoder := zapcore.NewJSONEncoder(encoderConfig) + file, _ := os.OpenFile("app.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) + fileOutput := zapcore.AddSync(file) + + // 4. 创建多输出 Core + core := zapcore.NewTee( + zapcore.NewCore(consoleEncoder, consoleOutput, zap.InfoLevel), + zapcore.NewCore(fileEncoder, fileOutput, zap.InfoLevel), + ) + + // 5. 创建 Logger + logger := zap.New(core, zap.AddCaller()) + defer logger.Sync() + + // 6. 使用 Logger + logger.Info("Application started") + logger.Warn("This is a warning") + logger.Error("This is an error") +} diff --git a/Web开发/05go-zap-demo/06zap_sugar.go b/Web开发/05go-zap-demo/06zap_sugar.go new file mode 100644 index 0000000..8f54f63 --- /dev/null +++ b/Web开发/05go-zap-demo/06zap_sugar.go @@ -0,0 +1,29 @@ +package main + +import ( + "go.uber.org/zap" +) + +func main() { + logger, _ := zap.NewProduction() + defer logger.Sync() + + // 1. 获取 Sugar Logger + sugar := logger.Sugar() + + // 2. 使用格式化字符串(类似 fmt.Printf) + sugar.Infof("User %s logged in with ID %d", "alice", 123) + sugar.Warnf("Low disk space: %d MB available", 500) + sugar.Errorf("Failed to connect to %s:%d", "localhost", 3306) + + // 3. 使用键值对 + sugar.Infow("User logged in", + "username", "alice", + "user_id", 123, + "is_admin", true, + ) + + // 4. 简洁日志 + sugar.Info("Simple info message") + sugar.Warn("Simple warn message") +} diff --git a/Web开发/05go-zap-demo/07zap_dynamic_level.go b/Web开发/05go-zap-demo/07zap_dynamic_level.go new file mode 100644 index 0000000..1248be5 --- /dev/null +++ b/Web开发/05go-zap-demo/07zap_dynamic_level.go @@ -0,0 +1,45 @@ +package main + +import ( + "time" + + "go.uber.org/zap" +) + +func main() { + // 1. 创建可调整的日志级别 + atom := zap.NewAtomicLevelAt(zap.InfoLevel) + + // 2. 配置 Logger + config := zap.Config{ + Level: atom, + Development: false, + Encoding: "json", + OutputPaths: []string{"stdout"}, + ErrorOutputPaths: []string{"stderr"}, + EncoderConfig: zap.NewProductionEncoderConfig(), + } + + logger, _ := config.Build() + defer logger.Sync() + + // 3. 初始日志级别为 Info + logger.Debug("Debug message - 1") // 不会输出 + logger.Info("Info message - 1") // 会输出 + + // 4. 动态调整为 Debug 级别 + time.Sleep(1 * time.Second) + atom.SetLevel(zap.DebugLevel) + logger.Info("Log level changed to Debug") + + logger.Debug("Debug message - 2") // 现在会输出 + logger.Info("Info message - 2") // 会输出 + + // 5. 动态调整为 Warn 级别 + time.Sleep(1 * time.Second) + atom.SetLevel(zap.WarnLevel) + logger.Info("Log level changed to Warn") + + logger.Info("Info message - 3") // 不会输出 + logger.Warn("Warn message - 3") // 会输出 +} diff --git a/Web开发/05go-zap-demo/08zap_global.go b/Web开发/05go-zap-demo/08zap_global.go new file mode 100644 index 0000000..b6c3f3b --- /dev/null +++ b/Web开发/05go-zap-demo/08zap_global.go @@ -0,0 +1,32 @@ +package main + +import ( + "go.uber.org/zap" +) + +func main() { + // 1. 创建 Logger + logger, _ := zap.NewProduction() + defer logger.Sync() + + // 2. 设置为全局 Logger + zap.ReplaceGlobals(logger) + + // 3. 在任何地方使用全局 Logger + doSomething() + doAnotherThing() +} + +func doSomething() { + // 直接使用全局 Logger + zap.L().Info("Doing something", + zap.String("function", "doSomething"), + ) +} + +func doAnotherThing() { + // 使用全局 Sugar Logger + zap.S().Infow("Doing another thing", + "function", "doAnotherThing", + ) +} diff --git a/Web开发/05go-zap-demo/09zap_performance.go b/Web开发/05go-zap-demo/09zap_performance.go new file mode 100644 index 0000000..735f587 --- /dev/null +++ b/Web开发/05go-zap-demo/09zap_performance.go @@ -0,0 +1,34 @@ +package main + +import ( + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +func main() { + // 1. 使用 Sampling 采样(减少高频日志) + config := zap.NewProductionConfig() + config.Sampling = &zap.SamplingConfig{ + Initial: 100, // 前 100 条日志全部记录 + Thereafter: 100, // 之后每 100 条记录 1 条 + } + + logger, _ := config.Build() + defer logger.Sync() + + // 2. 预分配字段(避免重复创建) + baseFields := []zapcore.Field{ + zap.String("service", "user-service"), + zap.String("version", "1.0.0"), + } + + // 3. 使用 With 创建子 Logger + userLogger := logger.With(baseFields...) + + // 4. 高效记录日志 + for i := 0; i < 1000; i++ { + userLogger.Info("Processing request", + zap.Int("request_id", i), + ) + } +} diff --git a/Web开发/05go-zap-demo/app.log b/Web开发/05go-zap-demo/app.log new file mode 100644 index 0000000..6b26b72 --- /dev/null +++ b/Web开发/05go-zap-demo/app.log @@ -0,0 +1,6 @@ +{"level":"info","time":"2025-12-30T10:08:51.909+0800","caller":"05go-zap-demo/03zap_custom.go:36","msg":"Custom logger initialized","env":"production","workers":10} +{"level":"warn","time":"2025-12-30T10:08:51.909+0800","caller":"05go-zap-demo/03zap_custom.go:41","msg":"Low disk space","available_mb":500,"threshold_mb":1000} +{"level":"error","time":"2025-12-30T10:08:51.909+0800","caller":"05go-zap-demo/03zap_custom.go:46","msg":"Database connection failed","host":"localhost","port":3306,"stacktrace":"main.main\n\t/home/liumangmang/GolandProjects/learn-golang/Web开发/05go-zap-demo/03zap_custom.go:46\nruntime.main\n\t/usr/lib/go-1.22/src/runtime/proc.go:271"} +{"level":"\u001b[34mINFO\u001b[0m","ts":"2025-12-30T10:12:45.344+0800","caller":"05go-zap-demo/05zap_multi_output.go:35","msg":"Application started"} +{"level":"\u001b[33mWARN\u001b[0m","ts":"2025-12-30T10:12:45.344+0800","caller":"05go-zap-demo/05zap_multi_output.go:36","msg":"This is a warning"} +{"level":"\u001b[31mERROR\u001b[0m","ts":"2025-12-30T10:12:45.344+0800","caller":"05go-zap-demo/05zap_multi_output.go:37","msg":"This is an error"} diff --git a/Web开发/05go-zap-demo/go.mod b/Web开发/05go-zap-demo/go.mod new file mode 100644 index 0000000..dbc458e --- /dev/null +++ b/Web开发/05go-zap-demo/go.mod @@ -0,0 +1,9 @@ +module go-zap-demo + +go 1.22.2 + +require ( + go.uber.org/multierr v1.10.0 // indirect + go.uber.org/zap v1.26.0 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect +) diff --git a/Web开发/05go-zap-demo/go.sum b/Web开发/05go-zap-demo/go.sum new file mode 100644 index 0000000..d2eae48 --- /dev/null +++ b/Web开发/05go-zap-demo/go.sum @@ -0,0 +1,6 @@ +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= diff --git a/Web开发/05go-zap-demo/logs/app.log b/Web开发/05go-zap-demo/logs/app.log new file mode 100644 index 0000000..c4559a6 --- /dev/null +++ b/Web开发/05go-zap-demo/logs/app.log @@ -0,0 +1,101 @@ +{"level":"INFO","ts":"2025-12-30T10:11:01.060+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":0,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":1,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":2,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":3,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":4,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":5,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":6,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":7,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":8,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":9,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":10,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":11,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":12,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":13,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":14,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":15,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":16,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":17,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":18,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":19,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":20,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":21,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":22,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":23,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":24,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":25,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":26,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":27,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":28,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":29,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":30,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":31,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":32,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":33,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":34,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":35,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":36,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":37,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":38,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":39,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":40,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":41,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":42,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":43,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":44,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":45,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":46,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":47,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":48,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":49,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":50,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":51,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":52,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":53,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":54,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":55,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":56,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":57,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":58,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":59,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":60,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":61,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":62,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":63,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":64,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":65,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":66,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":67,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":68,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":69,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":70,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":71,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":72,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":73,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":74,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":75,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":76,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":77,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":78,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":79,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":80,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":81,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":82,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":83,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":84,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":85,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":86,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":87,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":88,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":89,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":90,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":91,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":92,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":93,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":94,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":95,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":96,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":97,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":98,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:40","msg":"Processing request","request_id":99,"method":"GET","path":"/api/users"} +{"level":"INFO","ts":"2025-12-30T10:11:01.061+0800","caller":"05go-zap-demo/04zap_rotate.go:47","msg":"Log rotation configured successfully"} diff --git a/Web开发/06go-auth-api/.idea/.gitignore b/Web开发/06go-auth-api/.idea/.gitignore new file mode 100644 index 0000000..35410ca --- /dev/null +++ b/Web开发/06go-auth-api/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/Web开发/06go-auth-api/.idea/06go-auth-api.iml b/Web开发/06go-auth-api/.idea/06go-auth-api.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/Web开发/06go-auth-api/.idea/06go-auth-api.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/Web开发/06go-auth-api/.idea/dataSources.xml b/Web开发/06go-auth-api/.idea/dataSources.xml new file mode 100644 index 0000000..61a06b0 --- /dev/null +++ b/Web开发/06go-auth-api/.idea/dataSources.xml @@ -0,0 +1,20 @@ + + + + + sqlite.xerial + true + org.sqlite.JDBC + jdbc:sqlite:$PROJECT_DIR$/auth.db + $ProjectFileDir$ + + + file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/xerial/sqlite-jdbc/3.45.1.0/sqlite-jdbc-3.45.1.0.jar + + + file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar + + + + + \ No newline at end of file diff --git a/Web开发/06go-auth-api/.idea/data_source_mapping.xml b/Web开发/06go-auth-api/.idea/data_source_mapping.xml new file mode 100644 index 0000000..013ec61 --- /dev/null +++ b/Web开发/06go-auth-api/.idea/data_source_mapping.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Web开发/06go-auth-api/.idea/modules.xml b/Web开发/06go-auth-api/.idea/modules.xml new file mode 100644 index 0000000..b1dbaf2 --- /dev/null +++ b/Web开发/06go-auth-api/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/Web开发/06go-auth-api/.idea/vcs.xml b/Web开发/06go-auth-api/.idea/vcs.xml new file mode 100644 index 0000000..b2bdec2 --- /dev/null +++ b/Web开发/06go-auth-api/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Web开发/06go-auth-api/21综合实战项目.md b/Web开发/06go-auth-api/21综合实战项目.md new file mode 100644 index 0000000..831bd76 --- /dev/null +++ b/Web开发/06go-auth-api/21综合实战项目.md @@ -0,0 +1,826 @@ +--- +title: 综合实战项目 +icon: mdi:shield-account +date: 2025-12-23 +category: + - Go + - 后端 + - 工程化 + - 实战项目 +tag: + - API + - 用户认证 + - 密码加密 + - JWT + - 综合实战 +--- + +整合 Gin、GORM、Viper、Zap 等框架,开发一个完整的用户注册/登录 API。这个项目包含用户认证、密码加密、JWT 令牌和数据库操作,是学习 Go Web 开发的完美案例。 + + + +--- + +# Go 综合实战:用户注册/登录 API 完整指南 + +这是一个完整的 Web 应用示例,整合了前面学到的所有知识:Gin、GORM、Viper、Zap、中间件等。 + +--- + +## 一、项目结构 + +``` +go-auth-api/ +├── config/ +│ ├── app.yaml +│ ├── app.dev.yaml +│ └── app.prod.yaml +├── logs/ +│ └── app.log +├── main.go +├── config.go +├── logger.go +├── db.go +├── models.go +├── handlers.go +├── middleware.go +├── jwt.go +├── utils.go +└── go.mod +``` + +--- + +## 二、项目初始化 + +### 2.1 创建项目 + +```bash +cd ~/GolandProjects +mkdir go-auth-api && cd go-auth-api +go mod init go-auth-api + +# 安装依赖 +go get -u github.com/gin-gonic/gin +go get -u gorm.io/gorm +go get -u gorm.io/driver/sqlite +go get -u github.com/spf13/viper +go get -u go.uber.org/zap +go get -u github.com/golang-jwt/jwt/v4 +go get -u golang.org/x/crypto +go get -u gopkg.in/natefinch/lumberjack.v2 +``` + +> **版本说明**:代码兼容 Go 1.18+ 版本(建议 1.22+ 体验最佳性能) + +### 2.2 创建目录 + +```bash +mkdir -p config logs +``` + +--- + +## 三、配置文件 + +### 3.1 config/app.yaml + +```yaml +app: + name: AuthAPI + version: 1.0.0 + port: 8080 + env: dev + +database: + driver: sqlite + path: auth.db + +jwt: + secret: your-secret-key-change-in-production + expire: 86400 # 24 小时 + +password: + bcrypt_cost: 10 + +logging: + level: info + format: json +``` + +### 3.2 config/app.prod.yaml + +```yaml +app: + port: 80 + env: prod + +logging: + level: warn +``` + +--- + +## 四、模型定义(models.go) + +```go +package main + +import ( + "time" + "gorm.io/gorm" +) + +type User struct { + ID uint `gorm:"primaryKey" json:"id"` + Name string `gorm:"size:100;not null" json:"name"` + Email string `gorm:"size:100;unique;not null" json:"email"` + Password string `gorm:"size:255;not null" json:"-"` // 不在 JSON 中显示 + Phone string `gorm:"size:20" json:"phone,omitempty"` + Age int `json:"age,omitempty"` + Active bool `gorm:"default:true" json:"active"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + DeletedAt gorm.DeletedAt `json:"-"` +} + +func (User) TableName() string { + return "users" +} + +// 请求体 +type RegisterRequest struct { + Name string `json:"name" binding:"required"` + Email string `json:"email" binding:"required,email"` + Password string `json:"password" binding:"required,min=6"` + Phone string `json:"phone" binding:"omitempty,len=11"` +} + +type LoginRequest struct { + Email string `json:"email" binding:"required,email"` + Password string `json:"password" binding:"required"` +} + +type LoginResponse struct { + Token string `json:"token"` + User User `json:"user"` +} +``` + +--- + +## 五、数据库初始化(db.go) + +```go +package main + +import ( + "fmt" + "gorm.io/driver/sqlite" + "gorm.io/gorm" +) + +var DB *gorm.DB + +func InitDB(cfg *Config) error { + var dsn string + + if cfg.Database.Driver == "sqlite" { + dsn = cfg.Database.Path + } + + var err error + DB, err = gorm.Open(sqlite.Open(dsn), &gorm.Config{}) + if err != nil { + return fmt.Errorf("failed to connect database: %w", err) + } + + // 自动迁移 + if err = DB.AutoMigrate(&User{}); err != nil { + return fmt.Errorf("failed to migrate database: %w", err) + } + + Logger.Info("Database initialized successfully") + return nil +} +``` + +--- + +## 六、JWT 和密码工具(jwt.go + utils.go) + +### 6.1 jwt.go + +```go +package main + +import ( + "fmt" + "time" + "github.com/golang-jwt/jwt/v4" +) + +type Claims struct { + UserID uint `json:"user_id"` + Email string `json:"email"` + jwt.RegisteredClaims +} + +func GenerateToken(userID uint, email string, secret string, expire int64) (string, error) { + claims := Claims{ + UserID: userID, + Email: email, + RegisteredClaims: jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(expire) * time.Second)), + IssuedAt: jwt.NewNumericDate(time.Now()), + }, + } + + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + tokenString, err := token.SignedString([]byte(secret)) + if err != nil { + return "", err + } + + return tokenString, nil +} + +func VerifyToken(tokenString string, secret string) (*Claims, error) { + claims := &Claims{} + + token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) + } + return []byte(secret), nil + }) + + if err != nil { + return nil, err + } + + if !token.Valid { + return nil, fmt.Errorf("invalid token") + } + + return claims, nil +} +``` + +### 6.2 utils.go + +```go +package main + +import ( + "golang.org/x/crypto/bcrypt" +) + +// 密码加密 +func HashPassword(password string) (string, error) { + hash, err := bcrypt.GenerateFromPassword([]byte(password), 10) + if err != nil { + return "", err + } + return string(hash), nil +} + +// 验证密码 +func VerifyPassword(hashedPassword, password string) bool { + err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password)) + return err == nil +} + +// 检查邮箱是否已注册 +func EmailExists(email string) bool { + var count int64 + DB.Model(&User{}).Where("email = ?", email).Count(&count) + return count > 0 +} +``` + +--- + +## 七、中间件(middleware.go) + +```go +package main + +import ( + "strings" + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +// 请求日志中间件 +func LoggingMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + Logger.Info("Request", + zap.String("method", c.Request.Method), + zap.String("path", c.Request.URL.Path), + zap.String("ip", c.ClientIP()), + ) + + c.Next() + + Logger.Info("Response", + zap.String("path", c.Request.URL.Path), + zap.Int("status", c.Writer.Status()), + ) + } +} + +// JWT 认证中间件 +func AuthMiddleware(cfg *Config) gin.HandlerFunc { + return func(c *gin.Context) { + authHeader := c.GetHeader("Authorization") + + if authHeader == "" { + c.JSON(401, gin.H{"error": "Missing authorization header"}) + c.Abort() + return + } + + parts := strings.SplitN(authHeader, " ", 2) + if len(parts) != 2 || parts[0] != "Bearer" { + c.JSON(401, gin.H{"error": "Invalid authorization header"}) + c.Abort() + return + } + + claims, err := VerifyToken(parts[1], cfg.JWT.Secret) + if err != nil { + Logger.Error("Token verification failed", zap.Error(err)) + c.JSON(401, gin.H{"error": "Invalid token"}) + c.Abort() + return + } + + // 将用户信息存储在上下文中 + c.Set("user_id", claims.UserID) + c.Set("email", claims.Email) + + c.Next() + } +} + +// CORS 中间件 +func CORSMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + c.Writer.Header().Set("Access-Control-Allow-Origin", "*") + c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") + c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") + + if c.Request.Method == "OPTIONS" { + c.AbortWithStatus(204) + return + } + + c.Next() + } +} +``` + +--- + +## 八、业务逻辑处理器(handlers.go) + +```go +package main + +import ( + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +// 注册用户 +func Register(cfg *Config) gin.HandlerFunc { + return func(c *gin.Context) { + var req RegisterRequest + + if err := c.ShouldBindJSON(&req); err != nil { + Logger.Error("Invalid registration request", zap.Error(err)) + c.JSON(400, gin.H{"error": err.Error()}) + return + } + + // 检查邮箱是否已存在 + if EmailExists(req.Email) { + c.JSON(400, gin.H{"error": "Email already registered"}) + return + } + + // 密码加密 + hashedPassword, err := HashPassword(req.Password) + if err != nil { + Logger.Error("Failed to hash password", zap.Error(err)) + c.JSON(500, gin.H{"error": "Internal server error"}) + return + } + + // 创建用户 + user := User{ + Name: req.Name, + Email: req.Email, + Password: hashedPassword, + Phone: req.Phone, + } + + if err := DB.Create(&user).Error; err != nil { + Logger.Error("Failed to create user", zap.Error(err)) + c.JSON(500, gin.H{"error": "Failed to register user"}) + return + } + + Logger.Info("User registered successfully", zap.String("email", user.Email)) + + c.JSON(201, gin.H{ + "message": "User registered successfully", + "user_id": user.ID, + }) + } +} + +// 用户登录 +func Login(cfg *Config) gin.HandlerFunc { + return func(c *gin.Context) { + var req LoginRequest + + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(400, gin.H{"error": err.Error()}) + return + } + + // 查找用户 + var user User + if err := DB.Where("email = ?", req.Email).First(&user).Error; err != nil { + Logger.Warn("User not found", zap.String("email", req.Email)) + c.JSON(401, gin.H{"error": "Invalid email or password"}) + return + } + + // 验证密码 + if !VerifyPassword(user.Password, req.Password) { + Logger.Warn("Invalid password", zap.String("email", req.Email)) + c.JSON(401, gin.H{"error": "Invalid email or password"}) + return + } + + // 生成 JWT token + token, err := GenerateToken(user.ID, user.Email, cfg.JWT.Secret, int64(cfg.JWT.Expire)) + if err != nil { + Logger.Error("Failed to generate token", zap.Error(err)) + c.JSON(500, gin.H{"error": "Failed to generate token"}) + return + } + + Logger.Info("User logged in successfully", zap.String("email", user.Email)) + + c.JSON(200, LoginResponse{ + Token: token, + User: User{ + ID: user.ID, + Name: user.Name, + Email: user.Email, + Phone: user.Phone, + Age: user.Age, + }, + }) + } +} + +// 获取用户信息 +func GetProfile(c *gin.Context) { + userID, _ := c.Get("user_id") + + var user User + if err := DB.First(&user, userID.(uint)).Error; err != nil { + c.JSON(404, gin.H{"error": "User not found"}) + return + } + + c.JSON(200, user) +} + +// 更新用户信息 +func UpdateProfile(c *gin.Context) { + userID, _ := c.Get("user_id") + + var req struct { + Name string `json:"name"` + Phone string `json:"phone"` + Age int `json:"age"` + } + + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(400, gin.H{"error": err.Error()}) + return + } + + if err := DB.Model(&User{}).Where("id = ?", userID.(uint)).Updates(req).Error; err != nil { + Logger.Error("Failed to update user", zap.Error(err)) + c.JSON(500, gin.H{"error": "Failed to update profile"}) + return + } + + Logger.Info("User profile updated", zap.Uint("user_id", userID.(uint))) + c.JSON(200, gin.H{"message": "Profile updated successfully"}) +} + +// 健康检查 +func HealthCheck(c *gin.Context) { + c.JSON(200, gin.H{ + "status": "ok", + "app": "AuthAPI", + }) +} +``` + +--- + +## 九、配置加载(config.go) + +```go +package main + +import ( + "fmt" + "os" + "github.com/spf13/viper" +) + +type Config struct { + App struct { + Name string `mapstructure:"name"` + Version string `mapstructure:"version"` + Port int `mapstructure:"port"` + Env string `mapstructure:"env"` + } `mapstructure:"app"` + + Database struct { + Driver string `mapstructure:"driver"` + Path string `mapstructure:"path"` + } `mapstructure:"database"` + + JWT struct { + Secret string `mapstructure:"secret"` + Expire int `mapstructure:"expire"` + } `mapstructure:"jwt"` + + Logging struct { + Level string `mapstructure:"level"` + Format string `mapstructure:"format"` + } `mapstructure:"logging"` +} + +var GlobalConfig *Config + +func LoadConfig() (*Config, error) { + env := os.Getenv("GO_ENV") + if env == "" { + env = "dev" + } + + viper.SetConfigName("app") + viper.SetConfigType("yaml") + viper.AddConfigPath("./config") + + if err := viper.ReadInConfig(); err != nil { + return nil, fmt.Errorf("failed to read config: %w", err) + } + + viper.SetConfigName("app." + env) + viper.MergeInConfig() + + var cfg Config + if err := viper.Unmarshal(&cfg); err != nil { + return nil, fmt.Errorf("failed to unmarshal config: %w", err) + } + + GlobalConfig = &cfg + return &cfg, nil +} +``` + +--- + +## 十、日志初始化(logger.go) + +```go +package main + +import ( + "os" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "gopkg.in/natefinch/lumberjack.v2" +) + +var Logger *zap.Logger + +func InitLogger(env string) error { + var level zapcore.Level + + switch env { + case "prod": + level = zapcore.WarnLevel + case "test": + level = zapcore.DebugLevel + default: + level = zapcore.InfoLevel + } + + logFile := &lumberjack.Logger{ + Filename: "logs/app.log", + MaxSize: 100, + MaxBackups: 10, + MaxAge: 7, + Compress: true, + } + + encoderConfig := zapcore.EncoderConfig{ + TimeKey: "ts", + LevelKey: "level", + MessageKey: "msg", + CallerKey: "caller", + EncodeLevel: zapcore.LowercaseLevelEncoder, + EncodeTime: zapcore.ISO8601TimeEncoder, + EncodeCaller: zapcore.ShortCallerEncoder, + } + + core := zapcore.NewCore( + zapcore.NewJSONEncoder(encoderConfig), + zapcore.NewMultiWriteSyncer( + zapcore.AddSync(os.Stdout), + zapcore.AddSync(logFile), + ), + level, + ) + + Logger = zap.New(core, zap.AddCaller()) + zap.ReplaceGlobals(Logger) + + return nil +} +``` + +--- + +## 十一、主程序(main.go) + +```go +package main + +import ( + "fmt" + "github.com/gin-gonic/gin" +) + +func main() { + // 加载配置 + cfg, err := LoadConfig() + if err != nil { + panic(err) + } + + // 初始化日志 + if err = InitLogger(cfg.App.Env); err != nil { + panic(err) + } + defer Logger.Sync() + + // 初始化数据库 + if err = InitDB(cfg); err != nil { + Logger.Fatal("Failed to initialize database", zap.Error(err)) + } + + // 创建 Gin 应用 + r := gin.Default() + + // 应用中间件 + r.Use(LoggingMiddleware()) + r.Use(CORSMiddleware()) + + // 公开路由 + public := r.Group("/api") + { + public.GET("/health", HealthCheck) + public.POST("/register", Register(cfg)) + public.POST("/login", Login(cfg)) + } + + // 受保护的路由 + protected := r.Group("/api") + protected.Use(AuthMiddleware(cfg)) + { + protected.GET("/profile", GetProfile) + protected.PUT("/profile", UpdateProfile) + } + + // 启动服务器 + addr := fmt.Sprintf(":%d", cfg.App.Port) + Logger.Info("Server starting", + zap.String("app", cfg.App.Name), + zap.Int("port", cfg.App.Port), + zap.String("env", cfg.App.Env), + ) + + if err = r.Run(addr); err != nil { + Logger.Fatal("Server error", zap.Error(err)) + } +} +``` + +--- + +## 十二、API 使用示例 + +### 12.1 注册用户 + +```bash +curl -X POST http://localhost:8080/api/register \ + -H "Content-Type: application/json" \ + -d '{ + "name": "Alice", + "email": "alice@example.com", + "password": "password123", + "phone": "13800138000" + }' + +# 响应 +{ + "message": "User registered successfully", + "user_id": 1 +} +``` + +### 12.2 用户登录 + +```bash +curl -X POST http://localhost:8080/api/login \ + -H "Content-Type: application/json" \ + -d '{ + "email": "alice@example.com", + "password": "password123" + }' + +# 响应 +{ + "token": "eyJhbGciOiJIUzI1NiIs...", + "user": { + "id": 1, + "name": "Alice", + "email": "alice@example.com", + "phone": "13800138000" + } +} +``` + +### 12.3 获取用户信息(需要认证) + +```bash +curl -X GET http://localhost:8080/api/profile \ + -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." + +# 响应 +{ + "id": 1, + "name": "Alice", + "email": "alice@example.com", + "phone": "13800138000", + "age": 0, + "active": true, + "created_at": "2025-12-23T14:30:45Z" +} +``` + +--- + +## 十三、项目验收清单 + +- ✅ 用户注册(密码加密、邮箱验证) +- ✅ 用户登录(JWT 令牌生成) +- ✅ 用户认证(JWT 验证中间件) +- ✅ 用户信息管理(获取、更新) +- ✅ 配置管理(多环境) +- ✅ 日志记录(结构化、日志轮转) +- ✅ 错误处理(友好的错误消息) +- ✅ 代码组织(清晰的文件结构) + +--- + +## 十四、进阶扩展方向 + +1. **添加邮箱验证** - 发送验证码验证邮箱 +2. **实现刷新令牌** - 增加安全性 +3. **添加速率限制** - 防止暴力破解 +4. **用户权限管理** - 基于角色的访问控制 +5. **社交登录** - 集成 OAuth2(GitHub、Google) +6. **单元测试** - 为关键业务逻辑编写测试 +7. **Docker 打包** - 容器化部署 +8. **CI/CD 流程** - 自动化测试和部署 + +--- + +祝你编码愉快!🚀 这个项目整合了 Go Web 开发的所有核心知识,是学习和面试的好材料。 + diff --git a/Web开发/06go-auth-api/auth.db b/Web开发/06go-auth-api/auth.db new file mode 100644 index 0000000000000000000000000000000000000000..b4af3e6599bd599e2b57bb6fcb71fee31bae695f GIT binary patch literal 16384 zcmeI((NEey90%|#IH${$JuK<#CG$ZdLR;2pOqSSbP0(5e$`+pJQBG-63Y07Aqsw0W z0R&l>)n<6q%Y9WtUICL^kSF{n4neijwlM*r<4$qlXXef z$D+v2V*GgRf9D)2Pw&Mtm}k~UL@RVlbc9w-oi00bZa0SG_< z0uX=z1Rwwb2s}&RwxYZxYUTFThn3Z4lUPw0a5ii9`86AiB3=%LgAIdi=-S3f*BM_1 z-=_T=FE~Ex_ov-Jze&gC>Q_As>q0k1PrSF=9? F`~kbd;j;h$ literal 0 HcmV?d00001 diff --git a/Web开发/06go-auth-api/config.go b/Web开发/06go-auth-api/config.go new file mode 100644 index 0000000..6df2460 --- /dev/null +++ b/Web开发/06go-auth-api/config.go @@ -0,0 +1,60 @@ +package main + +import ( + "fmt" + "os" + + "github.com/spf13/viper" +) + +type Config struct { + App struct { + Name string `mapstructure:"name"` + Version string `mapstructure:"version"` + Port int `mapstructure:"port"` + Env string `mapstructure:"env"` + } `mapstructure:"app"` + + Database struct { + Driver string `mapstructure:"driver"` + Path string `mapstructure:"path"` + } `mapstructure:"database"` + + JWT struct { + Secret string `mapstructure:"secret"` + Expire int `mapstructure:"expire"` + } `mapstructure:"jwt"` + + Logging struct { + Level string `mapstructure:"level"` + Format string `mapstructure:"format"` + } `mapstructure:"logging"` +} + +var GlobalConfig *Config + +func LoadConfig() (*Config, error) { + env := os.Getenv("APP_ENV") + if env == "" { + env = "dev" + } + + viper.SetConfigName("app") + viper.AddConfigPath("./config") + viper.SetConfigType("yaml") + + err := viper.ReadInConfig() + if err != nil { + return nil, fmt.Errorf("读取配置文件失败: %w", err) + } + + viper.SetConfigName("app." + env) + err = viper.MergeInConfig() + var cfg Config + err = viper.Unmarshal(&cfg) + if err != nil { + return nil, fmt.Errorf("解析配置文件失败: %w", err) + } + GlobalConfig = &cfg + return &cfg, nil +} diff --git a/Web开发/06go-auth-api/config/app.prod.yaml b/Web开发/06go-auth-api/config/app.prod.yaml new file mode 100644 index 0000000..b09512f --- /dev/null +++ b/Web开发/06go-auth-api/config/app.prod.yaml @@ -0,0 +1,6 @@ +app: + port: 80 + env: prod + +logging: + level: warn diff --git a/Web开发/06go-auth-api/config/app.yaml b/Web开发/06go-auth-api/config/app.yaml new file mode 100644 index 0000000..985cdec --- /dev/null +++ b/Web开发/06go-auth-api/config/app.yaml @@ -0,0 +1,20 @@ +app: + name: AuthAPI + version: 1.0.0 + port: 8088 + env: dev + +database: + driver: sqlite + path: auth.db + +jwt: + secret: your-secret-key-change-in-production + expire: 86400 # 24 小时 + +password: + bcrypt_cost: 10 + +logging: + level: info + format: json diff --git a/Web开发/06go-auth-api/db.go b/Web开发/06go-auth-api/db.go new file mode 100644 index 0000000..f76acbb --- /dev/null +++ b/Web开发/06go-auth-api/db.go @@ -0,0 +1,32 @@ +package main + +import ( + "fmt" + + "gorm.io/driver/sqlite" + "gorm.io/gorm" +) + +var DB *gorm.DB + +func InitDB(cfg *Config) error { + var dsn string + if cfg.Database.Driver == "sqlite" { + dsn = cfg.Database.Path + } + + var err error + DB, err = gorm.Open(sqlite.Open(dsn), &gorm.Config{}) + if err != nil { + return fmt.Errorf("连接数据库失败: %w", err) + } + + //自动迁移 + err = DB.AutoMigrate(&User{}) + if err != nil { + return fmt.Errorf("自动迁移数据库失败: %w", err) + } + + Logger.Info("数据库初始化成功") + return nil +} diff --git a/Web开发/06go-auth-api/go.mod b/Web开发/06go-auth-api/go.mod new file mode 100644 index 0000000..2d2860f --- /dev/null +++ b/Web开发/06go-auth-api/go.mod @@ -0,0 +1,62 @@ +module go-auth-api + +go 1.22.2 + +require ( + github.com/golang-jwt/jwt/v4 v4.5.2 + go.uber.org/zap v1.27.0 + gopkg.in/natefinch/lumberjack.v2 v2.2.1 + gorm.io/driver/sqlite v1.5.6 + gorm.io/gorm v1.25.7 +) + +require ( + github.com/bytedance/sonic v1.11.6 // indirect + github.com/bytedance/sonic/loader v0.1.1 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/gin-gonic/gin v1.10.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.20.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-sqlite3 v1.14.22 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.18.2 // indirect + github.com/stretchr/testify v1.9.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + go.uber.org/multierr v1.10.0 // indirect + golang.org/x/arch v0.8.0 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/Web开发/06go-auth-api/go.sum b/Web开发/06go-auth-api/go.sum new file mode 100644 index 0000000..a6ecea5 --- /dev/null +++ b/Web开发/06go-auth-api/go.sum @@ -0,0 +1,134 @@ +github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= +github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= +github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= +github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= +github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= +github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= +golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/sqlite v1.5.6 h1:fO/X46qn5NUEEOZtnjJRWRzZMe8nqJiQ9E+0hi+hKQE= +gorm.io/driver/sqlite v1.5.6/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4= +gorm.io/gorm v1.25.7 h1:VsD6acwRjz2zFxGO50gPO6AkNs7KKnvfzUjHQhZDz/A= +gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/Web开发/06go-auth-api/handlers.go b/Web开发/06go-auth-api/handlers.go new file mode 100644 index 0000000..eb196b5 --- /dev/null +++ b/Web开发/06go-auth-api/handlers.go @@ -0,0 +1,159 @@ +package main + +import ( + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +// 注册用户 +func Register(cfg *Config) gin.HandlerFunc { + return func(c *gin.Context) { + var req RegisterRequest + err := c.ShouldBindJSON(&req) + if err != nil { + Logger.Error("参数错误", zap.Error(err)) + c.JSON(400, gin.H{ + "message": "参数错误", + }) + return + } + + // 验证邮箱 + if EmailExists(req.Email) { + c.JSON(400, gin.H{ + "message": "邮箱已存在", + }) + return + } + + //密码加密 + hashedPassword, err := HashPassword(req.Password) + if err != nil { + Logger.Error("密码加密失败", zap.Error(err)) + c.JSON(500, gin.H{ + "message": "密码加密失败", + }) + return + } + + // 创建用户 + user := User{ + Name: req.Name, + Email: req.Email, + Password: hashedPassword, + Phone: req.Phone, + } + + err = DB.Create(&user).Error + if err != nil { + Logger.Error("创建用户失败", zap.Error(err)) + c.JSON(500, gin.H{ + "message": "创建用户失败", + }) + return + } + + Logger.Info("用户注册成功", zap.String("email", req.Email)) + + c.JSON(200, gin.H{ + "message": "用户注册成功", + }) + } +} + +// 登录 +func Login(cfg *Config) gin.HandlerFunc { + return func(c *gin.Context) { + var req LoginRequest + + err := c.ShouldBindJSON(&req) + if err != nil { + c.JSON(400, gin.H{"错误": err.Error()}) + return + } + + //查找用户 + var user User + err = DB.Where("email = ?", req.Email).First(&user).Error + if err != nil { + Logger.Warn("用户不存在", zap.String("email", req.Email)) + c.JSON(401, gin.H{"错误": "电子邮件或密码无效"}) + return + } + + //验证密码 + if !VerifyPassword(user.Password, req.Password) { + Logger.Warn("密码错误", zap.String("email", req.Email)) + c.JSON(401, gin.H{"错误": "电子邮件或密码无效"}) + return + } + + //生成token + token, err := GenerateToken(user.ID, user.Email, cfg.JWT.Secret, int64(cfg.JWT.Expire)) + + if err != nil { + Logger.Error("生成token失败", zap.Error(err)) + c.JSON(500, gin.H{"错误": "生成token失败"}) + return + } + + Logger.Info("用户登录成功", zap.String("email", req.Email)) + + c.JSON(200, LoginResponse{ + Token: token, + User: User{ + ID: user.ID, + Name: user.Name, + Email: user.Email, + Phone: user.Phone, + Age: user.Age, + }, + }) + } +} + +// 获取用户信息 +func GetProfile(c *gin.Context) { + userID, _ := c.Get("user_id") + + var user User + if err := DB.First(&user, userID.(uint)).Error; err != nil { + c.JSON(404, gin.H{"error": "User not found"}) + return + } + + c.JSON(200, user) +} + +// 更新用户信息 +func UpdateProfile(c *gin.Context) { + userID, _ := c.Get("user_id") + + var req struct { + Name string `json:"name"` + Phone string `json:"phone"` + Age int `json:"age"` + } + + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(400, gin.H{"error": err.Error()}) + return + } + + if err := DB.Model(&User{}).Where("id = ?", userID.(uint)).Updates(req).Error; err != nil { + Logger.Error("Failed to update user", zap.Error(err)) + c.JSON(500, gin.H{"error": "Failed to update profile"}) + return + } + + Logger.Info("User profile updated", zap.Uint("user_id", userID.(uint))) + c.JSON(200, gin.H{"message": "Profile updated successfully"}) +} + +// 健康检查 +func HealthCheck(c *gin.Context) { + c.JSON(200, gin.H{ + "status": "ok", + "app": "AuthAPI", + }) +} diff --git a/Web开发/06go-auth-api/jwt.go b/Web开发/06go-auth-api/jwt.go new file mode 100644 index 0000000..c1ffc7f --- /dev/null +++ b/Web开发/06go-auth-api/jwt.go @@ -0,0 +1,66 @@ +package main + +import ( + "fmt" + "time" + + "github.com/golang-jwt/jwt/v4" +) + +type Claims struct { + UserID uint `json:"user_id"` + Email string `json:"email"` + jwt.RegisteredClaims +} + +func GenerateToken(userID uint, email string, secret string, expire int64) (string, error) { + claims := Claims{ + UserID: userID, + Email: email, + RegisteredClaims: jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(expire) * time.Second)), + IssuedAt: jwt.NewNumericDate(time.Now()), + }, + } + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + tokenString, err := token.SignedString([]byte(secret)) + if err != nil { + return "", err + } + + return tokenString, nil +} + +// VerifyToken 验证JWT令牌的有效性 +// 参数: +// +// tokenString - JWT令牌字符串 +// secret - 用于验证令牌的密钥 +// +// 返回值: +// +// *Claims - 解析出的令牌声明信息 +// error - 验证过程中出现的错误 +func VerifyToken(tokenString string, secret string) (*Claims, error) { + claims := &Claims{} + token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) { + + // 检查签名方法是否为HMAC + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) + } + return []byte(secret), nil + }) + + if err != nil { + return nil, err + } + + // 验证令牌是否有效 + if !token.Valid { + return nil, fmt.Errorf("invalid token") + } + + return claims, nil + +} diff --git a/Web开发/06go-auth-api/logger.go b/Web开发/06go-auth-api/logger.go new file mode 100644 index 0000000..8882476 --- /dev/null +++ b/Web开发/06go-auth-api/logger.go @@ -0,0 +1,56 @@ +package main + +import ( + "os" + + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "gopkg.in/natefinch/lumberjack.v2" +) + +var Logger *zap.Logger + +func InitLogger(env string) error { + var level zapcore.Level + + switch env { + case "prod": + level = zapcore.WarnLevel + case "test": + level = zapcore.DebugLevel + default: + level = zapcore.InfoLevel + } + + logfile := &lumberjack.Logger{ + Filename: "logs/app.log", + MaxSize: 100, + MaxBackups: 10, + MaxAge: 30, + Compress: true, + } + + encoderConfig := zapcore.EncoderConfig{ + TimeKey: "ts", + LevelKey: "level", + MessageKey: "msg", + CallerKey: "caller", + EncodeLevel: zapcore.LowercaseLevelEncoder, + EncodeTime: zapcore.ISO8601TimeEncoder, + EncodeCaller: zapcore.ShortCallerEncoder, + } + + core := zapcore.NewCore( + zapcore.NewJSONEncoder(encoderConfig), + zapcore.NewMultiWriteSyncer( + zapcore.AddSync(os.Stdout), + zapcore.AddSync(logfile), + ), + level, + ) + + Logger = zap.New(core, zap.AddCaller()) + zap.ReplaceGlobals(Logger) + + return nil +} diff --git a/Web开发/06go-auth-api/logs/app.log b/Web开发/06go-auth-api/logs/app.log new file mode 100644 index 0000000..22ccfde --- /dev/null +++ b/Web开发/06go-auth-api/logs/app.log @@ -0,0 +1,24 @@ +{"level":"info","ts":"2025-12-30T17:50:54.962+0800","caller":"06go-auth-api/db.go:30","msg":"数据库初始化成功"} +{"level":"info","ts":"2025-12-30T17:50:54.962+0800","caller":"06go-auth-api/main.go:57","msg":"Server starting","app":"AuthAPI","port":8080,"env":"dev"} +{"level":"fatal","ts":"2025-12-30T17:50:54.963+0800","caller":"06go-auth-api/main.go:64","msg":"Server error","error":"listen tcp :8080: bind: address already in use"} +{"level":"info","ts":"2025-12-30T17:52:46.052+0800","caller":"06go-auth-api/db.go:30","msg":"数据库初始化成功"} +{"level":"info","ts":"2025-12-30T17:52:46.052+0800","caller":"06go-auth-api/main.go:57","msg":"Server starting","app":"AuthAPI","port":8088,"env":"dev"} +{"level":"info","ts":"2025-12-30T17:53:51.134+0800","caller":"06go-auth-api/middleware.go:17","msg":"请求","方法":"POST","路径":"/api/register","IP":"::1"} +{"level":"info","ts":"2025-12-30T17:53:51.210+0800","caller":"06go-auth-api/handlers.go:56","msg":"用户注册成功","email":"alice@example.com"} +{"level":"info","ts":"2025-12-30T17:53:51.210+0800","caller":"06go-auth-api/middleware.go:26","msg":"响应","路径":"/api/register","状态码":200} +{"level":"info","ts":"2025-12-30T17:54:17.482+0800","caller":"06go-auth-api/middleware.go:17","msg":"请求","方法":"POST","路径":"/api/login","IP":"::1"} +{"level":"info","ts":"2025-12-30T17:54:17.532+0800","caller":"06go-auth-api/handlers.go:100","msg":"用户登录成功","email":"alice@example.com"} +{"level":"info","ts":"2025-12-30T17:54:17.532+0800","caller":"06go-auth-api/middleware.go:26","msg":"响应","路径":"/api/login","状态码":200} +{"level":"info","ts":"2025-12-30T17:54:53.342+0800","caller":"06go-auth-api/middleware.go:17","msg":"请求","方法":"GET","路径":"/api/profile","IP":"::1"} +{"level":"info","ts":"2025-12-30T17:55:00.113+0800","caller":"06go-auth-api/middleware.go:17","msg":"请求","方法":"GET","路径":"/api/profile","IP":"::1"} +{"level":"info","ts":"2025-12-30T17:55:11.577+0800","caller":"06go-auth-api/middleware.go:17","msg":"请求","方法":"GET","路径":"/api/profile","IP":"::1"} +{"level":"info","ts":"2025-12-30T17:56:24.245+0800","caller":"06go-auth-api/middleware.go:17","msg":"请求","方法":"GET","路径":"/api/profile","IP":"::1"} +{"level":"info","ts":"2025-12-30T17:56:56.927+0800","caller":"06go-auth-api/db.go:30","msg":"数据库初始化成功"} +{"level":"info","ts":"2025-12-30T17:56:56.927+0800","caller":"06go-auth-api/main.go:57","msg":"Server starting","app":"AuthAPI","port":8088,"env":"dev"} +{"level":"info","ts":"2025-12-30T17:57:06.731+0800","caller":"06go-auth-api/middleware.go:17","msg":"请求","方法":"GET","路径":"/api/profile","IP":"::1"} +{"level":"info","ts":"2025-12-30T17:58:16.750+0800","caller":"06go-auth-api/db.go:30","msg":"数据库初始化成功"} +{"level":"info","ts":"2025-12-30T17:58:16.750+0800","caller":"06go-auth-api/main.go:57","msg":"Server starting","app":"AuthAPI","port":8088,"env":"dev"} +{"level":"info","ts":"2025-12-30T17:58:23.713+0800","caller":"06go-auth-api/middleware.go:17","msg":"请求","方法":"GET","路径":"/api/profile","IP":"::1"} +{"level":"info","ts":"2025-12-30T17:58:23.714+0800","caller":"06go-auth-api/middleware.go:26","msg":"响应","路径":"/api/profile","状态码":200} +{"level":"info","ts":"2025-12-30T17:58:57.676+0800","caller":"06go-auth-api/middleware.go:17","msg":"请求","方法":"GET","路径":"/api/profile","IP":"::1"} +{"level":"info","ts":"2025-12-30T17:58:57.676+0800","caller":"06go-auth-api/middleware.go:26","msg":"响应","路径":"/api/profile","状态码":401} diff --git a/Web开发/06go-auth-api/main.go b/Web开发/06go-auth-api/main.go new file mode 100644 index 0000000..ebaf1fb --- /dev/null +++ b/Web开发/06go-auth-api/main.go @@ -0,0 +1,66 @@ +package main + +import ( + "fmt" + + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +func main() { + + //加载配置 + cfg, err := LoadConfig() + if err != nil { + panic(err) + } + + //初始化日志 + err = InitLogger(cfg.App.Env) + if err != nil { + panic(err) + } + + defer Logger.Sync() + + //初始化数据库 + err = InitDB(cfg) + if err != nil { + Logger.Fatal("数据库初始化失败", zap.Error(err)) + } + + //创建Gin应用 + r := gin.Default() + + //中间件 + r.Use(LoggingMiddleware()) + r.Use(CORSMiddleware()) + + // 公开路由 + public := r.Group("/api") + { + public.GET("/health", HealthCheck) + public.POST("/register", Register(cfg)) + public.POST("/login", Login(cfg)) + } + + // 受保护的路由 + protected := r.Group("/api") + protected.Use(AuthMiddleware(cfg)) + { + protected.GET("/profile", GetProfile) + protected.PUT("/profile", UpdateProfile) + } + + // 启动服务器 + addr := fmt.Sprintf(":%d", cfg.App.Port) + Logger.Info("Server starting", + zap.String("app", cfg.App.Name), + zap.Int("port", cfg.App.Port), + zap.String("env", cfg.App.Env), + ) + + if err = r.Run(addr); err != nil { + Logger.Fatal("Server error", zap.Error(err)) + } +} diff --git a/Web开发/06go-auth-api/middleware.go b/Web开发/06go-auth-api/middleware.go new file mode 100644 index 0000000..27881b6 --- /dev/null +++ b/Web开发/06go-auth-api/middleware.go @@ -0,0 +1,94 @@ +package main + +import ( + "strings" + + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +// LoggingMiddleware 创建一个请求日志中间件 +// 该中间件会在每个HTTP请求进入时记录请求信息,在响应返回时记录响应信息 +// 参数: 无 +// 返回值: gin.HandlerFunc - Gin框架的中间件处理函数 +func LoggingMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + // 记录请求日志:方法、路径、客户端IP + Logger.Info("请求", + zap.String("方法", c.Request.Method), + zap.String("路径", c.Request.URL.Path), + zap.String("IP", c.ClientIP()), + ) + + c.Next() + + // 记录响应日志:路径、状态码 + Logger.Info("响应", + zap.String("路径", c.Request.URL.Path), + zap.Int("状态码", c.Writer.Status()), + ) + } +} + +// AuthMiddleware JWT 认证中间件 +// 参数: cfg - 配置对象,包含JWT密钥等配置信息 +// 返回值: gin.HandlerFunc - Gin框架的中间件处理函数 +func AuthMiddleware(cfg *Config) gin.HandlerFunc { + return func(c *gin.Context) { + authHeader := c.GetHeader("Authorization") + + if authHeader == "" { + c.JSON(401, gin.H{"错误": "错误的认证头"}) + c.Abort() + return + } + + // 解析Authorization头,验证格式为 "Bearer " + parts := strings.SplitN(authHeader, " ", 2) + if len(parts) != 2 || parts[0] != "Bearer" { + c.JSON(401, gin.H{"错误": "错误的认证头"}) + c.Abort() + return + } + + // 验证JWT token的有效性 + claims, err := VerifyToken(parts[1], cfg.JWT.Secret) + if err != nil { + Logger.Error("验证token失败", zap.Error(err)) + c.JSON(401, gin.H{"错误": "验证token失败"}) + c.Abort() + return + } + + // 将用户信息保存到上下文 + c.Set("user_id", claims.UserID) + c.Set("email", claims.Email) + c.Next() + } +} + +// CORSMiddleware 创建并返回一个CORS(跨域资源共享)中间件处理器 +// 该中间件用于处理跨域请求,设置相应的响应头信息 +// +// 参数: +// +// 无 +// +// 返回值: +// +// gin.HandlerFunc - Gin框架的中间件处理器函数 +func CORSMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + // 设置CORS相关响应头 + c.Writer.Header().Set("Access-Control-Allow-Origin", "*") + c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") + c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") + + // 处理预检请求(OPTIONS方法) + if c.Request.Method == "OPTIONS" { + c.AbortWithStatus(204) + return + } + c.Next() + } +} diff --git a/Web开发/06go-auth-api/models.go b/Web开发/06go-auth-api/models.go new file mode 100644 index 0000000..66e84b0 --- /dev/null +++ b/Web开发/06go-auth-api/models.go @@ -0,0 +1,42 @@ +package main + +import ( + "time" + + "gorm.io/gorm" +) + +type User struct { + ID uint `gorm:"primaryKey" json:"id"` + Name string `gorm:"size:100;not null" json:"name"` + Email string `gorm:"size:100;unique;not null" json:"email"` + Password string `gorm:"size:255;not null" json:"-"` // 不在 JSON 中显示 + Phone string `gorm:"size:20" json:"phone,omitempty"` + Age int `json:"age,omitempty"` + Active bool `gorm:"default:true" json:"active"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + DeletedAt gorm.DeletedAt `json:"-"` +} + +func (User) TableName() string { + return "users" +} + +// 请求体 +type RegisterRequest struct { + Name string `json:"name" binding:"required"` + Email string `json:"email" binding:"required,email"` + Password string `json:"password" binding:"required,min=6"` + Phone string `json:"phone" binding:"omitempty,len=11"` +} + +type LoginRequest struct { + Email string `json:"email" binding:"required,email"` + Password string `json:"password" binding:"required"` +} + +type LoginResponse struct { + Token string `json:"token"` + User User `json:"user"` +} diff --git a/Web开发/06go-auth-api/utils.go b/Web开发/06go-auth-api/utils.go new file mode 100644 index 0000000..3e3fb97 --- /dev/null +++ b/Web开发/06go-auth-api/utils.go @@ -0,0 +1,28 @@ +package main + +import ( + "golang.org/x/crypto/bcrypt" +) + +// 密码加密 +func HashPassword(password string) (string, error) { + hash, err := bcrypt.GenerateFromPassword([]byte(password), 10) + if err != nil { + return "", err + } + + return string(hash), nil +} + +// 密码验证 +func VerifyPassword(hashedPassword, password string) bool { + err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password)) + return err == nil +} + +// 检查邮箱是否注册 +func EmailExists(email string) bool { + var count int64 + DB.Model(&User{}).Where("email = ?", email).Count(&count) + return count > 0 +} diff --git a/go-gin-demo/params_form_demo.go b/go-gin-demo/params_form_demo.go deleted file mode 100644 index aea3c8c..0000000 --- a/go-gin-demo/params_form_demo.go +++ /dev/null @@ -1,27 +0,0 @@ -package main - -import ( - "github.com/gin-gonic/gin" -) - -func main() { - r := gin.Default() - - r.POST("/login", func(c *gin.Context) { - username := c.PostForm("username") // 获取表单参数 - password := c.PostForm("password") - - // 简单验证 - if username == "" || password == "" { - c.JSON(400, gin.H{"error": "username and password required"}) - return - } - - c.JSON(200, gin.H{ - "message": "Login successful", - "username": username, - }) - }) - - r.Run(":8080") -} diff --git a/go-gin-demo/params_path_demo.go b/go-gin-demo/params_path_demo.go deleted file mode 100644 index e2d58c2..0000000 --- a/go-gin-demo/params_path_demo.go +++ /dev/null @@ -1,24 +0,0 @@ -package main - -import ( - "github.com/gin-gonic/gin" -) - -func main() { - r := gin.Default() - - // 单个参数 - r.GET("/users/:id", func(c *gin.Context) { - id := c.Param("id") // 获取参数 - c.JSON(200, gin.H{"user_id": id}) - }) - - // 多个参数 - r.GET("/users/:uid/posts/:pid", func(c *gin.Context) { - uid := c.Param("uid") - pid := c.Param("pid") - c.JSON(200, gin.H{"uid": uid, "pid": pid}) - }) - - r.Run(":8080") -} diff --git a/go-gin-demo/params_query_demo.go b/go-gin-demo/params_query_demo.go deleted file mode 100644 index 1c0b64c..0000000 --- a/go-gin-demo/params_query_demo.go +++ /dev/null @@ -1,20 +0,0 @@ -package main - -import ( - "github.com/gin-gonic/gin" -) - -func main() { - r := gin.Default() - - r.GET("/search", func(c *gin.Context) { - keyword := c.Query("q") // 获取查询参数 - limit := c.DefaultQuery("limit", "10") // 带默认值 - c.JSON(200, gin.H{ - "keyword": keyword, - "limit": limit, - }) - }) - - r.Run(":8080") -} diff --git a/go-gorm-demo/db_connect.go b/go-gorm-demo/db_connect.go deleted file mode 100644 index c05a281..0000000 --- a/go-gorm-demo/db_connect.go +++ /dev/null @@ -1,29 +0,0 @@ -package main - -import ( - "fmt" - "gorm.io/driver/sqlite" - "gorm.io/gorm" -) - -func main() { - // 连接 SQLite(文件数据库) - db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{}) - if err != nil { - panic("failed to connect database") - } - - fmt.Println("Database connected successfully!") - - // 获取底层的 *sql.DB - sqlDB, err := db.DB() - if err != nil { - panic(err) - } - - // 设置连接池 - sqlDB.SetMaxIdleConns(10) - sqlDB.SetMaxOpenConns(100) - - fmt.Println("Connection pool configured") -} diff --git a/hello-go/go.mod b/go基础语法/01hello-go/go.mod similarity index 100% rename from hello-go/go.mod rename to go基础语法/01hello-go/go.mod diff --git a/hello-go/hello-go b/go基础语法/01hello-go/hello-go similarity index 100% rename from hello-go/hello-go rename to go基础语法/01hello-go/hello-go diff --git a/hello-go/main.go b/go基础语法/01hello-go/main.go similarity index 100% rename from hello-go/main.go rename to go基础语法/01hello-go/main.go diff --git a/go-basics/go.mod b/go基础语法/02go-basics/go.mod similarity index 100% rename from go-basics/go.mod rename to go基础语法/02go-basics/go.mod diff --git a/go-basics/main.go b/go基础语法/02go-basics/main.go similarity index 100% rename from go-basics/main.go rename to go基础语法/02go-basics/main.go diff --git a/go-slice-map/go.mod b/go基础语法/03go-slice-map/go.mod similarity index 100% rename from go-slice-map/go.mod rename to go基础语法/03go-slice-map/go.mod diff --git a/go-slice-map/main.go b/go基础语法/03go-slice-map/main.go similarity index 100% rename from go-slice-map/main.go rename to go基础语法/03go-slice-map/main.go diff --git a/go-struct-methods/go.mod b/go基础语法/04go-struct-methods/go.mod similarity index 100% rename from go-struct-methods/go.mod rename to go基础语法/04go-struct-methods/go.mod diff --git a/go-struct-methods/main.go b/go基础语法/04go-struct-methods/main.go similarity index 100% rename from go-struct-methods/main.go rename to go基础语法/04go-struct-methods/main.go diff --git a/go-interfaces/go.mod b/go基础语法/05go-interfaces/go.mod similarity index 100% rename from go-interfaces/go.mod rename to go基础语法/05go-interfaces/go.mod diff --git a/go-interfaces/main.go b/go基础语法/05go-interfaces/main.go similarity index 100% rename from go-interfaces/main.go rename to go基础语法/05go-interfaces/main.go diff --git a/go-error-handling/go.mod b/go基础语法/06go-error-handling/go.mod similarity index 100% rename from go-error-handling/go.mod rename to go基础语法/06go-error-handling/go.mod diff --git a/go-error-handling/main.go b/go基础语法/06go-error-handling/main.go similarity index 100% rename from go-error-handling/main.go rename to go基础语法/06go-error-handling/main.go diff --git a/go-mini-logger/app.log b/go基础语法/07go-mini-logger/app.log similarity index 100% rename from go-mini-logger/app.log rename to go基础语法/07go-mini-logger/app.log diff --git a/go-mini-logger/go.mod b/go基础语法/07go-mini-logger/go.mod similarity index 100% rename from go-mini-logger/go.mod rename to go基础语法/07go-mini-logger/go.mod diff --git a/go-mini-logger/logger.go b/go基础语法/07go-mini-logger/logger.go similarity index 100% rename from go-mini-logger/logger.go rename to go基础语法/07go-mini-logger/logger.go diff --git a/go-mini-logger/main.go b/go基础语法/07go-mini-logger/main.go similarity index 100% rename from go-mini-logger/main.go rename to go基础语法/07go-mini-logger/main.go diff --git a/go-goroutine-gpm/go.mod b/go并发模型/01go-goroutine-gpm/go.mod similarity index 100% rename from go-goroutine-gpm/go.mod rename to go并发模型/01go-goroutine-gpm/go.mod diff --git a/go-goroutine-gpm/main.go b/go并发模型/01go-goroutine-gpm/main.go similarity index 100% rename from go-goroutine-gpm/main.go rename to go并发模型/01go-goroutine-gpm/main.go diff --git a/go-channel-practice/buffered.go b/go并发模型/02go-channel-practice/buffered.go similarity index 100% rename from go-channel-practice/buffered.go rename to go并发模型/02go-channel-practice/buffered.go diff --git a/go-channel-practice/directional.go b/go并发模型/02go-channel-practice/directional.go similarity index 78% rename from go-channel-practice/directional.go rename to go并发模型/02go-channel-practice/directional.go index 6c8b86a..05ecb34 100644 --- a/go-channel-practice/directional.go +++ b/go并发模型/02go-channel-practice/directional.go @@ -23,6 +23,6 @@ func consumer(in <-chan int) { func main() { ch := make(chan int) - go producer(ch) // ch 在这里被当作 只发送 channel 使用 - consumer(ch) // ch 在这里被当作 只接收 channel 使用 + go producer(ch) // ch 在这里被当作 只发送 channel 使用 + consumer(ch) // ch 在这里被当作 只接收 channel 使用 } diff --git a/go-channel-practice/go.mod b/go并发模型/02go-channel-practice/go.mod similarity index 100% rename from go-channel-practice/go.mod rename to go并发模型/02go-channel-practice/go.mod diff --git a/go-channel-practice/pipeline.go b/go并发模型/02go-channel-practice/pipeline.go similarity index 100% rename from go-channel-practice/pipeline.go rename to go并发模型/02go-channel-practice/pipeline.go diff --git a/go-channel-practice/unbuffered.go b/go并发模型/02go-channel-practice/unbuffered.go similarity index 100% rename from go-channel-practice/unbuffered.go rename to go并发模型/02go-channel-practice/unbuffered.go diff --git a/go-select-practice/go.mod b/go并发模型/03go-select-practice/go.mod similarity index 100% rename from go-select-practice/go.mod rename to go并发模型/03go-select-practice/go.mod diff --git a/go-select-practice/select_basic.go b/go并发模型/03go-select-practice/select_basic.go similarity index 99% rename from go-select-practice/select_basic.go rename to go并发模型/03go-select-practice/select_basic.go index ebe503c..022d42c 100644 --- a/go-select-practice/select_basic.go +++ b/go并发模型/03go-select-practice/select_basic.go @@ -31,4 +31,3 @@ func main() { fmt.Println("main 结束") } - diff --git a/go-select-practice/select_context.go b/go并发模型/03go-select-practice/select_context.go similarity index 100% rename from go-select-practice/select_context.go rename to go并发模型/03go-select-practice/select_context.go diff --git a/go-select-practice/select_default.go b/go并发模型/03go-select-practice/select_default.go similarity index 100% rename from go-select-practice/select_default.go rename to go并发模型/03go-select-practice/select_default.go diff --git a/go-select-practice/select_timeout.go b/go并发模型/03go-select-practice/select_timeout.go similarity index 100% rename from go-select-practice/select_timeout.go rename to go并发模型/03go-select-practice/select_timeout.go diff --git a/go-context-practice/cancel_basic.go b/go并发模型/04go-context-practice/cancel_basic.go similarity index 100% rename from go-context-practice/cancel_basic.go rename to go并发模型/04go-context-practice/cancel_basic.go diff --git a/go-context-practice/derived_context.go b/go并发模型/04go-context-practice/derived_context.go similarity index 100% rename from go-context-practice/derived_context.go rename to go并发模型/04go-context-practice/derived_context.go diff --git a/go-context-practice/go.mod b/go并发模型/04go-context-practice/go.mod similarity index 100% rename from go-context-practice/go.mod rename to go并发模型/04go-context-practice/go.mod diff --git a/go-context-practice/timeout_with_context.go b/go并发模型/04go-context-practice/timeout_with_context.go similarity index 100% rename from go-context-practice/timeout_with_context.go rename to go并发模型/04go-context-practice/timeout_with_context.go diff --git a/go-sync-practice/go.mod b/go并发模型/05go-sync-practice/go.mod similarity index 100% rename from go-sync-practice/go.mod rename to go并发模型/05go-sync-practice/go.mod diff --git a/go-sync-practice/mutex_counter.go b/go并发模型/05go-sync-practice/mutex_counter.go similarity index 100% rename from go-sync-practice/mutex_counter.go rename to go并发模型/05go-sync-practice/mutex_counter.go diff --git a/go-sync-practice/race_counter.go b/go并发模型/05go-sync-practice/race_counter.go similarity index 100% rename from go-sync-practice/race_counter.go rename to go并发模型/05go-sync-practice/race_counter.go diff --git a/go-sync-practice/rwmutex_cache.go b/go并发模型/05go-sync-practice/rwmutex_cache.go similarity index 100% rename from go-sync-practice/rwmutex_cache.go rename to go并发模型/05go-sync-practice/rwmutex_cache.go diff --git a/go-sync-practice/waitgroup_like_latch.go b/go并发模型/05go-sync-practice/waitgroup_like_latch.go similarity index 100% rename from go-sync-practice/waitgroup_like_latch.go rename to go并发模型/05go-sync-practice/waitgroup_like_latch.go diff --git a/go-atomic-cpu/atomic_cas.go b/go并发模型/06go-atomic-cpu/atomic_cas.go similarity index 100% rename from go-atomic-cpu/atomic_cas.go rename to go并发模型/06go-atomic-cpu/atomic_cas.go diff --git a/go-atomic-cpu/atomic_counter.go b/go并发模型/06go-atomic-cpu/atomic_counter.go similarity index 100% rename from go-atomic-cpu/atomic_counter.go rename to go并发模型/06go-atomic-cpu/atomic_counter.go diff --git a/go-atomic-cpu/cpu_busy_loop.go b/go并发模型/06go-atomic-cpu/cpu_busy_loop.go similarity index 100% rename from go-atomic-cpu/cpu_busy_loop.go rename to go并发模型/06go-atomic-cpu/cpu_busy_loop.go diff --git a/go-atomic-cpu/cpu_channel_wait.go b/go并发模型/06go-atomic-cpu/cpu_channel_wait.go similarity index 100% rename from go-atomic-cpu/cpu_channel_wait.go rename to go并发模型/06go-atomic-cpu/cpu_channel_wait.go diff --git a/go-atomic-cpu/go.mod b/go并发模型/06go-atomic-cpu/go.mod similarity index 100% rename from go-atomic-cpu/go.mod rename to go并发模型/06go-atomic-cpu/go.mod diff --git a/go-crawler/crawler_basic.go b/go并发模型/07go-crawler/crawler_basic.go similarity index 100% rename from go-crawler/crawler_basic.go rename to go并发模型/07go-crawler/crawler_basic.go diff --git a/go-crawler/go.mod b/go并发模型/07go-crawler/go.mod similarity index 100% rename from go-crawler/go.mod rename to go并发模型/07go-crawler/go.mod