【青训营】第二次作业 | 青训营笔记

98 阅读3分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第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对象。
  • 基于以上内容,先增加一个结构体用于返回相关信息:
    type ResAddPost struct {
            Code    int64  `json:"code"`
            Msg     string `json:"msg"`
            TopicID string `json:"topicid"`
            Content string `json:"content"`
    }
    
    此处TopicID和Content是我在调试时为了确认已经获得post的body中相关信息而增加的内容,其实是可以不用的。
  • 那么整个业务代码其实就是相当于将string类型的ID解析成int类型,随后进行后续的调用,并在错误或完成的时候返回ResAddPost结构体。
    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,
            }
    }
    
    如果是想要更好的清理逻辑,可以先把err判断删掉来看一下大概的业务,就会变得很简单:
    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方法:
    func AddPost(parentId int, content string) error {
            temp_post := repository.NewPostDaoInstance()
            err := temp_post.AddPost(&repository.Post{
                    ParentId: int64(parentId),
                    Content:  content,
            })
            return err
    }
    
    此时parentId和content即post的body内容。

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这些,也是一头雾水,只能慢慢梳理。当然现在来看还有蛮多问题,一个就是还是基于简单的文件内容(但是数据库我是真的还没学哈哈哈)。 趁着今天还在复习阶段,晚些时候会再试着优化实现一下,到时候看看能不能更新一下。