Go语言工程实践 | 青训营

77 阅读3分钟

课程例子讲解——查询

Post和Topic结构体

  • post

    • id (int64)
    • title (string)
    • content (string)
    • create_time (int64)
  • post

    • id (int64)
    • parent_id (int64)
    • content (string)
    • create_time (int64)

本地文件索引的初始化

根据map来初始化两个文件的索引

一个是话题topic (map[int64]),一个是回帖的list数组(map[int64][])

主要代码思路为

  • 通过os.Open打开对应文件,如果打开文件过程中出现错误,就会返回错误。
  • 创建一个Scanner对象,用于逐行读取打开的文件内容。
  • 创建一个临时的map索引,用于暂时存储从文件中读取的话题数据。
  • 通过for scanner.Scan(){...}这样子的循环,每次读取文件的一行内容,并将其解析为对应的结构体,将解析得到的结构体对象添加到刚刚创建的临时map中。
  • 文件读取完毕后,将临时存储的map索引,赋予给全局的map,完成索引的初始化

Router

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

controller层

主要负责处理用户的输入和输出,不包含复杂的业务逻辑。

所以在这一步我们需要知道需要什么数据,以及处理后输出什么数据。

输入的数据在我们设计传参方式的时候就已经决定下来了,一个id就可以,不过需要使用标准strconv库,将字符串id转化为int64id,为后续传参做准备,并检测错误

输出的数据也需要一个结构体来表示

  • PageDate

    • code(int64 -1为false 0为true)
    • msg(string 具体的错误信息)
    • data(inerface{} 一个匿名的空接口,表示任意类型)

这里的重点是data的值,不过具体data值为多少,就需要交给service层处理

service层

service层包含更复杂的业务逻辑,处理数据的验证、计算、操作等

这里根据流程描述可以将业务逻辑划分为三步,检验传参,获取所需信息,打包

实现上述三个的方法都将其绑定为结构体方法,这样在值传递过程中,只需要一个实体就够了,更加方便

  • QueryPageInfoFlow

    • topicId (int64)
    • pageInfo (*PageInfo)
    • topic (*repository.Topic)
    • posts([]*repository.Post)

检验传参

根据业务逻辑,我们可以得知,传入参数为id,根据此id查询相关话题和回帖列表,因此id号不可能小于0,所以这里只需要判断一步就可以

获取所需信息

这里使用了并行,用 go 关键字开了两个goroutine,因为post数据和topic数据保存在两个不同的文件当中,可以并行读取,同时加锁,使两者异步转同步

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

具体查询的方式也非常简单,topic直接根据id映射,post根据parent_id映射

将所得到的数据,赋值给结构体内部的topic和posts中

打包

为了使数据更加清楚,这里也定义了PageInfo的结构体

  • PageInfo

    • Topic(*repository.Topic)
    • PostList([]*repository.Post)

将QueryPageInfoFlow中PageInfo赋值,数据从QueryPageInfoFlow的topic和posts中来,整个功能便成功完成了

测试

主要对service层的业务进行测试,测试分两步,初始化和单元测试

初始化

加载数据所在文件夹

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

单元测试

主要测试下,返回是否为空,以及返回数量是否符合文件内的数量

assert.NotEqual(t, nil, pageInfo)
assert.Equal(t, 7, len(pageInfo.PostList))

结尾

这份课堂代码质量很高,从MVC三层架构,到业务逻辑的抽象归纳都做得非常好,尤其是结构体方法的运用和测试文件的书写,给我了很大的启发,对go有了更深一层次的了解,这里还有一份课后作业,添加append功能,将放在下次文章当中。