导读:本期聚焦于小伙伴创作的《如何使用Golang实现简单博客标签功能分类文章并展示》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何使用Golang实现简单博客标签功能分类文章并展示》有用,将其分享出去将是对创作者最好的鼓励。

博客标签功能是实现文章分类展示的核心模块,通过标签可以将同主题的文章聚合在一起,方便用户快速定位感兴趣的内容。使用Golang实现该功能需要处理好文章与标签的多对多关系,同时完成对应的接口开发和前端展示逻辑。

如何使用Golang实现简单博客标签功能分类文章并展示

数据库表结构设计

文章和标签是典型的多对多关系,一篇文章可以属于多个标签,一个标签也可以关联多篇文章,因此需要设计三张表:

  • 文章表:存储文章的基础信息
  • 标签表:存储标签的基础信息
  • 文章标签关联表:存储文章和标签的对应关系

下面是具体的表结构SQL定义:

-- 文章表
CREATE TABLE article (
    id INT PRIMARY KEY AUTO_INCREMENT,
    title VARCHAR(200) NOT NULL COMMENT '文章标题',
    content TEXT NOT NULL COMMENT '文章内容',
    create_time DATETIME NOT NULL COMMENT '创建时间'
);

-- 标签表
CREATE TABLE tag (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL UNIQUE COMMENT '标签名称'
);

-- 文章标签关联表
CREATE TABLE article_tag (
    article_id INT NOT NULL COMMENT '文章ID',
    tag_id INT NOT NULL COMMENT '标签ID',
    PRIMARY KEY (article_id, tag_id),
    FOREIGN KEY (article_id) REFERENCES article(id) ON DELETE CASCADE,
    FOREIGN KEY (tag_id) REFERENCES tag(id) ON DELETE CASCADE
);

核心结构体定义

在Golang中需要定义对应的结构体来映射数据库表,同时方便后续的数据操作:

package model

import "time"

// 文章结构体
type Article struct {
    ID         int       `json:"id"`
    Title      string    `json:"title"`
    Content    string    `json:"content"`
    CreateTime time.Time `json:"create_time"`
    Tags       []Tag     `json:"tags"` // 关联标签列表
}

// 标签结构体
type Tag struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

// 文章标签关联结构体
type ArticleTag struct {
    ArticleID int `json:"article_id"`
    TagID     int `json:"tag_id"`
}

标签与文章关联逻辑实现

新增文章时绑定标签

新增文章时需要同时处理标签的创建和关联关系,逻辑如下:先插入文章记录,再处理标签,如果标签不存在则先创建,最后插入关联记录。

package service

import (
    "database/sql"
    "errors"
    "time"
    "your_project/model"
)

type ArticleService struct {
    DB *sql.DB
}

// 新增文章并绑定标签
func (s *ArticleService) CreateArticle(title, content string, tagNames []string) (int, error) {
    // 开启事务
    tx, err := s.DB.Begin()
    if err != nil {
        return 0, err
    }
    defer tx.Rollback()

    // 插入文章
    result, err := tx.Exec("INSERT INTO article (title, content, create_time) VALUES (?, ?, ?)",
        title, content, time.Now())
    if err != nil {
        return 0, err
    }
    articleID, err := result.LastInsertId()
    if err != nil {
        return 0, err
    }

    // 处理标签和关联
    for _, tagName := range tagNames {
        // 查询标签是否存在
        var tagID int
        err := tx.QueryRow("SELECT id FROM tag WHERE name = ?", tagName).Scan(&tagID)
        if err != nil && err != sql.ErrNoRows {
            return 0, err
        }
        // 标签不存在则创建
        if err == sql.ErrNoRows {
            res, err := tx.Exec("INSERT INTO tag (name) VALUES (?)", tagName)
            if err != nil {
                return 0, err
            }
            tagID64, err := res.LastInsertId()
            if err != nil {
                return 0, err
            }
            tagID = int(tagID64)
        }
        // 插入关联记录
        _, err = tx.Exec("INSERT INTO article_tag (article_id, tag_id) VALUES (?, ?)", articleID, tagID)
        if err != nil {
            return 0, err
        }
    }

    // 提交事务
    if err := tx.Commit(); err != nil {
        return 0, err
    }
    return int(articleID), nil
}

根据标签查询文章列表

查询某个标签下的所有文章时,需要通过关联表进行连表查询:

// 根据标签ID查询文章列表
func (s *ArticleService) GetArticlesByTagID(tagID int) ([]model.Article, error) {
    rows, err := s.DB.Query(`
        SELECT a.id, a.title, a.content, a.create_time 
        FROM article a 
        JOIN article_tag at ON a.id = at.article_id 
        WHERE at.tag_id = ? 
        ORDER BY a.create_time DESC
    `, tagID)
    if err != nil {
        return nil, err
    }
    defer rows.Close()

    var articles []model.Article
    for rows.Next() {
        var article model.Article
        err := rows.Scan(&article.ID, &article.Title, &article.Content, &article.CreateTime)
        if err != nil {
            return nil, err
        }
        // 查询文章对应的所有标签
        tagRows, err := s.DB.Query(`
            SELECT t.id, t.name 
            FROM tag t 
            JOIN article_tag at ON t.id = at.tag_id 
            WHERE at.article_id = ?
        `, article.ID)
        if err != nil {
            return nil, err
        }
        defer tagRows.Close()
        for tagRows.Next() {
            var tag model.Tag
            tagRows.Scan(&tag.ID, &tag.Name)
            article.Tags = append(article.Tags, tag)
        }
        articles = append(articles, article)
    }
    return articles, nil
}

接口开发与前端展示

后端接口实现

使用Golang标准库的net/http包开发简单的接口,提供标签文章查询能力:

package main

import (
    "encoding/json"
    "net/http"
    "strconv"
    "your_project/service"
)

func main() {
    db := initDB() // 初始化数据库连接的函数,需自行实现
    articleService := &service.ArticleService{DB: db}

    // 根据标签ID查询文章的接口
    http.HandleFunc("/api/articles/by-tag", func(w http.ResponseWriter, r *http.Request) {
        tagIDStr := r.URL.Query().Get("tag_id")
        if tagIDStr == "" {
            http.Error(w, "缺少tag_id参数", http.StatusBadRequest)
            return
        }
        tagID, err := strconv.Atoi(tagIDStr)
        if err != nil {
            http.Error(w, "tag_id参数格式错误", http.StatusBadRequest)
            return
        }
        articles, err := articleService.GetArticlesByTagID(tagID)
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(articles)
    })

    http.ListenAndServe(":8080", nil)
}

前端展示逻辑

前端可以通过请求接口获取标签对应的文章列表,然后渲染到页面中,示例代码如下:

// 获取URL中的标签ID参数
function getTagID() {
    const params = new URLSearchParams(window.location.search);
    return params.get('tag_id');
}

// 请求接口并渲染文章列表
async function loadArticlesByTag() {
    const tagID = getTagID();
    if (!tagID) {
        document.getElementById('article-list').innerHTML = '未指定标签';
        return;
    }
    try {
        const res = await fetch(`/api/articles/by-tag?tag_id=${tagID}`);
        const articles = await res.json();
        const listContainer = document.getElementById('article-list');
        if (articles.length === 0) {
            listContainer.innerHTML = '该标签下暂无文章';
            return;
        }
        let html = '';
        articles.forEach(article => {
            html += `
                <div class="article-item">
                    <h3>${article.title}</h3>
                    <p>发布时间:${new Date(article.create_time).toLocaleString()}</p>
                    <p>所属标签:${article.tags.map(tag => tag.name).join('、')}</p>
                    <a href="/article?id=${article.id}">查看详情</a>
                </div>
            `;
        });
        listContainer.innerHTML = html;
    } catch (err) {
        console.error('加载文章失败:', err);
    }
}

// 页面加载完成后执行
window.onload = loadArticlesByTag;

注意事项

  • 操作文章和标签的关联时建议使用事务,避免数据不一致的问题
  • 标签名称建议添加唯一索引,防止重复创建相同标签
  • 查询文章列表时如果数据量较大,需要添加分页逻辑,避免一次性返回过多数据
  • 前端展示时可以对标签进行高亮处理,提升用户体验

Golang博客标签功能文章分类数据库操作Web开发修改时间:2026-07-15 00:30:39

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。