// 服务入口,初始化所有组件并启动 HTTP 服务 package main import ( "fmt" "log" "os" "com.sclktx/m/v2/internal/admin" "com.sclktx/m/v2/internal/auth" "com.sclktx/m/v2/internal/common/middleware" "com.sclktx/m/v2/internal/modules/appointment" "com.sclktx/m/v2/internal/modules/banner" "com.sclktx/m/v2/internal/modules/certification" "com.sclktx/m/v2/internal/modules/upload" "com.sclktx/m/v2/internal/modules/workflow" "com.sclktx/m/v2/internal/pkg/config" "com.sclktx/m/v2/internal/pkg/database" "com.sclktx/m/v2/internal/pkg/dingtalk" "com.sclktx/m/v2/internal/pkg/minio" "com.sclktx/m/v2/internal/pkg/wechat" "github.com/gin-gonic/gin" swaggerFiles "github.com/swaggo/files" ginSwagger "github.com/swaggo/gin-swagger" "go.uber.org/zap" // swagger docs _ "com.sclktx/m/v2/docs" ) // @title 来访预约管理系统 API // @version 1.0 // @description 来访预约管理系统的后端API接口文档 // @termsOfService http://swagger.io/terms/ // @contact.name API Support // @contact.url http://www.swagger.io/support // @contact.email support@swagger.io // @license.name Apache 2.0 // @license.url http://www.apache.org/licenses/LICENSE-2.0.html // @host localhost:8080 // @BasePath /v1 // @securityDefinitions.apikey BearerAuth // @in header // @name Authorization // @externalDocs.description OpenAPI // @externalDocs.url https://swagger.io/resources/open-api/ func main() { configPath := "config/config.yaml" if len(os.Args) > 1 { configPath = os.Args[1] } cfg, err := config.LoadConfig(configPath) if err != nil { log.Fatalf("Failed to load config: %v", err) } logger, err := zap.NewProduction() if err != nil { log.Fatalf("Failed to init logger: %v", err) } defer logger.Sync() db := database.InitDB(&cfg.Database) // 初始化基础数据(流程定义等) database.InitData(db) wxClient := wechat.NewMiniProgramClient(&cfg.Wechat) dingTalkClient := dingtalk.NewClient(&cfg.DingTalk) minioClient, err := minio.NewClient(&cfg.MinIO) if err != nil { log.Printf("Warning: MinIO 初始化失败: %v", err) } gin.SetMode(cfg.Server.Mode) r := gin.New() r.Use(middleware.CORS()) r.Use(middleware.Logger(logger)) r.Use(gin.Recovery()) r.GET("/health", func(c *gin.Context) { c.JSON(200, gin.H{"status": "ok"}) }) // Swagger 文档 r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) v1 := r.Group("/v1") authHandler := auth.NewAuthHandler(db, cfg, wxClient) // ========== 管理员路由(使用独立管理员 JWT,优先注册避免被小程序 JWT 拦截)========== var adminHandler *admin.AdminHandler { adminAuthHandler := admin.NewAdminAuthHandler(db, cfg.JWT.Secret, cfg.JWT.ExpireHours) // 公开:管理员登录 v1.POST("/admin/login", adminAuthHandler.Login) adminJWTMiddleware := middleware.AdminJWTAuth(cfg.JWT.Secret) // 管理员个人信息 v1.GET("/admin/profile", adminJWTMiddleware, adminAuthHandler.GetProfile) // 管理员 CRUD 接口 adminHandler = admin.NewAdminHandler(db, wxClient) adminHandler.RegisterRoutes(v1, adminJWTMiddleware) // 工作流流程定义管理(管理员后台使用) wfAdmin := workflow.NewHandler(db, wxClient.SendSubscribeMessage, nil) adminWF := v1.Group("/admin", adminJWTMiddleware) { adminWF.GET("/workflow/definitions", wfAdmin.ListDefinitions) adminWF.POST("/workflow/definitions", wfAdmin.CreateDefinition) adminWF.PUT("/workflow/definitions/:id", wfAdmin.UpdateDefinition) adminWF.DELETE("/workflow/definitions/:id", wfAdmin.DeleteDefinition) // 工作流实例查询(管理后台查看审批进度) adminWF.GET("/workflow/instances", wfAdmin.ListInstances) adminWF.GET("/workflow/instances/:id", wfAdmin.GetInstanceDetail) } } // 公开路由 { authHandler.RegisterRoutes(v1) bannerHandler := banner.NewBannerHandler(db) bannerHandler.RegisterRoutes(v1) uploadHandler := upload.NewUploadHandler(wxClient, minioClient) uploadHandler.RegisterRoutes(v1) // 到访区域列表(小程序端需要) v1.GET("/visitor-areas", adminHandler.ListVisitorAreas) // 部门列表(小程序认证申请需要) v1.GET("/departments", adminHandler.ListAllDepartments) } // 认证路由(需要 JWT) authV1 := v1.Group("") authV1.Use(middleware.JWTAuth(cfg.JWT.Secret)) { authHandler.RegisterAuthRoutes(authV1) appointmentHandler := appointment.NewAppointmentHandler(db, wxClient, dingTalkClient) appointmentHandler.RegisterRoutes(authV1) certificationHandler := certification.NewHandler(db) certificationHandler.RegisterRoutes(authV1) workflowHandler := workflow.NewHandler(db, wxClient.SendSubscribeMessage, dingTalkClient) workflowHandler.RegisterRoutes(authV1) } addr := fmt.Sprintf(":%d", cfg.Server.Port) logger.Info("Server starting", zap.String("addr", addr)) log.Printf("Server is running at http://localhost%s", addr) if err := r.Run(addr); err != nil { log.Fatalf("Failed to start server: %v", err) } }