package main import ( "fmt" "net/http" "os" "path/filepath" "strings" "github.com/gin-gonic/gin" "gorm.io/driver/sqlite" "gorm.io/gorm" "github.com/google/uuid" "goodnews/models" "goodnews/services" ) // 全局数据库连接 var db *gorm.DB func main() { // 初始化数据库 var err error db, err = gorm.Open(sqlite.Open("goodnews.db"), &gorm.Config{}) if err != nil { panic("failed to connect database") } // 自动迁移数据库 db.AutoMigrate(&models.GoodNews{}) // 初始化Gin路由 r := gin.Default() // 设置静态文件服务 r.Static("/uploads", "./uploads") // 设置页面路由 r.StaticFile("/", "./templates/index.html") r.StaticFile("/manage", "./templates/manage.html") // API路由组 api := r.Group("/api") { api.GET("/news", getGoodNewsList) api.PUT("/news/:id", updateGoodNews) api.DELETE("/news/:id", deleteGoodNews) api.POST("/upload", handleUploadAndCreate) api.GET("/offices", getOfficesList) } // 启动服务 r.Run(":8080") } // 获取喜报列表 func getGoodNewsList(c *gin.Context) { var goodNewsList []models.GoodNews result := db.Find(&goodNewsList) if result.Error != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": result.Error.Error()}) return } // 处理每条记录,添加图片URL并设置Office字段 for i := range goodNewsList { // 将ImagePath转换为可访问的URL goodNewsList[i].ImageURL = "/" + goodNewsList[i].ImagePath // 确保Office字段与Representative字段一致 goodNewsList[i].Office = goodNewsList[i].Representative } c.JSON(http.StatusOK, goodNewsList) } // 获取代表处列表 func getOfficesList(c *gin.Context) { var offices []string result := db.Model(&models.GoodNews{}).Distinct().Pluck("representative", &offices) if result.Error != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": result.Error.Error()}) return } c.JSON(http.StatusOK, offices) } // 处理文件上传并创建喜报 func handleUploadAndCreate(c *gin.Context) { // 获取上传的图片 file, err := c.FormFile("file") if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "请选择要上传的图片文件"}) return } // 验证文件类型 ext := filepath.Ext(file.Filename) allowedExts := map[string]bool{".jpg": true, ".jpeg": true, ".png": true} if !allowedExts[strings.ToLower(ext)] { c.JSON(http.StatusBadRequest, gin.H{"error": "只支持jpg、jpeg和png格式的图片"}) return } // 验证文件大小(限制为10MB) if file.Size > 10<<20 { // 10 MB c.JSON(http.StatusBadRequest, gin.H{"error": "图片大小不能超过10MB"}) return } // 生成唯一的文件名 newFileName := uuid.New().String() + ext uploadDir := filepath.Join(".", "uploads") filePath := filepath.Join(uploadDir, newFileName) // 确保上传目录存在 if err := os.MkdirAll(uploadDir, 0755); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("创建上传目录失败: %v", err)}) return } // 保存图片 if err := c.SaveUploadedFile(file, filePath); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("图片保存失败: %v", err)}) return } // 创建OCR服务 ocrService, err := services.NewOCRService() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "OCR服务初始化失败"}) return } defer ocrService.Close() // 提取图片信息 projectName, points, representative, err := ocrService.ExtractInfo(filePath) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("图片识别失败: %v", err)}) return } fmt.Println("OCR识别信息:", projectName, points, representative) // 创建喜报记录 goodNews := models.GoodNews{ ProjectName: projectName, Points: points, Representative: representative, ImagePath: filePath, } result := db.Create(&goodNews) if result.Error != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": result.Error.Error()}) return } // 重新获取记录以确保包含ID db.First(&goodNews, goodNews.ID) goodNews.ImageURL = "/" + goodNews.ImagePath c.JSON(http.StatusOK, goodNews) } // 更新喜报 func updateGoodNews(c *gin.Context) { var goodNews models.GoodNews if err := db.First(&goodNews, c.Param("id")).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "记录不存在"}) return } if err := c.ShouldBindJSON(&goodNews); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } db.Save(&goodNews) c.JSON(http.StatusOK, goodNews) } // 删除喜报 func deleteGoodNews(c *gin.Context) { var goodNews models.GoodNews if err := db.First(&goodNews, c.Param("id")).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "记录不存在"}) return } db.Delete(&goodNews) c.JSON(http.StatusOK, gin.H{"message": "删除成功"}) } // 处理文件上传 func handleFileUpload(c *gin.Context) { // 获取上传的文件 file, err := c.FormFile("file") if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "请选择要上传的文件"}) return } // 验证文件类型 ext := filepath.Ext(file.Filename) allowedExts := map[string]bool{".jpg": true, ".jpeg": true, ".png": true} if !allowedExts[strings.ToLower(ext)] { c.JSON(http.StatusBadRequest, gin.H{"error": "只支持jpg、jpeg和png格式的图片"}) return } // 生成唯一的文件名 newFileName := uuid.New().String() + ext uploadDir := filepath.Join(".", "uploads") filePath := filepath.Join(uploadDir, newFileName) // 确保上传目录存在 if err := os.MkdirAll(uploadDir, 0755); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("创建上传目录失败: %v", err)}) return } // 保存图片 if err := c.SaveUploadedFile(file, filePath); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("图片保存失败: %v", err)}) return } // 创建OCR服务 ocrService, err := services.NewOCRService() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "OCR服务初始化失败"}) return } defer ocrService.Close() // 提取图片信息 projectName, points, representative, err := ocrService.ExtractInfo(filePath) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("图片识别失败: %v", err)}) return } // 创建喜报记录 goodNews := models.GoodNews{ ProjectName: projectName, Points: points, Representative: representative, ImagePath: filePath, } result := db.Create(&goodNews) if result.Error != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": result.Error.Error()}) return } // 重新获取记录以确保包含ID db.First(&goodNews, goodNews.ID) goodNews.ImageURL = "/" + goodNews.ImagePath c.JSON(http.StatusOK, goodNews) }