(go + react) 博客项目(一)
项目介绍
之前使用的技术栈是java和vue,由于一些原因需要使用go和react进行开发,虽然语言学习起来比较简单,但是想要做到非常熟练还要大量的练习,本着练习的想法打算写一个简单的博客项目
技术栈
前端:react、zustand、antd、react-router-dom
后端: go、goframe
数据库:mysql
提示:使用的node.js版本是20.16.0,go版本为1.19.13,mysql版本为9.0.1,请注意版本不一致可能引起的问题
设计说明
项目分为三个模块:文章(article)、用户(user)、评论(comment),设计上就是这三个模块的增删改查,计划使用markdown作为编辑引擎,由于是练手项目,一切从简,可能存在部分不合理的地方。
库表设计
用户 user
用户只设计用户名、密码,用户名做唯一性验证,其他不过度设计
CREATE TABLE landc_blog_user (
id int NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT 'Primary Key',
name VARCHAR(255) NOT NULL COMMENT '用户名',
password VARCHAR(255) NOT NULL COMMENT '用户密码'
) COMMENT '用户表';
文章 article
文章主要包含三部分:作者、标题、内容
CREATE TABLE landc_blog_article (
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '文章ID',
title VARCHAR(255) NOT NULL COMMENT '文章标题',
content TEXT NOT NULL COMMENT '文章内容',
author_id INT NOT NULL COMMENT '作者ID'
) COMMENT = '博客文章表';
评论 comment
评论复杂一点,需要支持嵌套回复,所以需要特殊设计,除文章id、用户id、评论内容这三个,还额外增加了父评论id字段
CREATE TABLE landc_blog_comment (
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '评论ID',
article_id INT NOT NULL COMMENT '文章ID',
author_id INT NOT NULL COMMENT '用户ID',
content TEXT NOT NULL COMMENT '评论内容',
parent_id INT DEFAULT NULL COMMENT '父评论ID(用于回复)'
) COMMENT = '博客评论表';
后端准备
后端初始化
后端使用的是goframe框架,它有自己的框架工具,提供了项目脚手架、代码生成工具,具体的安装教程官网上有,在这里不过多赘述,请确保安装完成之后继续跟进下面的操作。
使用脚手架创建项目模版,我将项目命名为’landc_blog’,创建命令就是:
gf init landc_blog -u
命令执行之后出现以下字样表示初始化完成

在执行命令的文件夹下也会出现以项目名为命名的文件夹:

直接使用goland或者vscode打开项目即可。
提示:goframe框架生成的项目结构比较复杂,可先在官网了解目录结构之后再进行后续开发。
自动生成dao层代码
在项目目录的hack文件夹下存在一个配置文件:config.yaml,这个配置文件主要是框架工具的配置文件。暂时不需要关心生成的内容,我们要在文件中配置一下自己的数据库,最后的配置文件内容如下方所示:
gfcli:
docker:
build: "-a amd64 -s linux -p temp -ew"
tagPrefixes:
- my.image.pub/my-app
# 下方为自己添加的内容
gen:
dao:
- link: "mysql:root:12345678@tcp(127.0.0.1:3306)/landc_blog"
之后在项目文件夹下执行命令:
gf gen dao
就能够看到以下输出:

至此我们的dao层代码生成结束
目录清理
goframe自动生成了一个helloworld的项目,现在要将其中的一些内容清理掉,在api文件夹下,直接将hello包删除就好了

之后是/internal/controller包下面,同样删掉"hello"包

最后是在/internal/cmd包中有一个"cmd.go"文件,这个文件里是http server的配置信息,当前配置了helloworld的路由信息,我们要把它去掉
关于这里更多的信息,建议去goframe官网了解Web服务开发模块.这样,我们就清理好了后端项目,可以开始我们的代码编写了。
后端接口设计
后端接口无非就是增删改查,得益于gf框架的代码生成功能,我们设计出接口对应的Req和Res就可以生成对应的接口,但是gf的代码生成有比较严格的规范,希望大家命名时和我保持一致,如果对gf有所了解可以自行修改。
文章 article
文章除单个文章的CRUD外,还需要列表的获取。首先在/api包下面创建article文件夹,之后在article下面创建v1文件夹
创建文章 CreateArticle
创建文章不需要多说什么,请求中除了主键id,其他的都是需要的,返回中就返回插入数据之后的id吧,方便前端再查询展示。在/api/article/v1文件夹下创建文件"create_article.go",编写创建文章的Req和Res
type CreateArticleReq struct {
g.Meta `path:"/CreateArticle" method:"post"`
Title string `json:"title"`
Content string `json:"content"`
AuthorId int `json:"authorId"`
}
type CreateArticleRes struct {
Id int `json:"id"`
}
修改文章 ModifyArticle
修改文章和创建文章需要的请求数据相同,只是多加了一个文章的id用来定位,返回内容设置为一个布尔值和字符串,用来表示成功或者失败以及失败原因。在/api/article/v1文件夹中创建"modify_article.go"文件
type ModifyArticleReq struct {
g.Meta `path:"/ModifyArticle" method:"post"`
Id int `json:"id"`
Title string `json:"title"`
Content string `json:"content"`
AuthorId int `json:"authorId"`
}
type ModifyArticleRes struct {
Success bool `json:"success"`
Info string `json:"info"`
}
删除文章 DeleteArticle
删除文章需要用户id和文章id,返回和修改文章相同。在/api/article/v1文件夹中创建"delete_article.go"文件
type DeleteArticleReq struct {
g.Meta `path:"/DeleteArticle" method:"post"`
Id int `json:"id"`
AuthorId int `json:"authorId"`
}
type DeleteArticleRes struct {
Success bool `json:"success"`
Info string `json:"info"`
}
获取文章列表 DescribeArticleList
获取文章列表的请求需要复用,一个是普通用户获取,一个是作者自己获取,因此需要用户id和关键词两个参数,关键词主要用来做模糊搜索。返回就是返回列表,既然存在列表首先要有item,item不搞太复杂,就文章id、文章标题以及用户名即可。在/api/article/v1文件夹中创建"describe_article_list.go"文件
type DescribeArticleListReq struct {
g.Meta `path:"/DescribeArticleList" method:"post"`
Id int `json:"id"`
SearchKey string `json:"searchKey"`
}
type DescribeArticleListRes struct {
ArticleList []ArticleListItem `json:"articleList"`
}
type ArticleListItem struct {
Id int `json:"id"`
Title string `json:"title"`
AuthorName string `json:"authorName"`
}
获取文章内容 DescribeArticle
获取文章内容只需要一个id就够了,返回则需要文章和用户信息都返回,由于信息比较少,可以集成在一起,如果信息多的话,建议分开写。在/api/article/v1文件夹中创建"describe_article.go"文件
type DescribeArticleReq struct {
g.Meta `path:"/DescribeArticle" method:"post"`
Id int `json:"id"`
}
type DescribeArticleRes struct {
Id int `json:"id"`
Title string `json:"title"`
Content string `json:"content"`
AuthorId int `json:"authorId"`
AuthorName string `json:"authorName"`
}
用户 user
针对用户我们设计的内容同样不复杂,也是只有CRUD,用户需要一个登录接口,但是登录接口可以通过复用查询接口实现,因为查询用户信息接口对我们来说是不需要的,毕竟用户只有三个字段,并不存在查询用户的场景。在/api文件夹下创建"user"文件夹,在"user"文件夹下创建"v1"文件夹
查询/登录 DescribeUser
由于我们使用这个接口进行登录,所以需要用户名和密码,返回只要返回用户的id就好,因为用户只有这三个字段,而两个是从前端拿取的。在api/user/v1文件夹下创建文件"describe_user.go"
type DescribeUserReq struct {
g.Meta `path:"/DescribeUser" method:"post"`
Name string `json:"name"`
Password string `json:"password"`
}
type DescribeUserRes struct {
Id int `json:"id"`
}
创建/注册 CreateUser
创建其实和登录一样,用户输入用户名和密码,后端校验,返回id。在api/user/v1文件夹下创建文件"create_user.go"
type CreateUserReq struct {
g.Meta `path:"/CreateUser" method:"post"`
Name string `p:"name"`
Password string `p:"password"`
}
type CreateUserRes struct {
Id int `json:"id"`
}
修改用户 ModifyUser
修改用户需要用户的三个字段,返回和之前并无不同。在api/user/v1文件夹下创建文件"modify_user.go"
type ModifyUserReq struct {
g.Meta `path:"/ModifyUser" method:"post"`
Id int `json:"id"`
Name string `json:"name"`
Password string `json:"password"`
}
type ModifyUserRes struct {
Success bool `json:"success"`
Info string `json:"info"`
}
删除用户 DeleteUser
虽然这个接口不一定会用到,但是它比较简单,因此实现也无妨。在api/user/v1文件夹下创建文件"delete_user.go"
type DeleteUserReq struct {
g.Meta `path:"/DeleteUser" method:"post"`
Id int `json:"id"`
}
type DeleteUserRes struct {
Success bool `json:"success"`
Info string `json:"info"`
}
评论 comment
评论内容就更简单了,只需要三个接口,创建、删除、获取列表,不建议修改评论内容,推荐先删除再创建。在/api文件夹下创建"comment",并且在"comment"下创建"v1"文件夹。
创建评论 CreateComment
仍旧是老套路,除了id外,其他字段都是需要的。在/api/comment/v1文件夹下创建"create_comment.go"文件
type CreateCommentReq struct {
g.Meta `path:"/CreateComment" method:"post"`
ArticleId int `json:"articleId"`
AuthorId int `json:"authorId"`
Content string `json:"content"`
ParentId int `json:"parentId" `
}
type CreateCommentRes struct {
Id int `json:"id"`
}
删除评论
删除评论需要id和用户id,返回仍旧同之前。在/api/comment/v1文件夹下创建"delete_comment.go"文件
type DeleteCommentReq struct {
g.Meta `path:"/DeleteComment" method:"post"`
Id int `json:"id"`
AuthorId int `json:"authorId"`
}
type DeleteCommentRes struct {
Success bool `json:"success"`
Info string `json:"info"`
}
获取评论列表 DescribeCommentList
获取评论列表的请求仅需要文章id就可以了,但是返回内容是一个值得考虑的问题,实现的方式有很多种,我才用的方式是嵌套返回,item中存在childrenList用来存放它的回复。
type DescribeCommentListReq struct {
g.Meta `path:"/DescribeCommentList" method:"post"`
Aid int `json:"aid"`
}
type DescribeCommentListRes struct {
CommentList []CommentItem `json:"commentList"`
}
type CommentItem struct {
Id int `json:"id"`
AuthorId int `json:"authorId"`
AuthorName int `json:"authorName"`
Content string `json:"content"`
ChildrenList []CommentItem `json:"childrenList"`
}
生成接口gen ctrl
在项目文件夹下输入以下命令,就可以在/internal/controller文件夹中生成我们的接口
gf gen ctrl
看到如下内容即算完成
结语
项目第一期的内容就到这里了,主要是利用gf框架的代码生成工具做了一些准备,篇幅已经挺长了,下一篇会进行后端代码开发,很快就发出来,发送之后我会把链接补充在这里。另外关于项目实现上,还是说这次仅仅是一个简单实现,有很多会导致bug和信息泄漏的地方,大家仅做学习框架使用,请不要实际使用。
大家如果有什么疑问或者建议,欢迎提出,祝大家1024程序员节快乐!
更多推荐
所有评论(0)