简介
这个项目是一个简单的社区话题系统。gitee地址:字节青训营CommunityTopics:(gitee.com)
我使用了 MVC(Model-View-Controller)架构模式,其中 controller
包负责控制请求处理,service
包负责处理业务逻辑,repository
包负责与数据库交互。
main
包主要负责启动服务和定义路由,具体的业务逻辑在其他包中实现。- 为了保证代码的可扩展性和复用性,项目采用了单例模式,例如
NewTopicDaoInstance
、NewUserDaoInstance
和NewPostDaoInstance
函数,确保了在全局范围内只有一个实例,避免了多次创建实例和资源浪费。 - 使用了并发方式来提高数据查询的效率,例如在
prepareInfo()
方法中并发获取话题信息和帖子列表,通过sync.WaitGroup
来等待两个操作完成后再进行后续处理。
后续会添加jwt验证,并添加进代码仓库,我蒟蒻,有很多不好的地方还请带佬指点
结构:
-
main
:- 负责项目的入口,初始化 Gin 引擎和路由。
- 定义了两个路由,分别处理帖子页面信息查询和帖子发布的请求。
"/community/page/get/:id"
路由处理查询帖子页面信息的请求,调用controller.QueryPageInfo
处理函数。"/community/post/publish"
路由处理帖子发布的请求,调用controller.PostPublish
处理函数。
-
controller
:- 负责处理 HTTP 请求和路由处理逻辑。
QueryPageInfo
函数:处理查询帖子页面信息的请求,调用service.QueryPageInfo
函数。PostPublish
函数:处理帖子发布的请求,调用service.PostPublish
函数。PageData
结构体:用于封装返回给客户端的 JSON 数据。
-
service
:- 定义了与帖子发布和页面信息查询相关的服务函数,处理具体的业务逻辑。
PostPublish
函数:用于发布帖子,调用NewPublishPostFlow
创建一个PublishPostFlow
实例,并调用其Do()
方法执行帖子发布的流程。NewPublishPostFlow
函数:用于创建一个PublishPostFlow
实例。PublishPostFlow
结构体:用于组织帖子发布的流程,包含了参数校验、帖子发布的数据库操作,以及返回帖子信息和可能的错误。QueryPageInfo
函数:用于查询帖子页面信息,调用NewQueryPageInfoFlow
创建一个QueryPageInfoFlow
实例,并调用其Do()
方法执行查询帖子页面信息的流程。NewQueryPageInfoFlow
函数:用于创建一个QueryPageInfoFlow
实例。QueryPageInfoFlow
结构体:用于组织查询帖子页面信息的流程,包含了参数校验、准备信息和组织页面信息等操作。
-
repository
:- 负责与数据库交互,包含了对话题、帖子和用户数据表的结构体和相应的数据库操作方法。
- 包含了
Topic
、Post
和User
结构体,分别代表话题、帖子和用户的数据模型。 - 包含了
TopicDao
、PostDao
和UserDao
结构体,用于执行对应数据表的数据库操作。 Init
函数:用于初始化数据库连接。
结构体说明:
PageData
:用于封装返回给客户端的 JSON 数据,包括Code
(返回状态码)、Msg
(返回消息)和Data
(返回数据)字段。Post
:代表帖子的数据模型,包含帖子的各个字段信息。Topic
:代表话题的数据模型,包含话题的各个字段信息。User
:代表用户的数据模型,包含用户的各个字段信息。PostInfo
:用于组织帖子信息,包括帖子本身的信息和发布该帖子的用户信息。TopicInfo
:用于组织话题信息,包括话题本身的信息和创建该话题的用户信息。PageInfo
:用于组织页面信息,包括话题信息和相关的帖子列表。
函数说明:
QueryPageInfo
:处理查询帖子页面信息的请求,调用service.QueryPageInfo
函数进行处理,并返回查询结果。PostPublish
:处理帖子发布的请求,调用service.PostPublish
函数进行帖子发布,并返回发布后的帖子信息和可能的错误。NewPublishPostFlow
:用于创建一个帖子发布的流程实例。PublishPostFlow.Do()
:执行帖子发布的流程,包括参数检查、帖子发布的数据库操作,并返回发布后的帖子信息和可能的错误。NewQueryPageInfoFlow
:用于创建一个查询帖子页面信息的流程实例。QueryPageInfoFlow.Do()
:执行查询帖子页面信息的流程,包括参数检查、准备信息和组织页面信息,并返回页面信息和可能的错误。Init
:用于初始化数据库连接,并返回数据库对象db
。
逻辑流程:
-
帖子发布逻辑:
main
包启动 Gin 引擎,并定义了两个路由"/community/page/get/:id"
和"/community/post/publish"
。- 当访问
"/community/page/get/:id"
路由时,Gin 引擎调用controller.QueryPageInfo
处理函数,处理查询帖子页面信息的请求。 - 当访问
"/community/post/publish"
路由时,Gin 引擎调用controller.PostPublish
处理函数,处理帖子发布的请求。 controller.PostPublish
调用service.PostPublish
函数,该函数用于发布帖子。service.PostPublish
创建了一个PublishPostFlow
实例,并调用其Do()
方法执行帖子发布的流程。PublishPostFlow
实例的Do()
方法检查参数,如用户 ID 和帖子内容的合法性,然后执行帖子发布的数据库操作,并返回发布后的帖子信息和可能的错误。
-
页面信息查询逻辑:
- 当访问
"/community/page/get/:id"
路由时,Gin 引擎调用controller.QueryPageInfo
处理函数,处理查询帖子页面信息的请求。 controller.QueryPageInfo
调用service.QueryPageInfo
函数,该函数用于查询帖子页面信息。service.QueryPageInfo
创建了一个QueryPageInfoFlow
实例,并调用其Do()
方法执行查询帖子页面信息的流程。QueryPageInfoFlow
实例的Do()
方法检查参数,如话题 ID 的合法性,然后调用prepareInfo()
方法准备查询帖子页面信息所需的数据。prepareInfo()
方法使用并发方式分别获取话题信息和帖子列表,并通过sync.WaitGroup
等待两个操作完成后再进行后续处理。然后获取相关的用户信息,并将话题信息和相关的帖子信息组合成PageInfo
结构体返回。
- 当访问
文件说明
server.go
使用 Gin 框架的 Go(Golang)Web 应用程序。它建立了一个简单的 Web 服务器,并提供两个端点来处理与社区话题系统相关的 HTTP 请求。下面对代码进行解析:
- 导入语句:
go
import (
"communityTopics/controller"
"communityTopics/repository"
"fmt"
"github.com/gin-gonic/gin"
)
在这部分代码中,导入了必要的包。"github.com/gin-gonic/gin" 是 Gin 框架,用于创建 Web 服务器;"communityTopics/controller" 和 "communityTopics/repository" ,分别包含了业务逻辑和数据访问操作。
main
函数:
go
func main() {
repository.Init() // 在这里初始化仓库(数据库或数据存储)。
engine := gin.Default() // 使用默认中间件(日志、恢复)创建一个新的 Gin 路由器。
// 定义一个用于获取社区页面信息的路由,根据话题 ID。
engine.GET("/community/page/get/:id", func(c *gin.Context) {
topicId := c.Param("id") // 从 URL 参数中提取话题 ID。
data := controller.QueryPageInfo(topicId) // 调用控制器函数来查询页面信息。
c.JSON(200, data) // 以 JSON 格式返回页面信息数据。
})
// 定义一个用于发布社区帖子的路由。
engine.POST("/community/post/publish", func(c *gin.Context) {
userId := "1" // 假设测试目的下的用户 ID。
topicId := c.Query("tid") // 从请求 URL 中提取 "tid" 查询参数。
rootId := c.Query("rid") // 从请求 URL 中提取 "rid" 查询参数。
content := c.PostForm("content") // 从请求体(表单数据)中提取 "content" 参数。
content = "我是测试评论" // 将内容设置为固定字符串 "我是测试评论"。可能是为了测试目的。
fmt.Println(userId, topicId, rootId, &content) // 打印提取的值(用于调试)。
// 调用控制器函数来发布帖子,并获取响应数据。
data := controller.PostPublish(userId, topicId, rootId, &content)
c.JSON(200, data) // 以 JSON 格式返回发布帖子的结果数据。
})
engine.Run() // 启动 Web 服务器,监听默认端口 8080。
}
代码展示了使用 Gin 框架的 Go Web 应用程序的基本结构。根据 controller
和 repository
包的实现,该应用程序可能处理社区话题信息的检索和发布新的社区帖子。GET
端点用于根据给定的话题 ID 获取页面信息,POST
端点用于发布帖子,并包含相关的信息,如用户 ID、话题 ID、根帖子 ID(如果是对先前帖子的回复)以及内容。
pageDate.go
代码定义了一个名为 PageData
的结构体,用于封装响应数据,通常用于在控制器中返回给客户端的数据。结构体包含三个字段:
Code
(类型为int64
):表示响应状态码,用于标识请求的处理结果,例如 200 表示成功,400 表示客户端错误,500 表示服务器错误等。Msg
(类型为string
):表示响应消息,通常用于向客户端返回与处理结果相关的一些文字信息,比如 "Success" 表示成功,"Bad Request" 表示客户端请求错误等。Data
(类型为interface{}
):表示响应数据的主体内容,它是一个空接口类型,因此可以存储任何类型的数据,包括基本数据类型、结构体、切片、映射等。
在该结构体中,字段标签(tag)json:"..."
用于指定在序列化为 JSON 数据时的字段名称。这样,在将结构体对象转换为 JSON 格式时,字段名称将按照标签指定的名称进行映射。
示例使用方式:
go
// 创建一个 PageData 实例,并设置相应的字段值。
response := PageData{
Code: 200,
Msg: "Success",
Data: map[string]string{
"key1": "value1",
"key2": "value2",
},
}
// 将 PageData 实例转换为 JSON 格式的数据,并返回给客户端。
c.JSON(200, response)
上述示例中,我们创建了一个名为 response
的 PageData
实例,设置了 Code
和 Msg
字段的值,同时在 Data
字段中存储了一个字符串键值对的映射。然后,使用 Gin 的 c.JSON
方法将该结构体实例转换为 JSON 格式的数据,并返回给客户端作为 HTTP 响应。
post_publish.go
它用于处理社区帖子的发布,并调用了 service
包中的函数来完成实际的业务逻辑。函数接受一些参数,根据这些参数执行帖子发布逻辑,并返回一个 PageData
结构体指针作为结果。
函数签名:
go
func PostPublish(userIdStr string, topicIdStr string, rootIdStr string, content *string) *PageData
参数说明:
userIdStr
(类型为string
):表示用户 ID,这里假设是以字符串形式传入。topicIdStr
(类型为string
):表示话题 ID,同样假设是以字符串形式传入。rootIdStr
(类型为string
):表示根帖子 ID,同样假设是以字符串形式传入。如果是回复帖子,则根据此 ID 表示回复的目标帖子。content
(类型为*string
):指向内容字符串的指针。假设帖子内容通过指针传递,这样可以方便地在函数内修改内容。
函数返回值:
- 返回值为
*PageData
:表示帖子发布的结果,包含了响应状态码、消息和可能的数据。
函数流程:
- 将
userIdStr
、topicIdStr
和rootIdStr
这些字符串类型的参数转换为int64
类型。这里使用strconv.ParseInt
函数来实现转换。如果转换出错,会返回一个带有错误信息的PageData
结构体指针,表示转换失败。 - 调用
service.PostPublish
函数,并传入转换后的参数以及content
指针。该函数用于实际处理帖子发布的业务逻辑,并返回发布结果和可能的错误。 - 如果
service.PostPublish
函数执行过程中出现错误,会返回一个带有错误信息的PageData
结构体指针,表示发布失败。 - 如果一切顺利,将
data
(由service.PostPublish
返回的数据)填充到PageData
结构体中,并返回一个表示发布成功的PageData
结构体指针。
注意事项:
- 这里的
PageData
结构体与之前定义的PageData
结构体是同一个结构体。它用于将处理结果封装成一个统一的格式,方便在控制器中返回给客户端。 - 在实际应用中,可能还需要对帖子内容进行校验和处理,以确保输入的数据符合要求,避免潜在的安全问题。
- 函数中的错误处理是简单地将错误信息返回给客户端,实际应用中可能需要更加详细的错误处理,比如记录日志、不同类型的错误返回不同的状态码等。
示例用法:
less
content := "这是一个测试帖子内容"
result := controller.PostPublish("123", "456", "789", &content)
在上述示例中,我们调用 PostPublish
函数,并传入字符串类型的参数(用户 ID、话题 ID、根帖子 ID)以及内容字符串的指针。函数将根据参数执行帖子发布逻辑,并返回一个 PageData
结构体指针作为发布结果。
query_page_info.go
它用于根据话题 ID 查询页面信息,并调用了 service
包中的函数来处理实际的业务逻辑。函数接受话题 ID 的字符串形式作为参数,将其转换为 int64
类型,并返回一个 PageData
结构体指针作为查询结果。
函数签名:
go
func QueryPageInfo(topicIdStr string) *PageData
参数说明:
topicIdStr
(类型为string
):表示话题 ID,假设是以字符串形式传入。
函数返回值:
- 返回值为
*PageData
:表示查询结果,包含了响应状态码、消息和可能的数据。
函数流程:
- 将
topicIdStr
这个字符串类型的参数转换为int64
类型。这里使用strconv.ParseInt
函数来实现转换。如果转换出错,会返回一个带有错误信息的PageData
结构体指针,表示转换失败。 - 调用
service.QueryPageInfo
函数,并传入转换后的话题 ID。该函数用于实际处理查询页面信息的业务逻辑,并返回查询结果和可能的错误。 - 如果
service.QueryPageInfo
函数执行过程中出现错误,会返回一个带有错误信息的PageData
结构体指针,表示查询失败。 - 如果一切顺利,将
pageInfo
(由service.QueryPageInfo
返回的数据)填充到PageData
结构体中,并返回一个表示查询成功的PageData
结构体指针。
注意事项:
- 这里的
PageData
结构体与之前定义的PageData
结构体是同一个结构体。它用于将处理结果封装成一个统一的格式,方便在控制器中返回给客户端。 - 在实际应用中,可能还需要进行更复杂的逻辑处理和数据查询,以返回更丰富的页面信息。
- 函数中的错误处理是简单地将错误信息返回给客户端,实际应用中可能需要更加详细的错误处理,比如记录日志、不同类型的错误返回不同的状态码等。
示例用法:
css
result := controller.QueryPageInfo("123")
在上述示例中,我们调用 QueryPageInfo
函数,并传入话题 ID 的字符串形式作为参数。函数将根据话题 ID 查询页面信息,并返回一个 PageData
结构体指针作为查询结果。
DBInit.go
用于初始化和管理数据库连接。在这里,它使用了 GORM (Go ORM) 库来连接 MySQL 数据库,并在初始化时创建一个全局的数据库连接变量 db
。
代码解释:
- 导入语句:
go
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
这里导入了 GORM 相关的包,包括 MySQL 驱动、GORM 主库以及 GORM 日志模块。
- 全局变量
db
:
csharp
var db *gorm.DB
定义了一个全局变量 db
,用于保存 GORM 数据库连接。
Init()
函数:
go
func Init() error {
// 数据库连接字符串(DSN),指定了数据库的地址、用户名、密码、数据库名称等信息。
dsn := "root:123456@tcp(127.0.0.1:3306)/communityTopics?charset=utf8mb4&parseTime=True&loc=Local"
// 使用 GORM 打开 MySQL 数据库连接,创建数据库连接对象 db。
var err error
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info), // 使用默认的日志记录器,设置日志模式为 Info。
})
return err // 返回连接过程中的错误(如果有)。
}
在 Init()
函数中,它首先设置数据库连接字符串 dsn
,其中包含数据库地址、用户名、密码、数据库名称等信息。然后,使用 GORM 的 gorm.Open
函数打开 MySQL 数据库连接,并传入相应的 MySQL 驱动和配置信息。这样,全局变量 db
将持有对数据库的连接。
返回值为 error
,用于表示数据库连接过程中是否出现错误。如果连接成功,返回 nil
,否则返回连接过程中的错误。
注意事项:
- 此处使用的是 GORM v2 版本的库。GORM 是一个优秀的 ORM 库,它简化了数据库操作和数据模型的管理。
- 数据库连接字符串
dsn
需要根据实际情况进行配置,包括数据库的用户名、密码、地址、端口号和数据库名称等信息。 - 在实际应用中,可能还需要设置连接池大小、超时时间等数据库连接相关的配置。此处的代码只提供了基本的数据库连接部分。
- 在初始化时调用
repository.Init()
来初始化数据库连接,保证数据库连接的唯一性和复用性。
示例用法:
go
err := repository.Init()
if err != nil {
// 处理数据库连接错误。
}
// 程序的其他部分可以使用全局变量 db 来执行数据库操作。
以上代码是一个基本的数据库连接初始化的示例。在实际应用中,还需要根据具体需求进行数据库模型定义、数据操作函数编写等工作。
post.go
定义了名为 Post
的结构体,表示数据库中的帖子表。同时,还包含了一个名为 PostDao
的结构体,用于执行与帖子数据相关的数据库操作。
代码解释:
Post
结构体:
go
type Post struct {
Id int64 `gorm:"column:id"`
UserId int64 `gorm:"column:user_id"`
TopicId int64 `gorm:"column:topic_id"`
RootId int `gorm:"column:root_id"`
Content string `gorm:"column:content"`
Likes int64 `gorm:"column:likes;default:0"`
CreateTime int64 `gorm:"autoUpdateTime:milli"`
}
Post
结构体定义了帖子的字段,并使用了 GORM 的 struct tag(结构标签)来指定每个字段与数据库表中的列名对应关系。这样,GORM 就能够根据结构体字段自动映射到数据库表的列上。
func (Post) TableName() string
方法:
go
func (Post) TableName() string {
return "post"
}
TableName()
方法用于指定 Post
结构体所对应的数据库表名称是 "post"。GORM 在进行数据库操作时将会根据该方法返回的表名进行查询、插入等操作。
PostDao
结构体:
go
type PostDao struct{}
PostDao
结构体是一个空结构体,没有任何字段,它用于组织与帖子数据相关的数据库操作函数。
- 全局变量
postDao
和postOnce
:
dart
var (
postDao *PostDao
postOnce sync.Once
)
postDao
是一个指向 PostDao
结构体的指针,用于在单例模式下保持一个 PostDao
实例。postOnce
是一个 sync.Once
类型的变量,用于确保只有在第一次调用时才会创建 PostDao
实例。
NewPostDaoInstance()
函数:
go
func NewPostDaoInstance() *PostDao {
postOnce.Do(
func() {
postDao = &PostDao{}
})
return postDao
}
NewPostDaoInstance()
函数用于获取 PostDao
的实例。它使用 sync.Once
来保证只创建一个实例,并通过返回指针 postDao
来提供对唯一实例的访问。
PostDao
结构体的方法:
QueryPostById(id int64) (*Post, error)
:根据帖子 ID 查询单个帖子信息,并返回帖子结构体指针和可能的错误。QueryPostByTopicId(topicId int64) ([]*Post, error)
:根据话题 ID 查询相关帖子信息,并返回帖子结构体切片和可能的错误。CreatePost(post *Post) error
:创建一个新的帖子,并将帖子结构体插入数据库,返回可能的错误。
这些方法使用 db
全局变量(在 Init()
函数中初始化)来执行数据库操作,通过 GORM 提供的 API 来完成数据的查询和插入操作。
注意事项:
PostDao
结构体中的方法是用来处理帖子数据与数据库之间的交互,这些方法执行了相应的数据库操作,并返回数据或错误信息。- 为了保证数据的安全性和一致性,可以在这些方法中加入事务处理等功能,确保操作的原子性。
- 此处的示例代码只展示了与帖子相关的数据库操作,实际应用中可能还需要定义其他数据表的结构体和相应的操作方法。
示例用法:
go
postDao := repository.NewPostDaoInstance()
// 查询帖子信息
post, err := postDao.QueryPostById(123)
if err != nil {
// 处理查询错误。
}
// 创建新帖子
newPost := &repository.Post{
UserId: 1,
TopicId: 456,
Content: "这是一个新帖子",
}
err = postDao.CreatePost(newPost)
if err != nil {
// 处理创建帖子错误。
}
topic.go
定义了名为 Topic
的结构体,表示数据库中的话题表。同时,还包含了一个名为 TopicDao
的结构体,用于执行与话题数据相关的数据库操作。
代码解释:
Topic
结构体:
go
type Topic struct {
Id int64 `gorm:"column:id"`
UserId int64 `gorm:"column:user_id"`
Title string `gorm:"column:title"`
Content string `gorm:"column:content"`
CreateTime int64 `gorm:"autoCreateTime;column:create_time"`
ViewCount int64 `gorm:"column:view_count;default:0"`
}
Topic
结构体定义了话题的字段,并使用了 GORM 的 struct tag(结构标签)来指定每个字段与数据库表中的列名对应关系。这样,GORM 就能够根据结构体字段自动映射到数据库表的列上。
func (Topic) TableName() string
方法:
go
func (Topic) TableName() string {
return "topic"
}
TableName()
方法用于指定 Topic
结构体所对应的数据库表名称是 "topic"。GORM 在进行数据库操作时将会根据该方法返回的表名进行查询、插入等操作。
TopicDao
结构体:
go
type TopicDao struct{}
TopicDao
结构体是一个空结构体,没有任何字段,它用于组织与话题数据相关的数据库操作函数。
- 全局变量
topicDao
和topicOnce
:
dart
var (
topicDao *TopicDao
topicOnce sync.Once
)
topicDao
是一个指向 TopicDao
结构体的指针,用于在单例模式下保持一个 TopicDao
实例。topicOnce
是一个 sync.Once
类型的变量,用于确保只有在第一次调用时才会创建 TopicDao
实例。
NewTopicDaoInstance()
函数:
go
func NewTopicDaoInstance() *TopicDao {
topicOnce.Do(
func() {
topicDao = &TopicDao{}
})
return topicDao
}
NewTopicDaoInstance()
函数用于获取 TopicDao
的实例。它使用 sync.Once
来保证只创建一个实例,并通过返回指针 topicDao
来提供对唯一实例的访问。
TopicDao
结构体的方法:
QueryTopicById(id int64) (*Topic, error)
:根据话题 ID 查询单个话题信息,并返回话题结构体指针和可能的错误。AddTopic(topic *Topic) error
:新增一个话题,并将话题结构体插入数据库,返回可能的错误。
这些方法使用 db
全局变量(在 Init()
函数中初始化)来执行数据库操作,通过 GORM 提供的 API 来完成数据的查询和插入操作。
注意事项:
TopicDao
结构体中的方法是用来处理话题数据与数据库之间的交互,这些方法执行了相应的数据库操作,并返回数据或错误信息。- 为了保证数据的安全性和一致性,可以在这些方法中加入事务处理等功能,确保操作的原子性。
- 此处的示例代码只展示了与话题相关的数据库操作,实际应用中可能还需要定义其他数据表的结构体和相应的操作方法。
示例用法:
go
topicDao := repository.NewTopicDaoInstance()
// 查询话题信息
topic, err := topicDao.QueryTopicById(123)
if err != nil {
// 处理查询错误。
}
// 创建新话题
newTopic := &repository.Topic{
UserId: 1,
Title: "新话题标题",
Content: "这是一个新话题内容",
}
err = topicDao.AddTopic(newTopic)
if err != nil {
// 处理创建话题错误。
}
user.go
定义了名为 User
的结构体,表示数据库中的用户表。同时,还包含了一个名为 UserDao
的结构体,用于执行与用户数据相关的数据库操作。
代码解释:
User
结构体:
go
type User struct {
Id int64 `gorm:"column:id"`
Name string `gorm:"column:name"`
Avatar string `gorm:"column:avatar"`
Level int `gorm:"column:level"`
CreateTime int64 `gorm:"column:create_time"`
}
User
结构体定义了用户的字段,并使用了 GORM 的 struct tag(结构标签)来指定每个字段与数据库表中的列名对应关系。这样,GORM 就能够根据结构体字段自动映射到数据库表的列上。
func (User) TableName() string
方法:
go
func (User) TableName() string {
return "user"
}
TableName()
方法用于指定 User
结构体所对应的数据库表名称是 "user"。GORM 在进行数据库操作时将会根据该方法返回的表名进行查询、插入等操作。
UserDao
结构体:
go
type UserDao struct{}
UserDao
结构体是一个空结构体,没有任何字段,它用于组织与用户数据相关的数据库操作函数。
- 全局变量
userDao
和userOnce
:
dart
var (
userDao *UserDao
userOnce sync.Once
)
userDao
是一个指向 UserDao
结构体的指针,用于在单例模式下保持一个 UserDao
实例。userOnce
是一个 sync.Once
类型的变量,用于确保只有在第一次调用时才会创建 UserDao
实例。
NewUserDaoInstance()
函数:
go
func NewUserDaoInstance() *UserDao {
userOnce.Do(
func() {
userDao = &UserDao{}
})
return userDao
}
NewUserDaoInstance()
函数用于获取 UserDao
的实例。它使用 sync.Once
来保证只创建一个实例,并通过返回指针 userDao
来提供对唯一实例的访问。
UserDao
结构体的方法:
QueryUserById(id int64) (*User, error)
:根据用户 ID 查询单个用户信息,并返回用户结构体指针和可能的错误。QueryUserByIds(ids []int64) (map[int64]*User, error)
:根据用户 ID 列表批量查询用户信息,并返回用户 ID 到用户结构体的映射以及可能的错误。
这些方法使用 db
全局变量(在 Init()
函数中初始化)来执行数据库操作,通过 GORM 提供的 API 来完成数据的查询。
注意事项:
UserDao
结构体中的方法是用来处理用户数据与数据库之间的交互,这些方法执行了相应的数据库操作,并返回数据或错误信息。- 为了保证数据的安全性和一致性,可以在这些方法中加入事务处理等功能,确保操作的原子性。
- 此处的示例代码只展示了与用户相关的数据库操作,实际应用中可能还需要定义其他数据表的结构体和相应的操作方法。
示例用法:
go
userDao := repository.NewUserDaoInstance()
// 查询单个用户信息
user, err := userDao.QueryUserById(123)
if err != nil {
// 处理查询错误。
}
// 批量查询用户信息
userIds := []int64{1, 2, 3}
userMap, err := userDao.QueryUserByIds(userIds)
if err != nil {
// 处理批量查询错误。
}
post_publish.go
定义了与帖子发布相关的服务函数。主要包括 PostPublish
函数用于发布帖子,以及 PublishPostFlow
结构体和相关方法用于执行帖子发布的流程。
代码解释:
PostPublish
函数:
go
func PostPublish(userId int64, topicId int64, rootId int64, content *string) (*PostInfo, error) {
return NewPublishPostFlow(userId, topicId, rootId, content).Do()
}
PostPublish
函数用于发布帖子。它接收用户 ID (userId
)、话题 ID (topicId
)、根帖子 ID (rootId
) 和帖子内容 (content
) 作为参数,并返回一个 *PostInfo
和可能的错误。PostInfo
包含了发布的帖子信息以及相关的用户信息。
NewPublishPostFlow
函数:
go
func NewPublishPostFlow(userId int64, topicId int64, rootId int64, content *string) *PublishPostFlow {
return &PublishPostFlow{
userId: userId,
topicId: topicId,
content: content,
rootId: rootId,
}
}
NewPublishPostFlow
函数用于创建一个 PublishPostFlow
的实例,用于执行帖子发布的流程。它接收用户 ID、话题 ID、根帖子 ID 和帖子内容作为参数,并返回一个 PublishPostFlow
的指针。
PublishPostFlow
结构体:
go
type PublishPostFlow struct {
userId int64
content *string
topicId int64
rootId int64
postId int64
post *repository.Post
}
PublishPostFlow
结构体用于组织帖子发布的流程,包含了用户 ID、帖子内容、话题 ID、根帖子 ID、发布后的帖子 ID (postId
) 以及帖子信息 (post
)。
func (f *PublishPostFlow) Do() (*PostInfo, error)
方法:Do()
方法用于执行帖子发布的流程。它包括参数检查、发布帖子和获取用户信息等步骤,并返回PostInfo
和可能的错误。func (f *PublishPostFlow) checkParam() error
方法:checkParam()
方法用于检查帖子发布时的参数是否合法。它检查用户 ID 是否大于 0,帖子内容是否长度不超过 500 个字符。func (f *PublishPostFlow) publish() error
方法:publish()
方法用于执行帖子发布的数据库操作。它创建了一个repository.Post
实例并将其插入数据库。
注意事项:
- 这段代码主要实现了一个简单的帖子发布流程,包括参数检查和数据库插入操作。
- 在实际应用中,可能还需要增加更复杂的逻辑,如权限验证、帖子的回复和评论等功能。
PostInfo
的定义未在代码中给出,可能是一个包含了帖子信息和用户信息的结构体。
示例用法:
go
userId := int64(123)
topicId := int64(456)
rootId := int64(0)
content := "这是一个测试帖子内容"
postInfo, err := service.PostPublish(userId, topicId, rootId, &content)
if err != nil {
// 处理发布帖子错误。
}
// 使用 postInfo 和相关信息进行后续处理。
query_page_info.go
定义了与帖子页面信息查询相关的服务函数。主要包括 QueryPageInfo
函数用于查询帖子页面信息,以及 QueryPageInfoFlow
结构体和相关方法用于执行查询帖子页面信息的流程。
代码解释:
PostInfo
结构体:
go
type PostInfo struct {
Post *repository.Post
User *repository.User
}
PostInfo
结构体用于组织帖子信息,包括帖子本身的信息和发布该帖子的用户信息。
TopicInfo
结构体:
go
type TopicInfo struct {
Topic *repository.Topic
User *repository.User
}
TopicInfo
结构体用于组织话题信息,包括话题本身的信息和创建该话题的用户信息。
PageInfo
结构体:
go
type PageInfo struct {
TopicInfo *TopicInfo
PostList []*PostInfo
}
PageInfo
结构体用于组织页面信息,包括话题信息(TopicInfo
)和相关的帖子列表(PostList
)。
QueryPageInfo
函数:
scss
func QueryPageInfo(topicId int64) (*PageInfo, error) {
return NewQueryPageInfoFlow(topicId).Do()
}
QueryPageInfo
函数用于查询帖子页面信息。它接收话题 ID 作为参数,并返回一个 *PageInfo
和可能的错误。PageInfo
包含了话题信息和相关的帖子列表。
NewQueryPageInfoFlow
函数:
go
func NewQueryPageInfoFlow(topicId int64) *QueryPageInfoFlow {
return &QueryPageInfoFlow{
topicId: topicId,
}
}
NewQueryPageInfoFlow
函数用于创建一个 QueryPageInfoFlow
的实例,用于执行查询帖子页面信息的流程。它接收话题 ID 作为参数,并返回一个 QueryPageInfoFlow
的指针。
QueryPageInfoFlow
结构体:
go
type QueryPageInfoFlow struct {
topicId int64
topic *repository.Topic
pageInfo *PageInfo
posts []*repository.Post
userMap map[int64]*repository.User
}
QueryPageInfoFlow
结构体用于组织查询帖子页面信息的流程,包含了话题 ID、话题信息、页面信息、帖子列表以及用户 ID 到用户信息的映射。
func (f *QueryPageInfoFlow) Do() (*PageInfo, error)
方法:Do()
方法用于执行查询帖子页面信息的流程。它包括参数检查、准备信息和组织页面信息等步骤,并返回PageInfo
和可能的错误。func (f *QueryPageInfoFlow) checkParam() error
方法:checkParam()
方法用于检查查询帖子页面信息时的参数是否合法。它检查话题 ID 是否大于 0。func (f *QueryPageInfoFlow) prepareInfo() error
方法:prepareInfo()
方法用于准备查询帖子页面信息所需的数据。它包括获取话题信息、帖子列表和用户信息等操作。func (f *QueryPageInfoFlow) packPageInfo() error
方法:packPageInfo()
方法用于组织页面信息。它将话题信息和相关的帖子信息组合成一个PageInfo
结构体。
注意事项:
- 这段代码实现了查询帖子页面信息的功能,包括获取话题信息、帖子列表和用户信息,并将它们组织成
PageInfo
结构体返回。 - 为了提高查询效率,使用了并发方式分别获取话题信息和帖子列表,并通过
sync.WaitGroup
等待两个操作完成后再进行后续处理。 - 此处的示例代码只展示了查询帖子页面信息的逻辑,实际应用中可能还需要处理更多的业务逻辑和错误处理。
示例用法:
go
topicId := int64(123)
pageInfo, err := service.QueryPageInfo(topicId)
if err != nil {
// 处理查询页面信息错误。
}
// 使用 pageInfo 和相关信息进行页面展示。