diff --git a/internal/router/router.go b/internal/router/router.go new file mode 100644 index 0000000..fffbf6f --- /dev/null +++ b/internal/router/router.go @@ -0,0 +1,194 @@ +package router + +import ( + "net/http" + "time" + + "gotest/internal/config" + "gotest/internal/database" + "gotest/internal/handler" + "gotest/internal/middleware" + + "github.com/gin-gonic/gin" +) + +// Setup 初始化路由和中间件,返回 Gin 引擎 +func Setup( + cfg *config.Config, + db *database.DB, + authH *handler.AuthHandler, + userH *handler.UserHandler, + adminH *handler.AdminHandler, + uploadH *handler.UploadHandler, + enterpriseH *handler.EnterpriseHandler, + shopH *handler.ShopHandler, + foodH *handler.FoodHandler, + gameH *handler.GameHandler, + appH *handler.AppHandler, + musicH *handler.MusicHandler, + dashboardH *handler.DashboardHandler, +) *gin.Engine { + if cfg.Env.Name == "prod" { + gin.SetMode(gin.ReleaseMode) + } + + r := gin.New() + + // 全局中间件链(顺序:RequestID → Recover → Logger → CORS → RateLimit) + r.Use(middleware.RequestID()) + r.Use(middleware.Recover()) + r.Use(middleware.Logger()) + r.Use(middleware.CORS(&cfg.CORS)) + r.Use(middleware.RateLimit(&cfg.RateLimit)) + + // 健康检查(含数据库就绪探针) + r.GET("/api/health", func(c *gin.Context) { + dbStatus := "up" + if err := db.HealthCheck(); err != nil { + dbStatus = "down: " + err.Error() + } + status := http.StatusOK + if dbStatus != "up" { + status = http.StatusServiceUnavailable + } + c.JSON(status, gin.H{ + "code": 0, + "message": "ok", + "data": gin.H{ + "status": dbStatus, + "time": time.Now().Format(time.RFC3339), + "env": cfg.Env.Name, + "database": cfg.Database.Driver, + }, + }) + }) + + // API v1 路由组 + api := r.Group("/api/v1") + { + // 认证(公开) + auth := api.Group("/auth") + { + auth.POST("/login", authH.Login) + } + + // 管理员检查与初始化(公开) + adminPub := api.Group("/admin") + { + adminPub.GET("/check", adminH.CheckAdmin) + adminPub.POST("/init", adminH.InitAdmin) + } + + // 企业数据 API(公开,3 套企业官网共用) + enterprise := api.Group("/enterprise") + { + enterprise.GET("/all", enterpriseH.GetAllData) + enterprise.GET("/company", enterpriseH.GetCompany) + enterprise.GET("/services", enterpriseH.ListServices) + enterprise.GET("/news", enterpriseH.ListNews) + enterprise.GET("/news/:id", enterpriseH.GetNews) + enterprise.GET("/team", enterpriseH.ListTeam) + } + + // 电商数据 API(公开,跨境电商网站使用) + shop := api.Group("/shop") + { + shop.GET("/home", shopH.GetHomeData) + shop.GET("/categories", shopH.ListCategories) + shop.GET("/products", shopH.ListProducts) + shop.GET("/products/:id", shopH.GetProduct) + shop.GET("/featured", shopH.ListFeatured) + } + + // 外卖数据 API(公开,3 套外卖网站共用) + food := api.Group("/food") + { + food.GET("/home", foodH.GetHomeData) + food.GET("/categories", foodH.ListCategories) + food.GET("/restaurants", foodH.ListRestaurants) + food.GET("/restaurants/:id", foodH.GetRestaurant) + food.GET("/restaurants/:id/menu", foodH.GetRestaurantMenu) + food.GET("/featured", foodH.ListFeatured) + } + + // 游戏数据 API(公开,3 套游戏网站共用) + game := api.Group("/game") + { + game.GET("/home", gameH.GetHomeData) + game.GET("/categories", gameH.ListCategories) + game.GET("/games", gameH.ListGames) + game.GET("/games/:id", gameH.GetGame) + game.GET("/hot", gameH.ListHot) + game.GET("/new", gameH.ListNew) + } + + // 应用数据 API(公开,3 套应用网站共用) + app := api.Group("/app") + { + app.GET("/home", appH.GetHomeData) + app.GET("/categories", appH.ListCategories) + app.GET("/apps", appH.ListApps) + app.GET("/apps/:id", appH.GetApp) + app.GET("/featured", appH.ListFeatured) + app.GET("/new", appH.ListNew) + } + + // 音乐数据 API(公开,3 套音乐网站共用) + music := api.Group("/music") + { + music.GET("/home", musicH.GetHomeData) + music.GET("/categories", musicH.ListCategories) + music.GET("/songs", musicH.ListSongs) + music.GET("/songs/:id", musicH.GetSong) + music.GET("/hot", musicH.ListHot) + music.GET("/new", musicH.ListNew) + } + + // 需要登录的路由 + authed := api.Group("") + authed.Use(middleware.JWTAuth(cfg.JWT.Secret)) + { + // 用户资料 + user := authed.Group("/user") + { + user.GET("/profile", userH.GetProfile) + user.PUT("/profile", userH.UpdateProfile) + user.PUT("/password", userH.ChangePassword) + } + // 文件上传 + authed.POST("/upload", uploadH.Upload) + } + + // 管理端路由(需要登录 + admin 角色) + admin := api.Group("/admin") + admin.Use(middleware.JWTAuth(cfg.JWT.Secret), requireAdmin()) + { + admin.GET("/users", userH.ListUsers) + admin.POST("/users", userH.CreateUser) + admin.PUT("/users/:id", userH.UpdateUser) + admin.DELETE("/users/:id", userH.DeleteUser) + + // 仪表盘 & 内容管理(通用 CRUD) + admin.GET("/dashboard/stats", dashboardH.GetStats) + admin.GET("/dashboard/:table", dashboardH.ListItems) + admin.POST("/dashboard/:table", dashboardH.CreateItem) + admin.PUT("/dashboard/:table/:id", dashboardH.UpdateItem) + admin.DELETE("/dashboard/:table/:id", dashboardH.DeleteItem) + } + } + + return r +} + +// requireAdmin 管理员权限校验中间件 +func requireAdmin() gin.HandlerFunc { + return func(c *gin.Context) { + if middleware.GetRole(c) != "admin" { + c.AbortWithStatusJSON(http.StatusForbidden, gin.H{ + "code": 1, "message": "权限不足", "data": nil, + }) + return + } + c.Next() + } +}