课程搜寻|青训营

72 阅读2分钟

探索课程示例——搜寻

Post与Topic数据结构

  • post
    • id (int64)
    • title (string)
    • body (string)
    • timestamp (int64)
  • post
    • id (int64)
    • parent_id (int64)
    • body (string)
    • timestamp (int64)

文件索引初始化说明

利用map对两个文件的索引进行初始化。

包括话题index (map[int64]) 和回复的index列表(map[int64][])。

初始化流程如下:

  • 使用os.Open方法打开指定文件。如有错误,将会返回。
  • 实例化一个Scanner,用以逐行读取文件。
  • 设置一个临时map作为索引,暂时存储文件的数据。
  • 使用for scanner.Scan(){...}循环,读取文件中每一行,然后转换为相应的数据结构,并存入临时map中。
  • 当读取完毕,将临时map赋值给全局的map,完成索引初始化。

路由设置

  • URL: /community/page/view/
  • 请求方式:Get
  • 参数:路径参数(id)
r.GET("/community/page/view/:id", func(c *gin.Context) {
		threadId := c.Param("id")
	})

控制器部分

负责用户输入输出的管理,避免复杂业务逻辑的介入。

所需数据是已确定的路径参数。使用标准库strconv将字符串id转为int64类型,并处理可能出现的错误。

输出数据需要一个结构表示:

WebData status(int64 -1失败 0成功) message(string 错误详情) content(interface{} 通用空接口,可以是任何类型) 关键在于content的内容,实际内容将由service层提供。

服务层

此层含有复杂业务逻辑,包括数据校验、处理、操作等。

根据描述,可以分为三步:参数验证、信息获取、数据封装。

这些方法都绑定到结构体上,以方便传值。

PageInfoFlow threadId (int64) pageDetail (*PageDetail) topicDetail (*repository.Topic) replies([]*repository.Post) 参数验证 业务逻辑告诉我们,查询需要id参数,所以id不能小于0。

信息获取 使用并发,通过go关键词创建两个goroutines,异步读取topic和post数据,并加锁以保证同步。


var wg sync.WaitGroup
wg.Add(2)
go func() {
    defer wg.Done()
    //使用id获取topic
}()
go func() {
    defer wg.Done()
    //使用id获取post列表
}()
wg.Wait()

数据获取后,赋值给结构体的topicDetail和replies。

数据封装

为清晰表示数据,定义了PageDetail结构体:

PageDetail MainTopic(*repository.Topic) ReplyList([]*repository.Post) 从PageInfoFlow中获取数据,完成功能。

测试部分 重点测试服务层逻辑,包括初始化和单元测试。

初始化

加载数据目录:

func TestMain(m *testing.M) {
	repository.Init("../resources/")
	os.Exit(m.Run())
}

单元测试 核心测试内容为结果是否为空,以及结果数量是否与文件匹配。


assert.NotEqual(t, nil, pageDetail)
assert.Equal(t, 7, len(pageDetail.ReplyList))

总结

此段课程代码展示了出色的质量,从MVC架构到逻辑抽象,表现优异。尤其是结构体方法和测试代码的编写,对我有很大启示。对Go语言的理解也更上一层楼。课后有附加作业,即增加append功能,这将在后续文章中讨论。