AI书签管理工具开发全记录(五):后端服务搭建与API实现

news2025/7/21 23:43:38

文章目录

  • 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接口。
image.png

也可以访问doc.json
image.png

可以在apifox中导入doc.json,方便的对api服务进行测试
image.png

image.png

总结 📚

本文详细介绍了如何基于Gin框架搭建后端API服务,并实现书签和分类的CRUD功能。通过Swagger集成,我们实现了API文档的自动化生成,便于前后端协作。在下一篇文章中,我们将为搭建前端基础框架。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2397624.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

netTAP 100:在机器人技术中将 POWERLINK 转换为 EtherNet/IP

工业机器人服务专家 年轻的 More Robots 公司成立仅一年多&#xff0c;但其在许多应用领域的专业技术已受到广泛欢迎。这是因为More Robots提供 360 度全方位服务&#xff0c;包括从高品质工业机器人和协作机器人到咨询和培训。这包括推荐适合特定任务或应用的机器人&#xff0…

多模态大语言模型arxiv论文略读(九十八)

Accelerating Pre-training of Multimodal LLMs via Chain-of-Sight ➡️ 论文标题&#xff1a;Accelerating Pre-training of Multimodal LLMs via Chain-of-Sight ➡️ 论文作者&#xff1a;Ziyuan Huang, Kaixiang Ji, Biao Gong, Zhiwu Qing, Qinglong Zhang, Kecheng Zhe…

EXCEL--累加,获取大于某个值的第一个数

一、函数 LET(data,A1:A5,cumSum,SCAN(0,data,LAMBDA(a,b,ab)),idx,MATCH(TRUE,cumSum>C1,0),INDEX(data,idx)) 二、函数拆解 1、LET函数&#xff1a;LET(name1, value1, [name2, value2, ...], calculation) name1, name2...&#xff1a;自定义的变量名&#xff08;需以字…

本地部署 DeepSeek R1(最新)【从下载、安装、使用和调用一条龙服务】

文章目录 一、安装 Ollama1.1 下载1.2 安装 二、下载 DeepSeek 模型三、使用 DeepSeek3.1 在命令行环境中使用3.2 在第三方软件中使用 一、安装 Ollama 1.1 下载 官方网址&#xff1a;Ollama 官网下载很慢&#xff0c;甚至出现了下载完显示 无法下载&#xff0c;需要授权 目…

win主机如何结束正在执行的任务进程并重启

最近遇到一个问题&#xff0c;一个java入库程序经常在运行了几个小时之后消息无法入库&#xff0c;由于已经没有研发人员来维护这个程序了&#xff0c;故此只能每隔一段时间来重启这个程序以保证一直有消息入库。 但是谁也不能保证一直有人去看这个程序&#xff0c;并且晚上也不…

maven中的maven-resources-plugin插件详解

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站 一、插件定位与核心功能 maven-resources-plugin是Maven构建工具的核心插件之一&#xff0c;主要用于处理项目中的资源文件&#xff08;如…

ROS云课基础篇-01-Linux-250529

ROS云课基础篇收到了很多反馈&#xff0c;正面评价比例高&#xff0c;还有很多朋友反馈需要写更具体一点。 ROS云课基础篇极简复习-C、工具、导航、巡逻一次走完-CSDN博客 于是&#xff0c;有了这篇以及之后的案例&#xff0c;案例均已经测试过8年&#xff0c;但没有在博客公…

深入了解 C# 异步编程库 AsyncEx

在现代应用程序开发中&#xff0c;异步编程已经成为提升性能和响应能力的关键&#xff0c;尤其在处理网络请求、I/O 操作和其他耗时任务时&#xff0c;异步编程可以有效避免阻塞主线程&#xff0c;提升程序的响应速度和并发处理能力。C# 提供了内建的异步编程支持&#xff08;通…

Vulhub靶场搭建(Ubuntu)

前言&#xff1a;Vulhub 是一个开源的漏洞靶场平台&#xff0c;全称是 Vulhub: Vulnerable Web Application Environments&#xff0c;主要用于学习和复现各类 Web 安全漏洞。它的核心特征是通过 Docker 环境快速搭建出带有特定漏洞的靶场系统&#xff0c;适合渗透测试学习者、…

C++:参数传递方法(Parameter Passing Methods)

目录 1. 值传递&#xff08;Pass by Value&#xff09; 2. 地址传递&#xff08;Pass by Address&#xff09; 3. 引用传递&#xff08;Pass by Reference&#xff09; 数组作为函数参数&#xff08;Array as Parameter&#xff09; 数组作为函数返回值 什么是函数&#xff…

大语言模型的推理能力

2025年&#xff0c;各种会推理的AI模型如雨后春笋般涌现&#xff0c;比如ChatGPT o1/o3/o4、DeepSeek r1、Gemini 2 Flash Thinking、Claude 3.7 Sonnet (Extended Thinking)。 对于工程上一些问题比如复杂的自然语言转sql&#xff0c;我们可能忍受模型的得到正确答案需要更多…

Redis的安装与使用

网址&#xff1a;Spring Data Redis 安装包&#xff1a;Releases tporadowski/redis GitHub 解压后 在安装目录中打开cmd 打开服务&#xff08;注意&#xff1a;每次客户端连接都有先打开服务&#xff01;&#xff01;&#xff01;&#xff09; 按ctrlC退出服务 客户端连接…

2024年数维杯国际大学生数学建模挑战赛C题时间信号脉冲定时噪声抑制与大气时延抑制模型解题全过程论文及程序

2024年数维杯国际大学生数学建模挑战赛 C题 时间信号脉冲定时噪声抑制与大气时延抑制模型 原题再现&#xff1a; 脉冲星是一种快速旋转的中子星&#xff0c;具有连续稳定的旋转&#xff0c;因此被称为“宇宙灯塔”。脉冲星的空间观测在深空航天器导航和时间标准维护中发挥着至…

C# 控制台程序获取用户输入数据验证 不合规返回重新提示输入

在 C# 控制台程序中实现输入验证并循环重试&#xff0c;可以通过以下方式实现高效且用户友好的交互。以下是包含多种验证场景的完整解决方案&#xff1a; 一、通用输入验证框架 public static T GetValidInput<T>(string prompt, Func<string, (bool IsValid, T Val…

TDengine 运维——巡检工具(安装前检查)

简介 本文档旨在介绍 TDengine 安装部署前后配套的巡检工具。 相关工具的功能简介&#xff1a; 工具名称功能简介安装前检查部署前对 TDengine 安装部署的依赖要素进行安装前检查安装前预配置部署前对 TDengine 安装部署的依赖要素进行安装前预配置安装部署指定环境安装部署…

【Linux】权限chmod命令+Linux终端常用快捷键

目录 linux中权限表示形式 解析标识符 权限的数字序号 添加权限命令chmod 使用数字表示法设置权限 使用符号表示法设置权限 linux终端常用快捷键 &#x1f525;个人主页 &#x1f525; &#x1f608;所属专栏&#x1f608; 在 Linux 系统里&#xff0c;权限管理是保障系…

Java八股文智能体——Agent提示词(Prompt)

这个智能体能够为正在学习Java八股文的同学提供切实帮助&#xff1a;不仅可以帮你优化答案表述&#xff0c;还能直接解答八股文相关问题——它会以面试者的视角&#xff0c;给出贴合求职场景的专业回答。 将以下内容发送给任何一个LLM&#xff0c;他会按照你提示词的内容&…

Go语言的context

Golang context 实现原理 本篇文章是基于小徐先生的文章的修改和个人注解&#xff0c;要查看原文可以点击上述的链接查看 目前我这篇文章的go语言版本是1.24.1 context上下文 context被当作第一个参数&#xff08;官方建议&#xff09;&#xff0c;并且不断的传递下去&…

数据库原理 试卷

以下是某高校教学管理系统的毕业论文指导ER图&#xff0c;数据信息&#xff1a;一名教师指导多名学生&#xff0c;一名学生只能选择一名教师&#xff0c;试分析完成以下各题&#xff0c;如用SQL命令完成的&#xff0c;在SQL Server2008验证后把答案写在题目的下方。 图1 毕业论…

【Qt开发】对话框

目录 1&#xff0c;对话框的介绍 2&#xff0c;Qt内置对话框 2-1&#xff0c;消息对话框QMessageBox 2-2&#xff0c;颜色对话框QColorDialog 2-3&#xff0c;文件对话框QFileDialog 2-4&#xff0c;字体对话框QFontDialog 2-5&#xff0c;输入对话框QInputDialog 1&…