// Package note 提供便签接口。 package note import ( "net/http" "github.com/gin-gonic/gin" "gorm.io/gorm" "rill/internal/httpx" "rill/internal/model" ) // Request 创建/更新便签请求。 type Request struct { Title string `json:"title" binding:"required,max=200" example:"Shopping list"` Content string `json:"content" example:"Milk, eggs"` } // ListResponse 便签分页列表响应。 type ListResponse struct { Items []model.Note `json:"items"` Total int64 `json:"total" example:"42"` Page int `json:"page" example:"1"` PageSize int `json:"page_size" example:"20"` } // @Summary List notes // @Description List notes ordered by id DESC. page starts at 1; page_size is 1-100, default 20. // @Tags user // @Produce json // @Param page query int false "Page number, default 1" example(1) // @Param page_size query int false "Page size, default 20, max 100" example(20) // @Success 200 {object} note.ListResponse // @Failure 500 {object} httpx.ErrorResponse // @Security BearerAuth // @Failure 401 {object} httpx.ErrorResponse "unauthorized or session expired" // @Failure 403 {object} httpx.ErrorResponse "account disabled" // @Router /notes [get] func List(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { page, pageSize := httpx.ParsePagination(c) ctx := c.Request.Context() var total int64 if err := db.WithContext(ctx).Model(&model.Note{}).Count(&total).Error; err != nil { httpx.RespondDBError(c, err) return } var notes []model.Note if err := db.WithContext(ctx). Order("id DESC"). Offset((page - 1) * pageSize). Limit(pageSize). Find(¬es).Error; err != nil { httpx.RespondDBError(c, err) return } c.JSON(http.StatusOK, ListResponse{ Items: notes, Total: total, Page: page, PageSize: pageSize, }) } } // @Summary Create a note // @Description Create a note. title is required (max 200 chars); content is optional. // @Tags user // @Accept json // @Produce json // @Param note body note.Request true "Note payload" // @Success 201 {object} model.Note // @Failure 400 {object} httpx.ErrorResponse "invalid request" // @Failure 500 {object} httpx.ErrorResponse // @Security BearerAuth // @Failure 401 {object} httpx.ErrorResponse "unauthorized or session expired" // @Failure 403 {object} httpx.ErrorResponse "account disabled" // @Router /notes [post] func Create(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { var req Request if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, httpx.ErrorResponse{Error: "invalid request: " + err.Error()}) return } note := model.Note{Title: req.Title, Content: req.Content} if err := db.WithContext(c.Request.Context()).Create(¬e).Error; err != nil { httpx.RespondDBError(c, err) return } c.JSON(http.StatusCreated, note) } } // @Summary Get a note // @Description Get a note by id. // @Tags user // @Produce json // @Param id path int true "Note ID" example(1) // @Success 200 {object} model.Note // @Failure 400 {object} httpx.ErrorResponse "invalid id" // @Failure 404 {object} httpx.ErrorResponse "record not found" // @Failure 500 {object} httpx.ErrorResponse // @Security BearerAuth // @Failure 401 {object} httpx.ErrorResponse "unauthorized or session expired" // @Failure 403 {object} httpx.ErrorResponse "account disabled" // @Router /notes/{id} [get] func Get(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { id, ok := httpx.ParseID(c) if !ok { return } var note model.Note if err := db.WithContext(c.Request.Context()).First(¬e, id).Error; err != nil { httpx.RespondGetError(c, err) return } c.JSON(http.StatusOK, note) } } // @Summary Update a note // @Description Update title and content; validation is the same as create. // @Tags user // @Accept json // @Produce json // @Param id path int true "Note ID" example(1) // @Param note body note.Request true "Note payload" // @Success 200 {object} model.Note // @Failure 400 {object} httpx.ErrorResponse "invalid request or id" // @Failure 404 {object} httpx.ErrorResponse "record not found" // @Failure 500 {object} httpx.ErrorResponse // @Security BearerAuth // @Failure 401 {object} httpx.ErrorResponse "unauthorized or session expired" // @Failure 403 {object} httpx.ErrorResponse "account disabled" // @Router /notes/{id} [put] func Update(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { id, ok := httpx.ParseID(c) if !ok { return } var req Request if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, httpx.ErrorResponse{Error: "invalid request: " + err.Error()}) return } ctx := c.Request.Context() var note model.Note if err := db.WithContext(ctx).First(¬e, id).Error; err != nil { httpx.RespondGetError(c, err) return } note.Title = req.Title note.Content = req.Content if err := db.WithContext(ctx).Save(¬e).Error; err != nil { httpx.RespondDBError(c, err) return } c.JSON(http.StatusOK, note) } } // @Summary Delete a note // @Description Delete a note by id; returns 204 with no body on success. // @Tags user // @Produce json // @Param id path int true "Note ID" example(1) // @Success 204 "Deleted" // @Failure 400 {object} httpx.ErrorResponse "invalid id" // @Failure 404 {object} httpx.ErrorResponse "record not found" // @Failure 500 {object} httpx.ErrorResponse // @Security BearerAuth // @Failure 401 {object} httpx.ErrorResponse "unauthorized or session expired" // @Failure 403 {object} httpx.ErrorResponse "account disabled" // @Router /notes/{id} [delete] func Delete(db *gorm.DB) gin.HandlerFunc { return func(c *gin.Context) { id, ok := httpx.ParseID(c) if !ok { return } result := db.WithContext(c.Request.Context()).Delete(&model.Note{}, id) if result.Error != nil { httpx.RespondDBError(c, result.Error) return } if result.RowsAffected == 0 { c.JSON(http.StatusNotFound, httpx.ErrorResponse{Error: "record not found"}) return } c.Status(http.StatusNoContent) } }