package web import ( "fmt" "net/http" "strconv" "xiaji-go/models" "github.com/gin-gonic/gin" "gorm.io/gorm" ) type WebServer struct { db *gorm.DB port int username string password string } func NewWebServer(db *gorm.DB, port int, username, password string) *WebServer { return &WebServer{db: db, port: port, username: username, password: password} } func (s *WebServer) Start() { gin.SetMode(gin.ReleaseMode) r := gin.Default() r.LoadHTMLGlob("templates/*") // 页面 r.GET("/", s.handleIndex) r.GET("/api/records", s.handleRecords) r.POST("/delete/:id", s.handleDelete) r.GET("/export", s.handleExport) // 健康检查 r.GET("/health", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"status": "ok"}) }) logAddr := fmt.Sprintf(":%d", s.port) fmt.Printf("🌐 Web后台运行在 http://127.0.0.1%s\n", logAddr) if err := r.Run(logAddr); err != nil { fmt.Printf("❌ Web服务启动失败: %v\n", err) } } func (s *WebServer) handleIndex(c *gin.Context) { c.HTML(http.StatusOK, "index.html", nil) } func (s *WebServer) handleRecords(c *gin.Context) { var items []models.Transaction s.db.Where("is_deleted = ?", false).Order("id desc").Limit(50).Find(&items) type txResponse struct { ID uint `json:"id"` UserID int64 `json:"user_id"` Amount float64 `json:"amount"` Category string `json:"category"` Note string `json:"note"` Date string `json:"date"` } resp := make([]txResponse, len(items)) for i, item := range items { resp[i] = txResponse{ ID: item.ID, UserID: item.UserID, Amount: item.AmountYuan(), Category: item.Category, Note: item.Note, Date: item.Date, } } c.JSON(http.StatusOK, resp) } func (s *WebServer) handleDelete(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "无效的ID"}) return } result := s.db.Model(&models.Transaction{}).Where("id = ? AND is_deleted = ?", id, false).Update("is_deleted", true) if result.Error != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "删除失败"}) return } if result.RowsAffected == 0 { c.JSON(http.StatusNotFound, gin.H{"error": "记录不存在或已删除"}) return } c.JSON(http.StatusOK, gin.H{"status": "success"}) } func (s *WebServer) handleExport(c *gin.Context) { var items []models.Transaction s.db.Where("is_deleted = ?", false).Order("date asc, id asc").Find(&items) c.Header("Content-Type", "text/csv; charset=utf-8") c.Header("Content-Disposition", "attachment; filename=transactions.csv") // BOM for Excel c.Writer.Write([]byte{0xEF, 0xBB, 0xBF}) c.Writer.WriteString("ID,日期,分类,金额(元),备注\n") for _, item := range items { line := fmt.Sprintf("%d,%s,%s,%.2f,\"%s\"\n", item.ID, item.Date, item.Category, item.AmountYuan(), item.Note) c.Writer.WriteString(line) } }