package main import ( "context" "fmt" "lifelog/internal/handler" "lifelog/internal/model" "lifelog/internal/repository" "lifelog/internal/service" "log" "net/http" "os" "os/signal" "syscall" "time" "github.com/gin-gonic/gin" "gorm.io/driver/mysql" "gorm.io/gorm" ) func main() { gin.SetMode(gin.ReleaseMode) // 数据库连接 dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", getEnv("DB_USER", "rhett_claw"), getEnv("DB_PASS", "Zhugezhongli001"), getEnv("DB_HOST", "localhost"), getEnv("DB_NAME", "rhett_claw"), ) db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { log.Fatalf("连接数据库失败: %v", err) } // 自动建表 db.AutoMigrate(&model.Todo{}, &model.DailyLog{}, &model.FinanceEntry{}) // 初始化各层 todoRepo := repository.NewTodoRepository(db) dailyLogRepo := repository.NewDailyLogRepository(db) financeRepo := repository.NewFinanceRepository(db) todoSvc := service.NewTodoService(todoRepo) dailyLogSvc := service.NewDailyLogService(dailyLogRepo) financeSvc := service.NewFinanceService(financeRepo) h := handler.NewHandler(todoSvc, dailyLogSvc, financeSvc) // Gin 路由 r := gin.New() r.Use(gin.Recovery()) // 静态文件(前端构建产物) distPath := "/var/www/lifelog/web/dist" r.Static("/assets", distPath+"/assets") r.StaticFile("/favicon.svg", distPath+"/favicon.svg") // 前端路由兜底 r.NoRoute(func(c *gin.Context) { c.File(distPath + "/index.html") }) // API 路由 api := r.Group("/api") { api.GET("/todos", h.GetTodos) api.POST("/todos", h.CreateTodo) api.PUT("/todos/:id", h.UpdateTodo) api.PATCH("/todos/:id/done", h.ToggleTodo) api.DELETE("/todos/:id", h.DeleteTodo) api.GET("/logs/:date", h.GetDailyLog) api.GET("/logs", h.GetRecentLogs) api.PUT("/logs/:date", h.SaveDailyLog) // 经济记录 api.GET("/finance/:year", h.GetFinanceByYear) api.POST("/finance", h.CreateFinanceEntry) api.PUT("/finance/:id", h.UpdateFinanceEntry) api.DELETE("/finance/:id", h.DeleteFinanceEntry) } port := getEnv("PORT", "17010") srv := &http.Server{Addr: ":" + port, Handler: r} go func() { log.Printf("LifeLog 服务启动: http://localhost:%s", port) if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("服务启动失败: %v", err) } }() // 优雅关机 quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit log.Println("正在关闭服务...") ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { log.Fatalf("服务强制退出: %v", err) } log.Println("服务已关闭") } func getEnv(key, defaultVal string) string { if val := os.Getenv(key); val != "" { return val } return defaultVal }