115 lines
3.2 KiB
Go
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"})
|
|
}
|
|
}
|