166 lines
3.7 KiB
Go
166 lines
3.7 KiB
Go
package api
|
|
|
|
import (
|
|
"errors"
|
|
"log/slog"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"gorm.io/gorm"
|
|
|
|
"rill/internal/model"
|
|
)
|
|
|
|
const (
|
|
defaultPageSize = 20
|
|
maxPageSize = 100
|
|
)
|
|
|
|
type noteRequest struct {
|
|
Title string `json:"title" binding:"required,max=200"`
|
|
Content string `json:"content"`
|
|
}
|
|
|
|
func listNotes(db *gorm.DB) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
|
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", strconv.Itoa(defaultPageSize)))
|
|
if page < 1 {
|
|
page = 1
|
|
}
|
|
if pageSize < 1 || pageSize > maxPageSize {
|
|
pageSize = defaultPageSize
|
|
}
|
|
|
|
ctx := c.Request.Context()
|
|
var total int64
|
|
if err := db.WithContext(ctx).Model(&model.Note{}).Count(&total).Error; err != nil {
|
|
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 {
|
|
respondDBError(c, err)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"items": notes,
|
|
"total": total,
|
|
"page": page,
|
|
"page_size": pageSize,
|
|
})
|
|
}
|
|
}
|
|
|
|
func createNote(db *gorm.DB) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
var req noteRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"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 {
|
|
respondDBError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusCreated, note)
|
|
}
|
|
}
|
|
|
|
func getNote(db *gorm.DB) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
id, ok := parseID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
var note model.Note
|
|
if err := db.WithContext(c.Request.Context()).First(¬e, id).Error; err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "记录不存在"})
|
|
return
|
|
}
|
|
respondDBError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, note)
|
|
}
|
|
}
|
|
|
|
func updateNote(db *gorm.DB) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
id, ok := parseID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
var req noteRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "参数无效: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
ctx := c.Request.Context()
|
|
var note model.Note
|
|
if err := db.WithContext(ctx).First(¬e, id).Error; err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "记录不存在"})
|
|
return
|
|
}
|
|
respondDBError(c, err)
|
|
return
|
|
}
|
|
|
|
note.Title = req.Title
|
|
note.Content = req.Content
|
|
if err := db.WithContext(ctx).Save(¬e).Error; err != nil {
|
|
respondDBError(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, note)
|
|
}
|
|
}
|
|
|
|
func deleteNote(db *gorm.DB) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
id, ok := parseID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
result := db.WithContext(c.Request.Context()).Delete(&model.Note{}, id)
|
|
if result.Error != nil {
|
|
respondDBError(c, result.Error)
|
|
return
|
|
}
|
|
if result.RowsAffected == 0 {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "记录不存在"})
|
|
return
|
|
}
|
|
c.Status(http.StatusNoContent)
|
|
}
|
|
}
|
|
|
|
func parseID(c *gin.Context) (uint, bool) {
|
|
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
|
if err != nil || id == 0 {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "id 无效"})
|
|
return 0, false
|
|
}
|
|
return uint(id), true
|
|
}
|
|
|
|
func respondDBError(c *gin.Context, err error) {
|
|
slog.ErrorContext(c.Request.Context(), "数据库操作失败", "err", err, "path", c.Request.URL.Path)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "服务器内部错误"})
|
|
}
|