探索课程示例——搜寻
Post与Topic数据结构
- post
- id (
int64) - title (
string) - body (
string) - timestamp (
int64)
- id (
- post
- id (
int64) - parent_id (
int64) - body (
string) - timestamp (
int64)
- id (
文件索引初始化说明
利用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功能,这将在后续文章中讨论。