Files
rill/internal/api/api.go
T
2026-09-19 17:26:07 +08:00

115 lines
3.2 KiB
Go

// Package api 提供 HTTP 接口路由。
package api
import (
"errors"
"net/http"
"path"
"strconv"
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
"gorm.io/gorm"
"rill/internal/database"
)
// RegisterRoutes 注册 API 路由。
func RegisterRoutes(rg *gin.RouterGroup, db *gorm.DB) {
rg.GET("/health", health(db))
swagger := rg.Group("/swagger")
{
swagger.GET("", func(c *gin.Context) {
c.Redirect(http.StatusFound, path.Join("/", rg.BasePath(), "swagger", "index.html"))
})
swagger.GET("/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, ginSwagger.URL("doc.json")))
}
notes := rg.Group("/notes")
{
notes.GET("", listNotes(db))
notes.POST("", createNote(db))
notes.GET("/:id", getNote(db))
notes.PUT("/:id", updateNote(db))
notes.DELETE("/:id", deleteNote(db))
}
users := rg.Group("/users")
{
users.GET("", listUsers(db))
users.POST("", createUser(db))
users.GET("/:id", getUser(db))
users.PUT("/:id", updateUser(db))
users.DELETE("/:id", deleteUser(db))
}
userGroups := rg.Group("/user-groups")
{
userGroups.GET("", listUserGroups(db))
userGroups.POST("", createUserGroup(db))
userGroups.GET("/:id", getUserGroup(db))
userGroups.PUT("/:id", updateUserGroup(db))
userGroups.DELETE("/:id", deleteUserGroup(db))
}
}
func parsePagination(c *gin.Context) (int, int) {
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
}
return page, pageSize
}
// respondGetError 查询类错误:记录不存在返回 404,其余按数据库错误处理。
func respondGetError(c *gin.Context, err error) {
if errors.Is(err, gorm.ErrRecordNotFound) {
c.JSON(http.StatusNotFound, ErrorResponse{Error: "记录不存在"})
return
}
respondDBError(c, err)
}
// respondDuplicateOrDBError 写入类错误:唯一约束冲突返回 409,其余按数据库错误处理。
func respondDuplicateOrDBError(c *gin.Context, err error, duplicateMsg string) {
if errors.Is(err, gorm.ErrDuplicatedKey) {
c.JSON(http.StatusConflict, ErrorResponse{Error: duplicateMsg})
return
}
respondDBError(c, err)
}
// ErrorResponse 统一错误响应。
type ErrorResponse struct {
Error string `json:"error" example:"记录不存在"`
}
// HealthResponse 健康检查响应。
type HealthResponse struct {
Status string `json:"status" example:"ok"`
Error string `json:"error,omitempty" example:"数据库不可用"`
}
// @Summary Health check
// @Description 检查服务与数据库连接状态;数据库不可用时返回 503。
// @Tags system
// @Produce json
// @Success 200 {object} api.HealthResponse
// @Failure 503 {object} api.HealthResponse
// @Router /health [get]
func health(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
if err := database.Ping(c.Request.Context(), db); err != nil {
c.JSON(http.StatusServiceUnavailable, HealthResponse{Status: "error", Error: "数据库不可用"})
return
}
c.JSON(http.StatusOK, HealthResponse{Status: "ok"})
}
}