- 新增 /api/auth/register、/api/auth/login,JWT 签发与 Bearer 鉴权中间件 - notes 需登录,users/user-groups 仅管理员;auth 配置项随版本 1→2 自动补全 - internal/api 仅保留路由装配,拆分为 auth/user/usergroup/note/httpx/testutil - 同步更新 Swagger 文档与前端注册接口路径
206 lines
6.5 KiB
Go
206 lines
6.5 KiB
Go
// 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:"购物清单"`
|
|
Content string `json:"content" example:"牛奶、鸡蛋"`
|
|
}
|
|
|
|
// 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 分页查询便签列表,按 id 倒序返回。page 从 1 开始;page_size 取值 1-100,默认 20。
|
|
// @Tags notes
|
|
// @Produce json
|
|
// @Param page query int false "页码,默认 1" example(1)
|
|
// @Param page_size query int false "每页数量,默认 20,最大 100" example(20)
|
|
// @Success 200 {object} note.ListResponse
|
|
// @Failure 500 {object} httpx.ErrorResponse
|
|
// @Security BearerAuth
|
|
// @Failure 401 {object} httpx.ErrorResponse "未登录或登录已过期"
|
|
// @Failure 403 {object} httpx.ErrorResponse "账号已被禁用"
|
|
// @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 创建便签。title 必填且最长 200 字符,content 可选。
|
|
// @Tags notes
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param note body note.Request true "便签内容"
|
|
// @Success 201 {object} model.Note
|
|
// @Failure 400 {object} httpx.ErrorResponse "参数无效"
|
|
// @Failure 500 {object} httpx.ErrorResponse
|
|
// @Security BearerAuth
|
|
// @Failure 401 {object} httpx.ErrorResponse "未登录或登录已过期"
|
|
// @Failure 403 {object} httpx.ErrorResponse "账号已被禁用"
|
|
// @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: "参数无效: " + 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 按 id 查询单个便签。
|
|
// @Tags notes
|
|
// @Produce json
|
|
// @Param id path int true "便签 ID" example(1)
|
|
// @Success 200 {object} model.Note
|
|
// @Failure 400 {object} httpx.ErrorResponse "id 无效"
|
|
// @Failure 404 {object} httpx.ErrorResponse "记录不存在"
|
|
// @Failure 500 {object} httpx.ErrorResponse
|
|
// @Security BearerAuth
|
|
// @Failure 401 {object} httpx.ErrorResponse "未登录或登录已过期"
|
|
// @Failure 403 {object} httpx.ErrorResponse "账号已被禁用"
|
|
// @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 全量更新便签的 title 与 content,字段校验规则同创建。
|
|
// @Tags notes
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param id path int true "便签 ID" example(1)
|
|
// @Param note body note.Request true "便签内容"
|
|
// @Success 200 {object} model.Note
|
|
// @Failure 400 {object} httpx.ErrorResponse "参数无效或 id 无效"
|
|
// @Failure 404 {object} httpx.ErrorResponse "记录不存在"
|
|
// @Failure 500 {object} httpx.ErrorResponse
|
|
// @Security BearerAuth
|
|
// @Failure 401 {object} httpx.ErrorResponse "未登录或登录已过期"
|
|
// @Failure 403 {object} httpx.ErrorResponse "账号已被禁用"
|
|
// @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: "参数无效: " + 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 按 id 删除便签,成功时返回 204 且无响应体。
|
|
// @Tags notes
|
|
// @Produce json
|
|
// @Param id path int true "便签 ID" example(1)
|
|
// @Success 204 "删除成功"
|
|
// @Failure 400 {object} httpx.ErrorResponse "id 无效"
|
|
// @Failure 404 {object} httpx.ErrorResponse "记录不存在"
|
|
// @Failure 500 {object} httpx.ErrorResponse
|
|
// @Security BearerAuth
|
|
// @Failure 401 {object} httpx.ErrorResponse "未登录或登录已过期"
|
|
// @Failure 403 {object} httpx.ErrorResponse "账号已被禁用"
|
|
// @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: "记录不存在"})
|
|
return
|
|
}
|
|
c.Status(http.StatusNoContent)
|
|
}
|
|
}
|