package projects import ( "errors" "net/http" "github.com/gin-gonic/gin" "gorm.io/gorm" "senlinai-agent/backend/internal/httpx" ) // ChannelHandler owns project-scoped custom-link writes. type ChannelHandler struct { service *Service } func NewChannelHandler(service *Service) *ChannelHandler { return &ChannelHandler{service: service} } func (h *ChannelHandler) Register(router gin.IRouter) { router.POST("/projects/:id/channels", h.create) } func (h *ChannelHandler) create(c *gin.Context) { userID, project, ok := ownedProjectFromRequest(c) if !ok { return } var input CreateProjectChannelInput if err := c.ShouldBindJSON(&input); err != nil { httpx.Error(c, http.StatusBadRequest, "invalid_request", "频道参数无效") return } channel, err := h.service.CreateProjectChannel(userID, project.ID, input) if err != nil { switch { case errors.Is(err, ErrChannelTitleRequired), errors.Is(err, ErrChannelURLRequired), errors.Is(err, ErrChannelURLInvalid): httpx.Error(c, http.StatusBadRequest, "invalid_request", "频道名称和链接地址不能为空") case errors.Is(err, gorm.ErrRecordNotFound): httpx.Error(c, http.StatusNotFound, "not_found", "项目不存在") default: httpx.Error(c, http.StatusInternalServerError, "internal_error", "频道创建失败") } return } c.JSON(http.StatusCreated, WorkspaceChannelDTO{ ID: channel.Identity, ProjectID: project.Identity, Type: "custom_link", Title: channel.Title, Icon: channel.Icon, URL: channel.URL, SortOrder: channel.SortOrder, }) }