文章目录
- AI书签管理工具开发全记录(四):后端服务搭建与API实现
- 前言 📝
- 1. 后端框架选型 🛠️
- 2. 项目结构优化 📁
- 3. API路由设计 🧭
- 分类管理
- 书签管理
- 4. 数据模型定义 💾
- 分类模型(category.go)
- 书签模型(bookmark.go)
- 5. API服务实现 💻
- 服务结构体定义(server.go)
- 分类控制器实现
- 书签控制器实现
- 6. 服务启动集成 🚀
- 7. Swagger文档集成 📚
- 8. 测试运行 ✅
- 总结 📚
AI书签管理工具开发全记录(四):后端服务搭建与API实现
前言 📝
在上一篇文章中,我们为项目添加了完善的日志系统,使用zap和lumberjack实现了高性能的日志记录、文件切割和归档功能。现在,我们将基于Gin框架搭建后端服务,实现书签和分类的增删改查(CRUD)操作,并通过Swagger生成API文档。
1. 后端框架选型 🛠️
在Go语言中,有许多优秀的Web框架可供选择,如Gin、Echo、Beego等。经过调研,本项目选择了Gin框架,因为它具有以下特点:
- 高性能:基于HttpRouter,性能接近原生HTTP
- 轻量级:代码简洁,易于上手
- 丰富的中间件:支持各种中间件,如日志、恢复、认证等
- 良好的社区支持:拥有活跃的社区和丰富的插件
此外,我们还将使用Swagger来自动生成API文档,方便前后端协作。
# 安装依赖
go get github.com/gin-gonic/gin
go get github.com/swaggo/gin-swagger
go get github.com/swaggo/files
2. 项目结构优化 📁
在开始编写API代码之前,我们先对项目结构进行优化,使其更清晰合理:
aibookmark/
├── cmd/ # 命令行入口
├── internal/ # 内部包
│ ├── api/ # API路由和控制器
│ ├── models/ # 数据模型
│ ├── server/ # 服务启动逻辑
│ ├── utils/ # 工具函数(配置、数据库等)
│ └── log/ # 日志系统
├── docs/ # Swagger文档
└── go.mod
3. API路由设计 🧭
我们设计了以下API路由,用于管理书签和分类:
分类管理
POST /api/categories
:创建分类GET /api/categories
:获取分类列表(分页)GET /api/categories/:id
:获取单个分类PUT /api/categories/:id
:更新分类DELETE /api/categories/:id
:删除分类
书签管理
POST /api/bookmarks
:创建书签GET /api/bookmarks
:获取书签列表(分页,支持按分类过滤)GET /api/bookmarks/:id
:获取单个书签PUT /api/bookmarks/:id
:更新书签DELETE /api/bookmarks/:id
:删除书签
4. 数据模型定义 💾
在internal/models
包中,我们定义了两个核心模型:分类(Category)和书签(Bookmark)。
分类模型(category.go)
package models
import "time"
type Category struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"unique;not null"`
Description string
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP"`
}
书签模型(bookmark.go)
package models
import "time"
type Bookmark struct {
ID uint `gorm:"primaryKey"`
Title string `gorm:"not null"`
URL string `gorm:"not null"`
Description string
CategoryID uint
Category Category `gorm:"foreignKey:CategoryID"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP"`
}
同时,我们定义了用于请求和响应的结构体(bookmark_request.go和category_request.go),以便于参数绑定和序列化。
5. API服务实现 💻
在internal/api
包中,我们实现了API的路由和控制器逻辑。
服务结构体定义(server.go)
package api
import (
"github.com/ciclebyte/aibookmark/internal/models"
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
"gorm.io/gorm"
)
type Server struct {
db *gorm.DB
router *gin.Engine
}
func NewServer(db *gorm.DB) *Server {
server := &Server{db: db}
router := gin.Default()
// 添加Swagger路由
router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
// API路由分组
api := router.Group("/api")
{
category := api.Group("/categories")
{
category.POST("", server.createCategory)
category.GET("", server.listCategories)
category.GET("/:id", server.getCategory)
category.PUT("/:id", server.updateCategory)
category.DELETE("/:id", server.deleteCategory)
}
bookmark := api.Group("/bookmarks")
{
bookmark.POST("", server.createBookmark)
bookmark.GET("", server.listBookmarks)
bookmark.GET("/:id", server.getBookmark)
bookmark.PUT("/:id", server.updateBookmark)
bookmark.DELETE("/:id", server.deleteBookmark)
}
}
server.router = router
return server
}
func (s *Server) Start(address string) error {
return s.router.Run(address)
}
分类控制器实现
以创建分类为例,我们实现了以下逻辑:
// CreateCategory 创建新分类
func (s *Server) createCategory(c *gin.Context) {
var req models.CreateCategoryRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(400, gin.H{"error": "Invalid request data"})
return
}
// 检查分类名称是否已存在
var existingCategory models.Category
if err := s.db.Where("name = ?", req.Name).First(&existingCategory).Error; err == nil {
c.JSON(409, gin.H{"error": "Category with this name already exists"})
return
}
category := models.Category{
Name: req.Name,
Description: req.Description,
}
if err := s.db.Create(&category).Error; err != nil {
c.JSON(500, gin.H{"error": "Failed to create category"})
return
}
c.JSON(201, category)
}
书签控制器实现
以获取书签列表为例,我们实现了分页和按分类过滤的功能:
// ListBookmarks 获取书签列表
func (s *Server) listBookmarks(c *gin.Context) {
var bookmarks []models.Bookmark
pageStr := c.DefaultQuery("page", "1")
sizeStr := c.DefaultQuery("size", "10")
categoryID := c.Query("category_id")
page, err := strconv.Atoi(pageStr)
if err != nil || page < 1 {
page = 1
}
size, err := strconv.Atoi(sizeStr)
if err != nil || size < 1 {
size = 10
}
query := s.db.Model(&models.Bookmark{})
if categoryID != "" {
query = query.Where("category_id = ?", categoryID)
}
offset := (page - 1) * size
if err := query.Offset(offset).Limit(size).Preload("Category").Find(&bookmarks).Error; err != nil {
c.JSON(500, gin.H{"error": "Failed to fetch bookmarks"})
return
}
c.JSON(200, gin.H{
"data": bookmarks,
"page": page,
"size": size,
"total": len(bookmarks),
})
}
6. 服务启动集成 🚀
在internal/server/server.go
中,我们集成了数据库初始化和服务启动逻辑:
package server
import (
"github.com/ciclebyte/aibookmark/internal/api"
"github.com/ciclebyte/aibookmark/internal/models"
"github.com/ciclebyte/aibookmark/internal/utils"
)
func StartServer(port string) error {
db, err := utils.GetGormDB()
if err != nil {
return err
}
// 自动迁移数据库
err = db.AutoMigrate(&models.Category{}, &models.Bookmark{})
if err != nil {
return err
}
server := api.NewServer(db)
return server.Start(":" + port)
}
在cmd/root.go
中,我们添加了serve
命令来启动服务:
// serveCmd 代表 serve 命令
var serveCmd = &cobra.Command{
Use: "serve",
Short: "启动API服务",
Run: func(cmd *cobra.Command, args []string) {
port, _ := cmd.Flags().GetString("port")
startGinServer(port)
},
}
func startGinServer(port string) {
if err := server.StartServer(port); err != nil {
log.Error("Failed to start server",
zap.String("operation", "server start"),
zap.Error(err))
os.Exit(1)
}
}
7. Swagger文档集成 📚
我们使用Swagger注解来生成API文档。在每个API方法上方,我们添加了相应的Swagger注释,例如:
// CreateCategory godoc
// @Summary 创建分类
// @Description 创建新的书签分类
// @Tags categories
// @Accept json
// @Produce json
// @Param category body models.CreateCategoryRequest true "分类信息"
// @Success 201 {object} models.Category
// @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /api/categories [post]
func (s *Server) createCategory(c *gin.Context) {
// ...
}
然后,在项目根目录下运行以下命令生成Swagger文档:
swag init -g internal/api/api.go
生成的文档位于docs
目录,我们可以通过/swagger/index.html
访问API文档。
8. 测试运行 ✅
启动服务:
go run main.go serve -p 8080
访问Swagger文档:
http://localhost:8080/swagger/index.html
通过Swagger UI,我们可以方便地测试各个API接口。
也可以访问doc.json
可以在apifox
中导入doc.json
,方便的对api服务进行测试
总结 📚
本文详细介绍了如何基于Gin框架搭建后端API服务,并实现书签和分类的CRUD功能。通过Swagger集成,我们实现了API文档的自动化生成,便于前后端协作。在下一篇文章中,我们将为搭建前端基础框架。