fix: 添加分类名称唯一约束,防止重复名称
This commit is contained in:
@@ -5,7 +5,6 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"sale/internal/crawler"
|
||||
"sale/internal/models"
|
||||
"sale/internal/schemas"
|
||||
"sale/internal/utils"
|
||||
@@ -164,15 +163,3 @@ func (h *ArticleHandler) TogglePin(c *gin.Context) {
|
||||
utils.DB.First(&article, id)
|
||||
c.JSON(http.StatusOK, gin.H{"data": article})
|
||||
}
|
||||
|
||||
// CrawlRibenyan 采集 ribenyan.com 文章
|
||||
func (h *ArticleHandler) CrawlRibenyan(c *gin.Context) {
|
||||
crawler := crawler.NewRibenyanCrawler(utils.DB)
|
||||
|
||||
if err := crawler.CrawlArticles(); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Crawl completed successfully"})
|
||||
}
|
||||
|
||||
@@ -34,6 +34,12 @@ func (h *CategoryHandler) Create(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
var existing models.Category
|
||||
if err := utils.DB.Where("name = ?", req.Name).First(&existing).Error; err == nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "分类名称已存在"})
|
||||
return
|
||||
}
|
||||
|
||||
category := models.Category{
|
||||
Name: req.Name,
|
||||
Description: req.Description,
|
||||
@@ -69,6 +75,14 @@ func (h *CategoryHandler) Update(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if req.Name != nil && *req.Name != category.Name {
|
||||
var existing models.Category
|
||||
if err := utils.DB.Where("name = ? AND id != ?", *req.Name, id).First(&existing).Error; err == nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "分类名称已存在"})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
updates := make(map[string]interface{})
|
||||
if req.Name != nil {
|
||||
updates["name"] = *req.Name
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"sale/internal/api/handlers"
|
||||
"sale/internal/api/middlewares"
|
||||
|
||||
@@ -28,6 +26,8 @@ func SetupRoutes(r *gin.Engine) {
|
||||
|
||||
r.Use(middlewares.CORSMiddleware())
|
||||
|
||||
r.Static("/uploads", "./uploads")
|
||||
|
||||
api := r.Group("/api")
|
||||
{
|
||||
api.GET("/installed", authHandler.CheckInstalled)
|
||||
@@ -46,8 +46,6 @@ func SetupRoutes(r *gin.Engine) {
|
||||
{
|
||||
articles.GET("", articleHandler.List)
|
||||
articles.GET("/:id", articleHandler.GetByID)
|
||||
// 采集 API(管理员权限)
|
||||
articles.POST("/crawl-ribenyan", articleHandler.CrawlRibenyan)
|
||||
}
|
||||
|
||||
api.GET("/banners", bannerHandler.List)
|
||||
@@ -217,25 +215,4 @@ func SetupRoutes(r *gin.Engine) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Serve uploads
|
||||
r.Static("/uploads", "./uploads")
|
||||
|
||||
// Serve frontend static assets (CSS, JS, images, etc.)
|
||||
r.StaticFS("/assets", http.Dir("./static/assets"))
|
||||
|
||||
// SPA fallback - serve index.html for all other routes (Vue Router History Mode)
|
||||
// This must be the last route to catch all unmatched paths
|
||||
r.NoRoute(func(c *gin.Context) {
|
||||
path := c.Request.URL.Path
|
||||
// For root and SPA routes, serve index.html
|
||||
// API requests will return 404 if not found
|
||||
if path == "/" || path == "/favicon.ico" || path == "/icons.svg" || path == "/favicon.svg" ||
|
||||
len(path) > 4 && path[:4] != "/api/" {
|
||||
c.File("/app/static/index.html")
|
||||
} else {
|
||||
// API routes - let them return 404 if not found
|
||||
c.AbortWithStatus(404)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
type Category struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
Name string `gorm:"size:100;not null" json:"name"`
|
||||
Name string `gorm:"size:100;not null;uniqueIndex" json:"name"`
|
||||
Description string `json:"description"`
|
||||
ParentID *uint `json:"parent_id"`
|
||||
MinAmount *float64 `json:"min_amount"`
|
||||
|
||||
Reference in New Issue
Block a user