package handler import ( "net/http" "silk-server-go/internal/middleware" "silk-server-go/internal/model" "silk-server-go/internal/service" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // RegisterKnowledgeRoutes 注册知识库路由 func RegisterKnowledgeRoutes(rg *gin.RouterGroup, db *gorm.DB, s3 *service.S3Service, imageBucket string) { read := middleware.RequirePermission(db, "knowledge:read") write := middleware.RequirePermission(db, "knowledge:write") rg.GET("/knowledge/diseases", read, listDiseases(db)) rg.GET("/knowledge/diseases/:id", read, getDisease(db)) rg.POST("/knowledge/diseases", write, createDisease(db)) rg.PATCH("/knowledge/diseases/:id", write, updateDisease(db)) rg.DELETE("/knowledge/diseases/:id", write, deleteDisease(db)) rg.GET("/knowledge/articles", read, listArticles(db)) rg.GET("/knowledge/articles/:id", read, getArticle(db)) rg.POST("/knowledge/articles", write, createArticle(db)) rg.PATCH("/knowledge/articles/:id", write, updateArticle(db)) rg.DELETE("/knowledge/articles/:id", write, deleteArticle(db)) rg.GET("/knowledge/stage-hints", read, listStageHints(db)) registerKnowledgeImageRoute(rg, db, s3, imageBucket) } // listStageHints 饲养阶段风险提示(?stage= 过滤;GET 天然幂等) func listStageHints(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { hints, err := model.StageHintsByStage(c.Query("stage")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } resp := make([]gin.H, 0, len(hints)) for _, h := range hints { diseases := make([]gin.H, 0, len(h.Diseases)) for _, dh := range h.Diseases { id := "" var d model.Disease if db.Where("name = ?", dh.Name).First(&d).Error == nil { id = d.ID } diseases = append(diseases, gin.H{ "id": id, "name": dh.Name, "reason": dh.Reason, }) } resp = append(resp, gin.H{ "stage": h.Stage, "label": h.Label, "summary": h.Summary, "diseases": diseases, }) } c.JSON(http.StatusOK, resp) } } // listDiseases 蚕病百科列表(支持 category/keyword/enabled 过滤) func listDiseases(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { q := db.Model(&model.Disease{}) if cat := c.Query("category"); cat != "" { q = q.Where("category = ?", cat) } if kw := c.Query("keyword"); kw != "" { q = q.Where("name ILIKE ?", "%"+kw+"%") } if enabled := c.Query("enabled"); enabled != "" { q = q.Where("enabled = ?", enabled == "true") } if status := c.Query("status"); status != "" { if !canReviewKnowledge(c) { c.JSON(http.StatusForbidden, gin.H{"error": "无权限查看未发布内容"}) return } q = q.Where("status = ?", status) } else { q = q.Where("status = 'published'") } var list []model.Disease q.Order("sort_order ASC, name ASC").Find(&list) c.JSON(http.StatusOK, list) } } // getDisease 蚕病百科详情 func getDisease(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { var d model.Disease if db.Where("id = ?", c.Param("id")).First(&d).Error != nil { c.JSON(http.StatusNotFound, gin.H{"error": "disease not found"}) return } if d.Status != "published" && !canReviewKnowledge(c) { c.JSON(http.StatusNotFound, gin.H{"error": "disease not found"}) return } c.JSON(http.StatusOK, d) } } // createDisease 新建病种 func createDisease(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { var d model.Disease if err := c.ShouldBindJSON(&d); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } d.ID = "" d.Status = "draft" if d.Name == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "病种名称不能为空"}) return } if err := db.Create(&d).Error; err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "创建失败"}) return } c.JSON(http.StatusCreated, d) } } // updateDisease 更新病种 func updateDisease(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { id := c.Param("id") var d model.Disease if db.Where("id = ?", id).First(&d).Error != nil { c.JSON(http.StatusNotFound, gin.H{"error": "disease not found"}) return } updates, err := bindUpdates(c) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if len(updates) > 0 { if status, ok := updates["status"].(string); ok && !model.ValidKnowledgeTransition(d.Status, status) { c.JSON(http.StatusBadRequest, gin.H{"error": "非法的知识状态流转"}) return } db.Model(&model.Disease{}).Where("id = ?", id).Updates(updates) } db.Where("id = ?", id).First(&d) c.JSON(http.StatusOK, d) } } // deleteDisease 删除病种 func deleteDisease(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { id := c.Param("id") var d model.Disease if db.Where("id = ?", id).First(&d).Error != nil { c.JSON(http.StatusNotFound, gin.H{"error": "disease not found"}) return } db.Where("id = ?", id).Delete(&model.Disease{}) c.JSON(http.StatusOK, gin.H{"id": id}) } } // listArticles 知识文章列表(支持 kind/enabled 过滤) func listArticles(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { q := db.Model(&model.KnowledgeArticle{}) if kind := c.Query("kind"); kind != "" { q = q.Where("kind = ?", kind) } if enabled := c.Query("enabled"); enabled != "" { q = q.Where("enabled = ?", enabled == "true") } if status := c.Query("status"); status != "" { if !canReviewKnowledge(c) { c.JSON(http.StatusForbidden, gin.H{"error": "无权限查看未发布内容"}) return } q = q.Where("status = ?", status) } else { q = q.Where("status = 'published'") } var list []model.KnowledgeArticle q.Order("sort_order ASC, created_at DESC").Find(&list) c.JSON(http.StatusOK, list) } } // getArticle 知识文章详情 func getArticle(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { var a model.KnowledgeArticle if db.Where("id = ?", c.Param("id")).First(&a).Error != nil { c.JSON(http.StatusNotFound, gin.H{"error": "article not found"}) return } if a.Status != "published" && !canReviewKnowledge(c) { c.JSON(http.StatusNotFound, gin.H{"error": "article not found"}) return } c.JSON(http.StatusOK, a) } } // createArticle 新建知识文章 func createArticle(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { var a model.KnowledgeArticle if err := c.ShouldBindJSON(&a); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } a.ID = "" a.Status = "draft" if a.Kind == "" || a.Title == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "文章类型和标题不能为空"}) return } if err := db.Create(&a).Error; err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "创建失败"}) return } c.JSON(http.StatusCreated, a) } } // updateArticle 更新知识文章 func updateArticle(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { id := c.Param("id") var a model.KnowledgeArticle if db.Where("id = ?", id).First(&a).Error != nil { c.JSON(http.StatusNotFound, gin.H{"error": "article not found"}) return } updates, err := bindUpdates(c) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if len(updates) > 0 { if status, ok := updates["status"].(string); ok && !model.ValidKnowledgeTransition(a.Status, status) { c.JSON(http.StatusBadRequest, gin.H{"error": "非法的知识状态流转"}) return } db.Model(&model.KnowledgeArticle{}).Where("id = ?", id).Updates(updates) } db.Where("id = ?", id).First(&a) c.JSON(http.StatusOK, a) } } func canReviewKnowledge(c *gin.Context) bool { user, ok := c.Get("user") if !ok { return false } m, ok := user.(map[string]interface{}) if !ok { return false } role, _ := m["role"].(string) return role == "admin" || role == "operator" } // deleteArticle 删除知识文章 func deleteArticle(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { id := c.Param("id") var a model.KnowledgeArticle if db.Where("id = ?", id).First(&a).Error != nil { c.JSON(http.StatusNotFound, gin.H{"error": "article not found"}) return } db.Where("id = ?", id).Delete(&model.KnowledgeArticle{}) c.JSON(http.StatusOK, gin.H{"id": id}) } }