AI书签管理工具开发全记录(五):后端服务搭建与API实现
文章目录
- 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 modelsimport "time"type Category struct {ID uint `gorm:"primaryKey"`Name string `gorm:"unique;not null"`Description stringCreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP"`
}
书签模型(bookmark.go)
package modelsimport "time"type Bookmark struct {ID uint `gorm:"primaryKey"`Title string `gorm:"not null"`URL string `gorm:"not null"`Description stringCategoryID uintCategory 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 apiimport ("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.DBrouter *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 = routerreturn server
}func (s *Server) Start(address string) error {return s.router.Run(address)
}
分类控制器实现
以创建分类为例,我们实现了以下逻辑:
// CreateCategory 创建新分类
func (s *Server) createCategory(c *gin.Context) {var req models.CreateCategoryRequestif err := c.ShouldBindJSON(&req); err != nil {c.JSON(400, gin.H{"error": "Invalid request data"})return}// 检查分类名称是否已存在var existingCategory models.Categoryif 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.BookmarkpageStr := 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) * sizeif 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 serverimport ("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文档的自动化生成,便于前后端协作。在下一篇文章中,我们将为搭建前端基础框架。