课后作业:CommunityTopics| 青训营

简介

这个项目是一个简单的社区话题系统。gitee地址:字节青训营CommunityTopics:(gitee.com)

我使用了 MVC(Model-View-Controller)架构模式,其中 controller 包负责控制请求处理,service 包负责处理业务逻辑,repository 包负责与数据库交互。

  • main 包主要负责启动服务和定义路由,具体的业务逻辑在其他包中实现。
  • 为了保证代码的可扩展性和复用性,项目采用了单例模式,例如 NewTopicDaoInstanceNewUserDaoInstanceNewPostDaoInstance 函数,确保了在全局范围内只有一个实例,避免了多次创建实例和资源浪费。
  • 使用了并发方式来提高数据查询的效率,例如在 prepareInfo() 方法中并发获取话题信息和帖子列表,通过 sync.WaitGroup 来等待两个操作完成后再进行后续处理。

后续会添加jwt验证,并添加进代码仓库,我蒟蒻,有很多不好的地方还请带佬指点

结构:

  1. main

    • 负责项目的入口,初始化 Gin 引擎和路由。
    • 定义了两个路由,分别处理帖子页面信息查询和帖子发布的请求。
    • "/community/page/get/:id" 路由处理查询帖子页面信息的请求,调用 controller.QueryPageInfo 处理函数。
    • "/community/post/publish" 路由处理帖子发布的请求,调用 controller.PostPublish 处理函数。
  2. controller

    • 负责处理 HTTP 请求和路由处理逻辑。
    • QueryPageInfo 函数:处理查询帖子页面信息的请求,调用 service.QueryPageInfo 函数。
    • PostPublish 函数:处理帖子发布的请求,调用 service.PostPublish 函数。
    • PageData 结构体:用于封装返回给客户端的 JSON 数据。
  3. service

    • 定义了与帖子发布和页面信息查询相关的服务函数,处理具体的业务逻辑。
    • PostPublish 函数:用于发布帖子,调用 NewPublishPostFlow 创建一个 PublishPostFlow 实例,并调用其 Do() 方法执行帖子发布的流程。
    • NewPublishPostFlow 函数:用于创建一个 PublishPostFlow 实例。
    • PublishPostFlow 结构体:用于组织帖子发布的流程,包含了参数校验、帖子发布的数据库操作,以及返回帖子信息和可能的错误。
    • QueryPageInfo 函数:用于查询帖子页面信息,调用 NewQueryPageInfoFlow 创建一个 QueryPageInfoFlow 实例,并调用其 Do() 方法执行查询帖子页面信息的流程。
    • NewQueryPageInfoFlow 函数:用于创建一个 QueryPageInfoFlow 实例。
    • QueryPageInfoFlow 结构体:用于组织查询帖子页面信息的流程,包含了参数校验、准备信息和组织页面信息等操作。
  4. repository

    • 负责与数据库交互,包含了对话题、帖子和用户数据表的结构体和相应的数据库操作方法。
    • 包含了 TopicPostUser 结构体,分别代表话题、帖子和用户的数据模型。
    • 包含了 TopicDaoPostDaoUserDao 结构体,用于执行对应数据表的数据库操作。
    • 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 请求。下面对代码进行解析:

  1. 导入语句:
go 复制代码
import (
	"communityTopics/controller"
	"communityTopics/repository"
	"fmt"
	"github.com/gin-gonic/gin"
)

在这部分代码中,导入了必要的包。"github.com/gin-gonic/gin" 是 Gin 框架,用于创建 Web 服务器;"communityTopics/controller" 和 "communityTopics/repository" ,分别包含了业务逻辑和数据访问操作。

  1. 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 应用程序的基本结构。根据 controllerrepository 包的实现,该应用程序可能处理社区话题信息的检索和发布新的社区帖子。GET 端点用于根据给定的话题 ID 获取页面信息,POST 端点用于发布帖子,并包含相关的信息,如用户 ID、话题 ID、根帖子 ID(如果是对先前帖子的回复)以及内容。

pageDate.go

代码定义了一个名为 PageData 的结构体,用于封装响应数据,通常用于在控制器中返回给客户端的数据。结构体包含三个字段:

  1. Code(类型为 int64):表示响应状态码,用于标识请求的处理结果,例如 200 表示成功,400 表示客户端错误,500 表示服务器错误等。
  2. Msg(类型为 string):表示响应消息,通常用于向客户端返回与处理结果相关的一些文字信息,比如 "Success" 表示成功,"Bad Request" 表示客户端请求错误等。
  3. 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)

上述示例中,我们创建了一个名为 responsePageData 实例,设置了 CodeMsg 字段的值,同时在 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:表示帖子发布的结果,包含了响应状态码、消息和可能的数据。

函数流程:

  1. userIdStrtopicIdStrrootIdStr 这些字符串类型的参数转换为 int64 类型。这里使用 strconv.ParseInt 函数来实现转换。如果转换出错,会返回一个带有错误信息的 PageData 结构体指针,表示转换失败。
  2. 调用 service.PostPublish 函数,并传入转换后的参数以及 content 指针。该函数用于实际处理帖子发布的业务逻辑,并返回发布结果和可能的错误。
  3. 如果 service.PostPublish 函数执行过程中出现错误,会返回一个带有错误信息的 PageData 结构体指针,表示发布失败。
  4. 如果一切顺利,将 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:表示查询结果,包含了响应状态码、消息和可能的数据。

函数流程:

  1. topicIdStr 这个字符串类型的参数转换为 int64 类型。这里使用 strconv.ParseInt 函数来实现转换。如果转换出错,会返回一个带有错误信息的 PageData 结构体指针,表示转换失败。
  2. 调用 service.QueryPageInfo 函数,并传入转换后的话题 ID。该函数用于实际处理查询页面信息的业务逻辑,并返回查询结果和可能的错误。
  3. 如果 service.QueryPageInfo 函数执行过程中出现错误,会返回一个带有错误信息的 PageData 结构体指针,表示查询失败。
  4. 如果一切顺利,将 pageInfo(由 service.QueryPageInfo 返回的数据)填充到 PageData 结构体中,并返回一个表示查询成功的 PageData 结构体指针。

注意事项:

  • 这里的 PageData 结构体与之前定义的 PageData 结构体是同一个结构体。它用于将处理结果封装成一个统一的格式,方便在控制器中返回给客户端。
  • 在实际应用中,可能还需要进行更复杂的逻辑处理和数据查询,以返回更丰富的页面信息。
  • 函数中的错误处理是简单地将错误信息返回给客户端,实际应用中可能需要更加详细的错误处理,比如记录日志、不同类型的错误返回不同的状态码等。

示例用法:

css 复制代码
result := controller.QueryPageInfo("123")

在上述示例中,我们调用 QueryPageInfo 函数,并传入话题 ID 的字符串形式作为参数。函数将根据话题 ID 查询页面信息,并返回一个 PageData 结构体指针作为查询结果。

DBInit.go

用于初始化和管理数据库连接。在这里,它使用了 GORM (Go ORM) 库来连接 MySQL 数据库,并在初始化时创建一个全局的数据库连接变量 db

代码解释:

  1. 导入语句:
go 复制代码
import (
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

这里导入了 GORM 相关的包,包括 MySQL 驱动、GORM 主库以及 GORM 日志模块。

  1. 全局变量 db
csharp 复制代码
var db *gorm.DB

定义了一个全局变量 db,用于保存 GORM 数据库连接。

  1. 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 的结构体,用于执行与帖子数据相关的数据库操作。

代码解释:

  1. 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 就能够根据结构体字段自动映射到数据库表的列上。

  1. func (Post) TableName() string 方法:
go 复制代码
func (Post) TableName() string {
	return "post"
}

TableName() 方法用于指定 Post 结构体所对应的数据库表名称是 "post"。GORM 在进行数据库操作时将会根据该方法返回的表名进行查询、插入等操作。

  1. PostDao 结构体:
go 复制代码
type PostDao struct{}

PostDao 结构体是一个空结构体,没有任何字段,它用于组织与帖子数据相关的数据库操作函数。

  1. 全局变量 postDaopostOnce
dart 复制代码
var (
	postDao  *PostDao
	postOnce sync.Once
)

postDao 是一个指向 PostDao 结构体的指针,用于在单例模式下保持一个 PostDao 实例。postOnce 是一个 sync.Once 类型的变量,用于确保只有在第一次调用时才会创建 PostDao 实例。

  1. NewPostDaoInstance() 函数:
go 复制代码
func NewPostDaoInstance() *PostDao {
	postOnce.Do(
		func() {
			postDao = &PostDao{}
		})
	return postDao
}

NewPostDaoInstance() 函数用于获取 PostDao 的实例。它使用 sync.Once 来保证只创建一个实例,并通过返回指针 postDao 来提供对唯一实例的访问。

  1. 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 的结构体,用于执行与话题数据相关的数据库操作。

代码解释:

  1. 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 就能够根据结构体字段自动映射到数据库表的列上。

  1. func (Topic) TableName() string 方法:
go 复制代码
func (Topic) TableName() string {
	return "topic"
}

TableName() 方法用于指定 Topic 结构体所对应的数据库表名称是 "topic"。GORM 在进行数据库操作时将会根据该方法返回的表名进行查询、插入等操作。

  1. TopicDao 结构体:
go 复制代码
type TopicDao struct{}

TopicDao 结构体是一个空结构体,没有任何字段,它用于组织与话题数据相关的数据库操作函数。

  1. 全局变量 topicDaotopicOnce
dart 复制代码
var (
	topicDao  *TopicDao
	topicOnce sync.Once
)

topicDao 是一个指向 TopicDao 结构体的指针,用于在单例模式下保持一个 TopicDao 实例。topicOnce 是一个 sync.Once 类型的变量,用于确保只有在第一次调用时才会创建 TopicDao 实例。

  1. NewTopicDaoInstance() 函数:
go 复制代码
func NewTopicDaoInstance() *TopicDao {
	topicOnce.Do(
		func() {
			topicDao = &TopicDao{}
		})
	return topicDao
}

NewTopicDaoInstance() 函数用于获取 TopicDao 的实例。它使用 sync.Once 来保证只创建一个实例,并通过返回指针 topicDao 来提供对唯一实例的访问。

  1. 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 的结构体,用于执行与用户数据相关的数据库操作。

代码解释:

  1. 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 就能够根据结构体字段自动映射到数据库表的列上。

  1. func (User) TableName() string 方法:
go 复制代码
func (User) TableName() string {
	return "user"
}

TableName() 方法用于指定 User 结构体所对应的数据库表名称是 "user"。GORM 在进行数据库操作时将会根据该方法返回的表名进行查询、插入等操作。

  1. UserDao 结构体:
go 复制代码
type UserDao struct{}

UserDao 结构体是一个空结构体,没有任何字段,它用于组织与用户数据相关的数据库操作函数。

  1. 全局变量 userDaouserOnce
dart 复制代码
var (
	userDao  *UserDao
	userOnce sync.Once
)

userDao 是一个指向 UserDao 结构体的指针,用于在单例模式下保持一个 UserDao 实例。userOnce 是一个 sync.Once 类型的变量,用于确保只有在第一次调用时才会创建 UserDao 实例。

  1. NewUserDaoInstance() 函数:
go 复制代码
func NewUserDaoInstance() *UserDao {
	userOnce.Do(
		func() {
			userDao = &UserDao{}
		})
	return userDao
}

NewUserDaoInstance() 函数用于获取 UserDao 的实例。它使用 sync.Once 来保证只创建一个实例,并通过返回指针 userDao 来提供对唯一实例的访问。

  1. 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 结构体和相关方法用于执行帖子发布的流程。

代码解释:

  1. 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 包含了发布的帖子信息以及相关的用户信息。

  1. 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 的指针。

  1. 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)。

  1. func (f *PublishPostFlow) Do() (*PostInfo, error) 方法: Do() 方法用于执行帖子发布的流程。它包括参数检查、发布帖子和获取用户信息等步骤,并返回 PostInfo 和可能的错误。
  2. func (f *PublishPostFlow) checkParam() error 方法: checkParam() 方法用于检查帖子发布时的参数是否合法。它检查用户 ID 是否大于 0,帖子内容是否长度不超过 500 个字符。
  3. 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 结构体和相关方法用于执行查询帖子页面信息的流程。

代码解释:

  1. PostInfo 结构体:
go 复制代码
type PostInfo struct {
	Post *repository.Post
	User *repository.User
}

PostInfo 结构体用于组织帖子信息,包括帖子本身的信息和发布该帖子的用户信息。

  1. TopicInfo 结构体:
go 复制代码
type TopicInfo struct {
	Topic *repository.Topic
	User  *repository.User
}

TopicInfo 结构体用于组织话题信息,包括话题本身的信息和创建该话题的用户信息。

  1. PageInfo 结构体:
go 复制代码
type PageInfo struct {
	TopicInfo *TopicInfo
	PostList  []*PostInfo
}

PageInfo 结构体用于组织页面信息,包括话题信息(TopicInfo)和相关的帖子列表(PostList)。

  1. QueryPageInfo 函数:
scss 复制代码
func QueryPageInfo(topicId int64) (*PageInfo, error) {
	return NewQueryPageInfoFlow(topicId).Do()
}

QueryPageInfo 函数用于查询帖子页面信息。它接收话题 ID 作为参数,并返回一个 *PageInfo 和可能的错误。PageInfo 包含了话题信息和相关的帖子列表。

  1. NewQueryPageInfoFlow 函数:
go 复制代码
func NewQueryPageInfoFlow(topicId int64) *QueryPageInfoFlow {
	return &QueryPageInfoFlow{
		topicId:  topicId,
	}
}

NewQueryPageInfoFlow 函数用于创建一个 QueryPageInfoFlow 的实例,用于执行查询帖子页面信息的流程。它接收话题 ID 作为参数,并返回一个 QueryPageInfoFlow 的指针。

  1. QueryPageInfoFlow 结构体:
go 复制代码
type QueryPageInfoFlow struct {
	topicId  int64
	topic    *repository.Topic
	pageInfo *PageInfo
	posts    []*repository.Post
	userMap  map[int64]*repository.User
}

QueryPageInfoFlow 结构体用于组织查询帖子页面信息的流程,包含了话题 ID、话题信息、页面信息、帖子列表以及用户 ID 到用户信息的映射。

  1. func (f *QueryPageInfoFlow) Do() (*PageInfo, error) 方法: Do() 方法用于执行查询帖子页面信息的流程。它包括参数检查、准备信息和组织页面信息等步骤,并返回 PageInfo 和可能的错误。
  2. func (f *QueryPageInfoFlow) checkParam() error 方法: checkParam() 方法用于检查查询帖子页面信息时的参数是否合法。它检查话题 ID 是否大于 0。
  3. func (f *QueryPageInfoFlow) prepareInfo() error 方法: prepareInfo() 方法用于准备查询帖子页面信息所需的数据。它包括获取话题信息、帖子列表和用户信息等操作。
  4. func (f *QueryPageInfoFlow) packPageInfo() error 方法: packPageInfo() 方法用于组织页面信息。它将话题信息和相关的帖子信息组合成一个 PageInfo 结构体。

注意事项:

  • 这段代码实现了查询帖子页面信息的功能,包括获取话题信息、帖子列表和用户信息,并将它们组织成 PageInfo 结构体返回。
  • 为了提高查询效率,使用了并发方式分别获取话题信息和帖子列表,并通过 sync.WaitGroup 等待两个操作完成后再进行后续处理。
  • 此处的示例代码只展示了查询帖子页面信息的逻辑,实际应用中可能还需要处理更多的业务逻辑和错误处理。

示例用法:

go 复制代码
topicId := int64(123)
pageInfo, err := service.QueryPageInfo(topicId)
if err != nil {
	// 处理查询页面信息错误。
}
// 使用 pageInfo 和相关信息进行页面展示。
相关推荐
千慌百风定乾坤4 小时前
Go 语言入门指南:基础语法和常用特性解析(下) | 豆包MarsCode AI刷题
青训营笔记
FOFO4 小时前
青训营笔记 | HTML语义化的案例分析: 粗略地手绘分析juejin.cn首页 | 豆包MarsCode AI 刷题
青训营笔记
滑滑滑2 天前
后端实践-优化一个已有的 Go 程序提高其性能 | 豆包MarsCode AI刷题
青训营笔记
柠檬柠檬2 天前
Go 语言入门指南:基础语法和常用特性解析 | 豆包MarsCode AI刷题
青训营笔记
用户967136399652 天前
计算最小步长丨豆包MarsCodeAI刷题
青训营笔记
用户52975799354723 天前
字节跳动青训营刷题笔记2| 豆包MarsCode AI刷题
青训营笔记
clearcold3 天前
浅谈对LangChain中Model I/O的见解 | 豆包MarsCode AI刷题
青训营笔记
夭要7夜宵4 天前
【字节青训营】 Go 进阶语言:并发概述、Goroutine、Channel、协程池 | 豆包MarsCode AI刷题
青训营笔记
用户336901104444 天前
数字分组求和题解 | 豆包MarsCode AI刷题
青训营笔记
dnxb1234 天前
GO语言工程实践课后作业:实现思路、代码以及路径记录 | 豆包MarsCode AI刷题
青训营笔记