这是我参与「第三届青训营 -后端场」笔记创作活动的的第3篇笔记,主要内容是对第二节课的课后实践作业做一次记录。
课后实践
帖子 需要知道的信息有 主题(parentId),回复内容(content),创建时间(create_time),根据已有map自动建立索引并且需要注意并发安全
repository层
思路:每增加一个帖子,需要在post文件中增加一条记录,同时要考虑到并发安全,和帖子id的唯一性。那么首先考虑在数据层增加一个原子读操作GetPostId(),
该方法打开文件post,并读取其中的所有帖子记录,并返回下一条帖子的ID(可以理解为统计已发帖子个数)。
同时需要新增一个 AddPostByParentId(post Post)函数,函数功能是对post文件进行写入,并更新map,具体代码如下
func (*PostDao) AddPostByParentId(post Post) error {
//写入文件
lock.Lock()
defer lock.Unlock()
file, err := os.OpenFile("E:\ByteDance\goProjectExample\data\post", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0777)
if err != nil {
return err
}
defer file.Close()
data, err := json.Marshal(post)
data = append(data, '\n')
if err != nil {
return err
}
file.Write(data)
//更新map postIndexMap map[int64][]*Post
postIndexMap[post.ParentId] = append(postIndexMap[post.ParentId], &post)
return nil
}
AddPostByParentId(post Post)函数的知识点涉及到文件读写,JSON格式转换和锁的使用,在写文件之前上锁,禁止一切的其他的读写,确保帖子的ID的唯一性,并更新map。
service层
在service层中新增了一个AddPost的方法,其具体形式为func AddPost(topicId int64, content string, creatTime int64) error,函数参数接收通过controller层传来的
主题ID,发帖内容和创建时间,并将其封装为repository层中的Post结构体,并调用repository层中的GetPostId()和AddPostByParentId(post Post),本来还
应该在这个函数中写一些更复杂的逻辑判断,比如传入的topicID是否存在该主题的讨论区,(最开始的parentId只有1和2,如果传入为3则不合法),但为了简单只先确保了
整个数据是通的。
func AddPost(topicId int64, content string, creatTime int64) error {
Dao := repository.PostDao{}
id, err := Dao.GetPostId()
if err != nil {
return err
}
//这里应该还有一些逻辑检查 判断回帖的信息是否合法
Dao.AddPostByParentId(repository.Post{
Id: id,
ParentId: topicId,
Content: content,
CreateTime: creatTime,
})
return nil
}
controller层
控制器层主要是对前端传入的表单数据做一下格式上的转换,并将转换后的数据传给service层的AddPost()方法
func AddNewPost(topicIdStr, content, create_time string) int {
topicId, err := strconv.ParseInt(topicIdStr, 10, 64)
if err != nil {
return -1
}
creatTime, err := strconv.ParseInt(create_time, 10, 64)
if err != nil {
return -1
}
err = service.AddPost(topicId, content, creatTime)
if err != nil {
return -1
}
return 200
}
server.go
在主服务中主要学习的知识点是有关gin框架的使用,利用POST方法从表单中获取传入的数据,涉及到的知识点有RESTful风格,Gin框架的使用
func main() {
if err := Init("./data/"); err != nil {
os.Exit(-1)
}
r := gin.Default()
r.GET("/community/page/get/:id", func(c *gin.Context) {
topicId := c.Param("id")
data := cotroller.QueryPageInfo(topicId)
c.JSON(200, data)
})
//发布回帖功能
r.POST("/community/page/post/:id", func(c *gin.Context) {
topicId := c.Param("id") //主题id
content, ok := c.GetPostForm("content")
if !ok {
c.JSON(-1, "获取帖子内容失败")
}
create_time, ok := c.GetPostForm("creatTime")
if !ok {
c.JSON(-1, "获取帖子发布时间失败")
}
//c.JSON(200, gin.H{"帖子主题": topicId, "内容": content, "发布时间": create_time})
status := cotroller.AddNewPost(topicId, content, create_time)
c.JSON(status, gin.H{"发布状态": status})
})
err := r.Run()
if err != nil {
return
}
}
测试过程
在写每一层函数的时候,都最好写一个关于自己写的函数的test方法,这样能第一时间发现错误,而不是在整个三层的代码写完以后再来测试。每一步的测试,可以自己设计数据,运行后观察数据是否被写入 post文件中。 最后运行服务器,使用postman等工具进行测试。运行结果如下:
首次查询主题1
发帖
查看本地文件是否新增记录
再次使用get方法查询
通过测试可以得出后端的数据通路已经是通畅的,之后我重新选择不同主题插入数据,可以发现本地文件中帖子id的信息是唯一的
高并发的测试后续学习以后再继续完善。
小结
这一次的实践内容让我自己记忆深刻的几个知识点是:
1.网络编程restful风格与gin框架的结合
2.Go语言文件读写操作
3.Go语言锁的运用
但自己写的代码我觉得还是不够规范,之后要阅读更多的资料,了解更多关于并发上的一些规则和方法。