148 lines
5.3 KiB
Go
148 lines
5.3 KiB
Go
// Package router 装配 gin 引擎与各模块路由。
|
|
// 路由分层:/api/ma(小程序,靠请求头 app_id 鉴权)、/api/admin(后台)。
|
|
package router
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"gorm.io/gorm"
|
|
|
|
adminhdl "portal-mini-go/internal/handler/admin"
|
|
miniapphdl "portal-mini-go/internal/handler/admin/miniapp"
|
|
rbachdl "portal-mini-go/internal/handler/admin/rbac"
|
|
userhdl "portal-mini-go/internal/handler/admin/user"
|
|
app1hdl "portal-mini-go/internal/handler/miniapp/app1"
|
|
filehdl "portal-mini-go/internal/handler/file"
|
|
"portal-mini-go/internal/middleware"
|
|
"portal-mini-go/internal/pkg/config"
|
|
"portal-mini-go/internal/pkg/response"
|
|
"portal-mini-go/internal/pkg/session"
|
|
"portal-mini-go/internal/pkg/storage"
|
|
"portal-mini-go/internal/pkg/wechat"
|
|
adminrepo "portal-mini-go/internal/repository/admin"
|
|
miniapprepo "portal-mini-go/internal/repository/miniapp"
|
|
"portal-mini-go/internal/repository/user"
|
|
filerepo "portal-mini-go/internal/repository/file"
|
|
authsvc "portal-mini-go/internal/service/admin/auth"
|
|
miniappsvc "portal-mini-go/internal/service/admin/miniapp"
|
|
adminrbac "portal-mini-go/internal/service/admin/rbac"
|
|
usersvc "portal-mini-go/internal/service/admin/user"
|
|
app1svc "portal-mini-go/internal/service/miniapp/app1"
|
|
filesvc "portal-mini-go/internal/service/file"
|
|
)
|
|
|
|
// New 构造并装配整个 HTTP 服务。
|
|
func New(cfg *config.Config, db *gorm.DB, store session.Store) *gin.Engine {
|
|
if cfg.Server.Mode == "release" {
|
|
gin.SetMode(gin.ReleaseMode)
|
|
}
|
|
|
|
r := gin.New()
|
|
r.Use(middleware.Recovery(), middleware.Trace(), middleware.ErrorHandler())
|
|
|
|
r.GET("/healthz", func(c *gin.Context) {
|
|
response.OK(c, gin.H{"status": "ok"})
|
|
})
|
|
|
|
// 文件存储:依据配置构造(当前支持本地磁盘,minIO 等预留)。
|
|
fileStore, err := storage.New(cfg.Upload)
|
|
if err != nil {
|
|
slog.Error("init storage", slog.Any("err", err))
|
|
os.Exit(1)
|
|
}
|
|
// 本地存储静态目录对外可访问(与 file.URL 拼接的 baseURL 对齐)。
|
|
r.Static(cfg.Upload.BaseURL, cfg.Upload.LocalPath)
|
|
// 共享的文件服务实例,admin 与小程序端复用。
|
|
fileRepo := filerepo.New(db)
|
|
fileSvc := filesvc.New(fileRepo, fileStore, cfg.Upload)
|
|
fileHdl := filehdl.NewHandler(fileSvc)
|
|
|
|
// 共享基础设施:微信客户端、用户仓储、小程序仓储。
|
|
wx := wechat.NewClient()
|
|
userRepo := user.New(db)
|
|
miniRepo := miniapprepo.New(db)
|
|
|
|
// 小程序分组:先校验请求头 app_id 身份,再挂载各小程序业务路由。
|
|
ma := r.Group("/api/ma")
|
|
ma.Use(middleware.MiniAppAuth(miniRepo))
|
|
{
|
|
app1Svc := app1svc.New(userRepo, miniRepo, wx, store)
|
|
app1hdl.Register(ma, app1hdl.NewHandler(app1Svc), middleware.MiniUserAuth(store))
|
|
|
|
// 文件上传服务:小程序端已登录用户可上传/管理文件。
|
|
maAuth := ma.Group("")
|
|
maAuth.Use(middleware.MiniUserAuth(store))
|
|
filehdl.RegisterMiniAppUpload(maAuth, fileHdl)
|
|
}
|
|
|
|
// 后台 RBAC 基础设施:仓储、缓存、权限 Provider、种子初始化。
|
|
adminUserRepo := adminrepo.NewUserRepository(db)
|
|
adminRoleRepo := adminrepo.NewRoleRepository(db)
|
|
adminPermRepo := adminrepo.NewPermissionRepository(db)
|
|
permCache := adminrbac.NewMemoryCache(5 * time.Minute)
|
|
provider := adminrbac.NewProvider(adminUserRepo, adminRoleRepo, adminPermRepo, permCache)
|
|
if err := provider.Seed(context.Background(),
|
|
seedDefault("ADMIN_SEED_USERNAME", "admin"),
|
|
seedDefault("ADMIN_SEED_PASSWORD", "Admin@123456"),
|
|
); err != nil {
|
|
slog.Error("rbac seed", slog.Any("err", err))
|
|
}
|
|
|
|
// 后台管理:登录为公开接口,其余均经 JWT 鉴权 + 权限加载(注入角色/权限集到 context)。
|
|
ad := r.Group("/api/admin")
|
|
{
|
|
authSvc := authsvc.New(adminUserRepo, provider, cfg.Admin.JWTSecret, parseTTL(cfg.Admin.TokenTTL))
|
|
adminHandler := adminhdl.NewHandler(authSvc)
|
|
// 登录公开,不挂 JWT 鉴权中间件。
|
|
adminhdl.RegisterLogin(ad, adminHandler)
|
|
|
|
// 鉴权后分组:AdminAuth + PermissionLoader。
|
|
authAd := ad.Group("")
|
|
authAd.Use(middleware.AdminAuth(cfg.Admin.JWTSecret))
|
|
authAd.Use(middleware.PermissionLoader(provider))
|
|
{
|
|
adminhdl.Register(authAd, adminHandler)
|
|
|
|
// RBAC 管理接口(自身受 PermAdminManage / PermRoleManage / PermPermManage 守卫)。
|
|
rbacHandler := rbachdl.NewHandler(
|
|
adminrbac.NewAdminService(adminUserRepo, provider),
|
|
adminrbac.NewRoleService(adminRoleRepo, provider),
|
|
adminrbac.NewPermissionService(adminPermRepo, provider),
|
|
)
|
|
rbachdl.Register(authAd, rbacHandler)
|
|
|
|
// 小程序管理:被后台纳管的微信小程序配置(增删改查)。
|
|
miniSvc := miniappsvc.New(miniRepo)
|
|
miniapphdl.Register(authAd, miniapphdl.NewHandler(miniSvc))
|
|
|
|
// 用户管理:各小程序端用户(列表、详情、编辑、禁用/启用)。
|
|
userSvc := usersvc.New(userRepo)
|
|
userhdl.Register(authAd, userhdl.NewHandler(userSvc))
|
|
|
|
// 文件上传服务:后台已认证管理员可上传/管理文件(admin 与 ma 双入口复用同一 Service)。
|
|
filehdl.RegisterAdminUpload(authAd, fileHdl)
|
|
}
|
|
}
|
|
|
|
return r
|
|
}
|
|
|
|
// seedDefault 读取环境变量,缺失时回退到默认值(仅用于首次种子初始化)。
|
|
func seedDefault(key, def string) string {
|
|
if v := os.Getenv(key); v != "" {
|
|
return v
|
|
}
|
|
return def
|
|
}
|
|
|
|
func parseTTL(s string) time.Duration {
|
|
if d, err := time.ParseDuration(s); err == nil {
|
|
return d
|
|
}
|
|
return 24 * time.Hour
|
|
}
|