package handler import ( "net/http" "strings" "unicode" "silk-server-go/internal/middleware" "silk-server-go/internal/model" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // RegisterRoomRoutes 注册蚕房路由 func RegisterRoomRoutes(rg *gin.RouterGroup, db *gorm.DB) { rg.GET("/rooms", middleware.RequirePermission(db, "room:read"), listRooms(db)) rg.GET("/rooms/:id", middleware.RequirePermission(db, "room:read"), getRoom(db)) rg.POST("/rooms", middleware.RequirePermission(db, "room:write"), createRoom(db)) rg.PATCH("/rooms/:id", middleware.RequirePermission(db, "room:write"), updateRoom(db)) rg.DELETE("/rooms/:id", middleware.RequirePermission(db, "room:write"), deleteRoom(db)) } // listRooms 蚕房列表 func listRooms(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { var rooms []model.Room q := applyOrgScope(db.Model(&model.Room{}), c, "org_id") q.Order("created_at DESC").Find(&rooms) c.JSON(http.StatusOK, rooms) } } // getRoom 蚕房详情 func getRoom(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { id := c.Param("id") if !requireObjectAccess(c, db, "room", id) { return } var room model.Room if db.Where("id = ?", id).First(&room).Error != nil { c.JSON(http.StatusNotFound, gin.H{"error": "room not found"}) return } c.JSON(http.StatusOK, room) } } // createRoom 新建蚕房 func createRoom(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { var room model.Room if err := c.ShouldBindJSON(&room); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } room.ID = "" // 让数据库自动生成 if room.OrgID == nil { room.OrgID = defaultOrgID(db, c) } else if !canAccessOrganization(db, c, *room.OrgID) { c.JSON(http.StatusForbidden, gin.H{"error": "无权在该组织下创建蚕房"}) return } if err := db.Create(&room).Error; err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "创建失败"}) return } c.JSON(http.StatusCreated, room) } } // updateRoom 更新蚕房 func updateRoom(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { id := c.Param("id") if !requireObjectAccess(c, db, "room", id) { return } var room model.Room if db.Where("id = ?", id).First(&room).Error != nil { c.JSON(http.StatusNotFound, gin.H{"error": "room not found"}) return } updates, err := bindUpdates(c) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if orgID, ok := updates["org_id"]; ok { if orgStr, ok := orgID.(string); ok && !canAccessOrganization(db, c, orgStr) { c.JSON(http.StatusForbidden, gin.H{"error": "无权迁移蚕房到该组织"}) return } } if len(updates) > 0 { db.Model(&model.Room{}).Where("id = ?", id).Updates(updates) } db.Where("id = ?", id).First(&room) c.JSON(http.StatusOK, room) } } // deleteRoom 删除蚕房 func deleteRoom(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { id := c.Param("id") if !requireObjectAccess(c, db, "room", id) { return } var room model.Room if db.Where("id = ?", id).First(&room).Error != nil { c.JSON(http.StatusNotFound, gin.H{"error": "room not found"}) return } db.Where("id = ?", id).Delete(&model.Room{}) c.JSON(http.StatusOK, gin.H{"id": id}) } } // camelToSnake 将 camelCase 转为 snake_case(供所有 CRUD handler 复用) func camelToSnake(s string) string { var result strings.Builder for i, r := range s { if i > 0 && unicode.IsUpper(r) { result.WriteRune('_') } result.WriteRune(unicode.ToLower(r)) } return result.String() } // bindUpdates 绑定 JSON body 并转为 snake_case 的 map(排除不可更新字段) func bindUpdates(c *gin.Context) (map[string]interface{}, error) { var body map[string]interface{} if err := c.ShouldBindJSON(&body); err != nil { return nil, err } updates := make(map[string]interface{}) for k, v := range body { // 排除不可更新字段 if k == "id" || k == "createdAt" || k == "updatedAt" { continue } updates[camelToSnake(k)] = v } return updates, nil }