这是我参与「第三届青训营 -后端场」笔记创作活动的的第4篇笔记
注:虽然距离第二次作业布置已经过了很久了,但也是这两天才有时间尝试去完成,并且实现了一个简单版本,也借此机会帮助自己理清顺序
任务
- 支持发布帖子
- 本地 ID 生成需要保证不重复、唯一性
- Append 文件,更新索引,注意 Map 的并发安全问题
server.go
- 在server.go文件中,主要任务便是构建路由、启动服务。
- 结合本次作业任务,选择的
r.POST("/Post", posting) - 先看看get和post的区别:
- 都包含请求头、请求行,但是post多了请求body。
- get多用来查询,请求参数放在url中,不会对服务器上的内容产生作用。post用来提交,如将账号密码放到body中。
- get是直接添加到url后面的,直接就可以在url中看到内容。post是放在报文内部的,用户无法直接看到。
- get提交的数据长度是有限的,因为url有长度限制。post的长度没有限制。
- 所以server.go中主要就是针对post请求设置路由路径,并获取参数,随后调用
AddPost()方法,其具体添加程序如下:
r.POST("/community/page/add", func(c *gin.Context) {
topicId, _ := c.GetPostForm("topicid")
content, _ := c.GetPostForm("content")
data := cotroller.AddPost(topicId, content)
c.JSON(200, data)
})
cotroller/query_page_info.go
- 从server.go中可以看到,主要调用的为
data := cotroller.AddPost(topicId, content),此方法在cotroller/query_page_info.go文件中。 - 其主要是视图层view是的实现,用于处理和外部的交互逻辑,简单来说就是构建view对象。
- 基于以上内容,先增加一个结构体用于返回相关信息:
此处TopicID和Content是我在调试时为了确认已经获得post的body中相关信息而增加的内容,其实是可以不用的。type ResAddPost struct { Code int64 `json:"code"` Msg string `json:"msg"` TopicID string `json:"topicid"` Content string `json:"content"` } - 那么整个业务代码其实就是相当于将string类型的ID解析成int类型,随后进行后续的调用,并在错误或完成的时候返回ResAddPost结构体。
如果是想要更好的清理逻辑,可以先把err判断删掉来看一下大概的业务,就会变得很简单:func AddPost(str_topicID, content string) *ResAddPost { topicId, err := strconv.Atoi(str_topicID) if err != nil { return &ResAddPost{ Code: -1, Msg: err.Error(), TopicID: str_topicID, Content: content, } } err = service.AddPost(topicId, content) if err != nil { return &ResAddPost{ Code: -1, Msg: err.Error(), TopicID: str_topicID, Content: content, } } return &ResAddPost{ Code: 0, Msg: "success", TopicID: str_topicID, Content: content, } }那么可以看出,其最重要的就是func AddPost(str_topicID, content string) *ResAddPost { topicId, err := strconv.Atoi(str_topicID) err = service.AddPost(topicId, content) return &ResAddPost{ Code: 0, Msg: "success", TopicID: str_topicID, Content: content, } }err = service.AddPost(topicId, content)。
service/query_page_info.go
- 此处主要是对实体的操作,先看一下实体的内容:
// 实体 type PageInfo struct { Topic *repository.Topic PostList []*repository.Post } - 在原始程序中,主要流程为:参数校验——数据准备——组装实体
- 所以在此处增加一个AddPost方法:
此时parentId和content即post的body内容。func AddPost(parentId int, content string) error { temp_post := repository.NewPostDaoInstance() err := temp_post.AddPost(&repository.Post{ ParentId: int64(parentId), Content: content, }) return err }
repository/post.go
下面就是主要的业务逻辑部分,先把代码粘贴出来~
func (*PostDao) AddPost(post *Post) error {
// 加锁
Lock.Lock()
defer Lock.Unlock()
// NowIndex保证post的id不重复,用递增的方法来实现
NowIndex++
newPost := &Post{
NowIndex, post.ParentId, post.Content, time.Now().Unix(),
}
// map索引append一下,即写入post文件中
postIndexMap[post.ParentId] = append(postIndexMap[post.ParentId], newPost)
open, err := os.OpenFile(FilePath+"post", os.O_APPEND, 0666)
if err != nil {
return err
}
defer open.Close()
writer := bufio.NewWriter(open)
bytes, _ := json.Marshal(newPost)
// 写完一条换个行
_, err = writer.WriteString((string)(bytes) + "\n")
if err != nil {
return err
}
writer.Flush()
return nil
}
- 上述中的FilePath即初始化时的filePath;
- NowIndex则是在初始化的时候获得最大的id,做为初始值,随后每次都+1:
if post.Id >= NowIndex { NowIndex = post.Id }
总结
这次实现也是参考了一些同学的实现,最开始的时候由于没什么经验也没了解过gin这些,也是一头雾水,只能慢慢梳理。当然现在来看还有蛮多问题,一个就是还是基于简单的文件内容(但是数据库我是真的还没学哈哈哈)。 趁着今天还在复习阶段,晚些时候会再试着优化实现一下,到时候看看能不能更新一下。