diff --git a/sc-lktx-backend/.gitignore b/sc-lktx-backend/.gitignore index 7e2cd35..4fdb21f 100644 --- a/sc-lktx-backend/.gitignore +++ b/sc-lktx-backend/.gitignore @@ -99,5 +99,3 @@ tmp/ *.coverprofile coverage.html coverage.out - -server \ No newline at end of file diff --git a/sc-lktx-backend/cmd/server/main.go b/sc-lktx-backend/cmd/server/main.go new file mode 100644 index 0000000..6356001 --- /dev/null +++ b/sc-lktx-backend/cmd/server/main.go @@ -0,0 +1,170 @@ +// 服务入口,初始化所有组件并启动 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) + } +}