package main import ( "context" "fmt" "log" "net/http" "os" "os/signal" "syscall" "time" "github.com/gin-gonic/gin" "photography-backend/internal/config" "photography-backend/internal/database" "photography-backend/internal/models" "photography-backend/internal/service/upload" "photography-backend/internal/service/auth" "photography-backend/internal/handlers" "photography-backend/internal/middleware" ) func main() { // 加载配置 configPath := os.Getenv("CONFIG_PATH") if configPath == "" { configPath = "configs/config.yaml" } cfg, err := config.LoadConfig(configPath) if err != nil { log.Fatalf("加载配置失败: %v", err) } // 初始化数据库 if err := database.InitDatabase(cfg); err != nil { log.Fatalf("数据库初始化失败: %v", err) } // 自动迁移数据表 if err := database.AutoMigrate(); err != nil { log.Fatalf("数据库迁移失败: %v", err) } // 创建服务 uploadService := upload.NewUploadService(cfg) jwtService := auth.NewJWTService(&cfg.JWT) // 创建处理器 uploadHandler := handlers.NewUploadHandler(uploadService) // 创建中间件 authMiddleware := middleware.NewAuthMiddleware(jwtService) // 设置 Gin 模式 if cfg.IsProduction() { gin.SetMode(gin.ReleaseMode) } // 创建 Gin 引擎 r := gin.Default() // 添加 CORS 中间件 r.Use(func(c *gin.Context) { c.Header("Access-Control-Allow-Origin", "*") c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With") if c.Request.Method == "OPTIONS" { c.AbortWithStatus(204) return } c.Next() }) // 健康检查 r.GET("/health", func(c *gin.Context) { // 检查数据库连接 if err := database.HealthCheck(); err != nil { c.JSON(http.StatusServiceUnavailable, gin.H{ "status": "error", "message": "数据库连接失败", "error": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "status": "ok", "timestamp": time.Now().Unix(), "version": cfg.App.Version, "database": "connected", }) }) // 数据库统计 r.GET("/stats", func(c *gin.Context) { stats := database.GetStats() c.JSON(http.StatusOK, gin.H{ "database_stats": stats, }) }) // 静态文件服务 r.Static("/uploads", cfg.Storage.Local.BasePath) // 基础 API 路由 api := r.Group("/api/v1") { // 文件上传路由 upload := api.Group("/upload") { upload.POST("/photo", authMiddleware.RequireAuth(), uploadHandler.UploadPhoto) upload.DELETE("/photo/:id", authMiddleware.RequireAuth(), uploadHandler.DeletePhoto) upload.GET("/stats", authMiddleware.RequireAdmin(), uploadHandler.GetUploadStats) } // 认证路由 auth := api.Group("/auth") { auth.POST("/login", login) auth.POST("/register", register) auth.POST("/refresh", refreshToken) } // 用户相关 users := api.Group("/users") { users.GET("", getUsers) users.POST("", createUser) users.GET("/:id", getUser) users.PUT("/:id", updateUser) users.DELETE("/:id", deleteUser) } // 分类相关 categories := api.Group("/categories") { categories.GET("", getCategories) categories.POST("", createCategory) categories.GET("/:id", getCategory) categories.PUT("/:id", updateCategory) categories.DELETE("/:id", deleteCategory) } // 标签相关 tags := api.Group("/tags") { tags.GET("", getTags) tags.POST("", createTag) tags.GET("/:id", getTag) tags.PUT("/:id", updateTag) tags.DELETE("/:id", deleteTag) } // 相册相关 albums := api.Group("/albums") { albums.GET("", getAlbums) albums.POST("", createAlbum) albums.GET("/:id", getAlbum) albums.PUT("/:id", updateAlbum) albums.DELETE("/:id", deleteAlbum) } // 照片相关 photos := api.Group("/photos") { photos.GET("", getPhotos) photos.POST("", createPhoto) photos.GET("/:id", getPhoto) photos.PUT("/:id", updatePhoto) photos.DELETE("/:id", deletePhoto) } } // 创建 HTTP 服务器 server := &http.Server{ Addr: cfg.GetServerAddr(), Handler: r, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, } // 启动服务器 go func() { fmt.Printf("服务器启动在 %s\n", server.Addr) if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("服务器启动失败: %v", err) } }() // 等待中断信号 quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit fmt.Println("正在关闭服务器...") // 优雅关闭 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() if err := server.Shutdown(ctx); err != nil { log.Fatalf("服务器强制关闭: %v", err) } // 关闭数据库连接 if err := database.Close(); err != nil { log.Printf("关闭数据库连接失败: %v", err) } fmt.Println("服务器已关闭") } // 认证相关处理函数 func login(c *gin.Context) { var req models.LoginRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // 查找用户 var user models.User db := database.GetDB() // 可以使用用户名或邮箱登录 if err := db.Where("username = ? OR email = ?", req.Username, req.Username).First(&user).Error; err != nil { c.JSON(http.StatusUnauthorized, gin.H{"error": "用户名或密码错误"}) return } // TODO: 验证密码 (需要集成bcrypt) // 这里暂时跳过密码验证 // 生成JWT令牌 (简化实现) c.JSON(http.StatusOK, gin.H{ "message": "登录成功", "user": gin.H{ "id": user.ID, "username": user.Username, "email": user.Email, "role": user.Role, }, "token": "mock-jwt-token", // 实际项目中应该生成真实的JWT }) } func register(c *gin.Context) { var req models.CreateUserRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // 检查用户名是否已存在 var existingUser models.User db := database.GetDB() if err := db.Where("username = ? OR email = ?", req.Username, req.Email).First(&existingUser).Error; err == nil { c.JSON(http.StatusConflict, gin.H{"error": "用户名或邮箱已存在"}) return } // 创建用户 user := models.User{ Username: req.Username, Email: req.Email, Password: req.Password, // 实际项目中应该加密 Name: req.Name, Role: "user", IsActive: true, } if err := db.Create(&user).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // 清除密码 user.Password = "" c.JSON(http.StatusCreated, gin.H{ "message": "注册成功", "user": user, }) } func refreshToken(c *gin.Context) { // TODO: 实现刷新令牌逻辑 c.JSON(http.StatusOK, gin.H{ "message": "刷新令牌成功", "token": "new-mock-jwt-token", }) } // 用户 CRUD 操作 func getUsers(c *gin.Context) { var users []models.User db := database.GetDB() if err := db.Find(&users).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"data": users}) } func createUser(c *gin.Context) { var user models.User if err := c.ShouldBindJSON(&user); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } db := database.GetDB() if err := db.Create(&user).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusCreated, gin.H{"data": user}) } func getUser(c *gin.Context) { id := c.Param("id") var user models.User db := database.GetDB() if err := db.First(&user, id).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "用户不存在"}) return } c.JSON(http.StatusOK, gin.H{"data": user}) } func updateUser(c *gin.Context) { id := c.Param("id") var user models.User db := database.GetDB() if err := db.First(&user, id).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "用户不存在"}) return } if err := c.ShouldBindJSON(&user); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if err := db.Save(&user).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"data": user}) } func deleteUser(c *gin.Context) { id := c.Param("id") db := database.GetDB() if err := db.Delete(&models.User{}, id).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "用户删除成功"}) } // 分类 CRUD 操作 func getCategories(c *gin.Context) { var categories []models.Category db := database.GetDB() if err := db.Find(&categories).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"data": categories}) } func createCategory(c *gin.Context) { var category models.Category if err := c.ShouldBindJSON(&category); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } db := database.GetDB() if err := db.Create(&category).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusCreated, gin.H{"data": category}) } func getCategory(c *gin.Context) { id := c.Param("id") var category models.Category db := database.GetDB() if err := db.First(&category, id).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "分类不存在"}) return } c.JSON(http.StatusOK, gin.H{"data": category}) } func updateCategory(c *gin.Context) { id := c.Param("id") var category models.Category db := database.GetDB() if err := db.First(&category, id).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "分类不存在"}) return } if err := c.ShouldBindJSON(&category); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if err := db.Save(&category).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"data": category}) } func deleteCategory(c *gin.Context) { id := c.Param("id") db := database.GetDB() if err := db.Delete(&models.Category{}, id).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "分类删除成功"}) } // 标签 CRUD 操作 func getTags(c *gin.Context) { var tags []models.Tag db := database.GetDB() if err := db.Find(&tags).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"data": tags}) } func createTag(c *gin.Context) { var tag models.Tag if err := c.ShouldBindJSON(&tag); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } db := database.GetDB() if err := db.Create(&tag).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusCreated, gin.H{"data": tag}) } func getTag(c *gin.Context) { id := c.Param("id") var tag models.Tag db := database.GetDB() if err := db.First(&tag, id).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "标签不存在"}) return } c.JSON(http.StatusOK, gin.H{"data": tag}) } func updateTag(c *gin.Context) { id := c.Param("id") var tag models.Tag db := database.GetDB() if err := db.First(&tag, id).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "标签不存在"}) return } if err := c.ShouldBindJSON(&tag); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if err := db.Save(&tag).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"data": tag}) } func deleteTag(c *gin.Context) { id := c.Param("id") db := database.GetDB() if err := db.Delete(&models.Tag{}, id).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "标签删除成功"}) } // 相册 CRUD 操作 func getAlbums(c *gin.Context) { var albums []models.Album db := database.GetDB() if err := db.Find(&albums).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"data": albums}) } func createAlbum(c *gin.Context) { var album models.Album if err := c.ShouldBindJSON(&album); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } db := database.GetDB() if err := db.Create(&album).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusCreated, gin.H{"data": album}) } func getAlbum(c *gin.Context) { id := c.Param("id") var album models.Album db := database.GetDB() if err := db.First(&album, id).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "相册不存在"}) return } c.JSON(http.StatusOK, gin.H{"data": album}) } func updateAlbum(c *gin.Context) { id := c.Param("id") var album models.Album db := database.GetDB() if err := db.First(&album, id).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "相册不存在"}) return } if err := c.ShouldBindJSON(&album); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if err := db.Save(&album).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"data": album}) } func deleteAlbum(c *gin.Context) { id := c.Param("id") db := database.GetDB() if err := db.Delete(&models.Album{}, id).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "相册删除成功"}) } // 照片 CRUD 操作 func getPhotos(c *gin.Context) { var photos []models.Photo db := database.GetDB() if err := db.Find(&photos).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"data": photos}) } func createPhoto(c *gin.Context) { var photo models.Photo if err := c.ShouldBindJSON(&photo); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } db := database.GetDB() if err := db.Create(&photo).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusCreated, gin.H{"data": photo}) } func getPhoto(c *gin.Context) { id := c.Param("id") var photo models.Photo db := database.GetDB() if err := db.First(&photo, id).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "照片不存在"}) return } c.JSON(http.StatusOK, gin.H{"data": photo}) } func updatePhoto(c *gin.Context) { id := c.Param("id") var photo models.Photo db := database.GetDB() if err := db.First(&photo, id).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "照片不存在"}) return } if err := c.ShouldBindJSON(&photo); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if err := db.Save(&photo).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"data": photo}) } func deletePhoto(c *gin.Context) { id := c.Param("id") db := database.GetDB() if err := db.Delete(&models.Photo{}, id).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "照片删除成功"}) }